007 add telemetry

This commit is contained in:
Раис Юсупалиев
2026-03-08 11:43:15 +03:00
parent 150675cc75
commit 8d47917ba2
15 changed files with 952 additions and 62 deletions
+93 -53
View File
@@ -13,6 +13,8 @@ from app.domain.price import (
filter_and_sort_prices,
normalize_delivery_request,
)
from app.observability.metrics import get_metrics
from app.observability.tracing import get_tracer
from app.schemas.request import DeliveryRequest
from app.schemas.response import DeliveryPrice
@@ -44,33 +46,41 @@ class AggregatorService:
self._filter_and_sort_prices = filter_and_sort_prices_fn
async def get_all_prices(self, request: DeliveryRequest) -> list[DeliveryPrice]:
normalized_request = normalize_delivery_request(
request, weight_round_scale=self._weight_round_scale
)
provider_request = self._to_provider_request(normalized_request)
tracer = get_tracer(__name__)
with tracer.start_as_current_span("AggregatorService.get_all_prices") as span:
span.set_attribute("from_city", request.from_city)
span.set_attribute("to_city", request.to_city)
span.set_attribute("weight_kg", float(request.weight_kg))
provider_results = await asyncio.gather(
*(
self._get_provider_price(
provider=provider,
request=provider_request,
cache_key=self._build_cache_key(
provider_name=provider.name,
request=normalized_request,
),
)
for provider in self._providers
),
return_exceptions=True,
)
normalized_request = normalize_delivery_request(
request, weight_round_scale=self._weight_round_scale
)
provider_request = self._to_provider_request(normalized_request)
successful_results = [
price
for price in provider_results
if isinstance(price, DeliveryPrice)
]
filtered_and_sorted = self._filter_and_sort_prices(successful_results)
return [self._coerce_delivery_price(price) for price in filtered_and_sorted]
provider_results = await asyncio.gather(
*(
self._get_provider_price(
provider=provider,
request=provider_request,
cache_key=self._build_cache_key(
provider_name=provider.name,
request=normalized_request,
),
)
for provider in self._providers
),
return_exceptions=True,
)
successful_results = [
price
for price in provider_results
if isinstance(price, DeliveryPrice)
]
filtered_and_sorted = self._filter_and_sort_prices(successful_results)
result = [self._coerce_delivery_price(price) for price in filtered_and_sorted]
span.set_attribute("tariffs_found", len(result))
return result
async def _get_provider_price(
self,
@@ -79,31 +89,59 @@ class AggregatorService:
request: DeliveryRequest,
cache_key: str,
) -> DeliveryPrice:
cached_price = await self._get_cached_price(cache_key)
if cached_price is not None:
return cached_price
tracer = get_tracer(__name__)
with tracer.start_as_current_span("DeliveryProvider.get_price") as span:
span.set_attribute("provider", provider.name)
span.set_attribute("from_city", request.from_city)
span.set_attribute("to_city", request.to_city)
span.set_attribute("weight_kg", float(request.weight_kg))
fresh_price = await provider.get_price(request)
await self._set_cached_price(
cache_key,
fresh_price,
ttl=getattr(provider, "cache_ttl_seconds", None),
)
return fresh_price
cached_price = await self._get_cached_price(cache_key)
span.set_attribute("cache_hit", cached_price is not None)
if cached_price is not None:
return cached_price
try:
fresh_price = await provider.get_price(request)
except Exception:
get_metrics().record_provider_availability(
provider=provider.name,
is_available=False,
)
raise
get_metrics().record_provider_availability(
provider=provider.name,
is_available=True,
)
await self._set_cached_price(
cache_key,
fresh_price,
ttl=getattr(provider, "cache_ttl_seconds", None),
)
return fresh_price
async def _get_cached_price(self, cache_key: str) -> DeliveryPrice | None:
if self._cache is None:
return None
try:
payload = await self._cache.get(cache_key)
except Exception:
return None
if payload is None:
return None
try:
return self._coerce_delivery_price(payload)
except Exception:
return None
tracer = get_tracer(__name__)
with tracer.start_as_current_span("PriceCache.get") as span:
if self._cache is None:
span.set_attribute("cache_hit", False)
return None
try:
payload = await self._cache.get(cache_key)
except Exception:
span.set_attribute("cache_hit", False)
return None
if payload is None:
span.set_attribute("cache_hit", False)
return None
try:
price = self._coerce_delivery_price(payload)
except Exception:
span.set_attribute("cache_hit", False)
return None
span.set_attribute("cache_hit", True)
return price
async def _set_cached_price(
self,
@@ -112,12 +150,14 @@ class AggregatorService:
*,
ttl: int | None,
) -> None:
if self._cache is None:
return
try:
await self._cache.set(cache_key, payload, ttl=ttl)
except Exception:
return
tracer = get_tracer(__name__)
with tracer.start_as_current_span("PriceCache.set"):
if self._cache is None:
return
try:
await self._cache.set(cache_key, payload, ttl=ttl)
except Exception:
return
@staticmethod
def _to_provider_request(request: NormalizedDeliveryRequest) -> DeliveryRequest: