Fix POI distance calculation reliability for remote/Celery execution

- Fix silent log loss: replace hardcoded "uvicorn.error" logger with __name__
  in osrm_client, otp_client, poi_distance_calculator, and poi_tasks (uvicorn
  logger has no handlers in Celery worker, so all errors were silently dropped)
- Add Celery retry: autoretry_for=(Exception,), max_retries=3, retry_backoff
- Add top-level exception handling in task with full traceback logging
- Fix upsert_distances: replace session.merge() (PK-based) with proper
  dialect-aware INSERT ON DUPLICATE KEY UPDATE / ON CONFLICT DO UPDATE
- Filter out listings with null/zero coordinates before routing
- Raise OSError when all routing engines fail with 0 results computed,
  distinguishing "nothing to compute" from "all engines unreachable"
This commit is contained in:
Viktor Barzin 2026-02-08 20:11:12 +00:00
parent 46995cb9da
commit 5b566bab4c
No known key found for this signature in database
GPG key ID: 0EB088298288D958
5 changed files with 85 additions and 16 deletions

View file

@ -15,7 +15,7 @@ from rec.otp_client import otp_transit_route
from repositories.listing_repository import ListingRepository
from repositories.poi_repository import POIRepository
logger = logging.getLogger("uvicorn.error")
logger = logging.getLogger(__name__)
# Map travel mode names to OSRM profiles
_OSRM_PROFILES = {
@ -61,8 +61,25 @@ async def calculate_poi_distances(
logger.info("No listings found for distance calculation")
return 0
# Filter out listings without valid coordinates
valid_listings = [
l for l in listings
if l.latitude is not None and l.longitude is not None
and l.latitude != 0 and l.longitude != 0
]
if len(valid_listings) < len(listings):
logger.warning(
f"Skipped {len(listings) - len(valid_listings)} listings "
f"with missing coordinates"
)
listings = valid_listings
if not listings:
logger.warning("No listings with valid coordinates")
return 0
total_computed = 0
total_modes = len(travel_modes)
modes_failed: list[str] = []
for mode_idx, mode in enumerate(travel_modes):
mode_upper = mode.upper()
@ -105,6 +122,7 @@ async def calculate_poi_distances(
continue
except (aiohttp.ClientError, OSError) as e:
logger.error(f"Routing engine unreachable for {mode_upper}: {e}")
modes_failed.append(mode_upper)
if on_progress:
on_progress(
total_computed, len(listings) * total_modes,
@ -114,6 +132,13 @@ async def calculate_poi_distances(
total_computed += computed
if modes_failed and total_computed == 0:
failed_str = ", ".join(modes_failed)
raise OSError(
f"All routing engines failed ({failed_str}). "
f"No distances computed for {len(listings)} listings."
)
return total_computed