fix(dispatch): bucket failures by geo cell while the backend omits zone_id
Every booking.assignment_failed observed in production carries no zone_id, so the per-zone failure counter and the once-a-day alert limit collapsed into a single global "unknown" bucket — one alert per day for the whole country. zone_key() prefers the backend's zone_id and falls back to a coarse lat/lon grid cell (1 dp, ~11 km, DISPATCH_GEO_BUCKET_DP). The failure reason is now logged too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012AJLYcbTHCe45fyFnMfEin
This commit is contained in:
@@ -47,6 +47,27 @@ MILER_UNAVAILABLE_STATUSES = {"break", "offline", "busy", "onbreak", "off_duty",
|
||||
|
||||
AVAILABLE, UNAVAILABLE, UNKNOWN = "available", "unavailable", "unknown"
|
||||
|
||||
# The Go backend does not yet send zone_id on booking.assignment_failed (every
|
||||
# event observed carries none). Without a fallback, every failure in the country
|
||||
# shares the single "unknown" bucket, so the per-zone counter and the once-a-day
|
||||
# alert limit would collapse into one global bucket. Derive a coarse grid cell
|
||||
# from the pickup coordinates instead: 1 decimal place is ~11 km, about the size
|
||||
# of a delivery zone. Real zone_ids, once sent, take precedence.
|
||||
GEO_BUCKET_DP = int(os.getenv("DISPATCH_GEO_BUCKET_DP", "1"))
|
||||
|
||||
|
||||
def zone_key(zone_id, lat, lon) -> str:
|
||||
"""Stable bucket for counting failures: the backend's zone_id when present,
|
||||
else a coarse lat/lon grid cell, else 'unknown'."""
|
||||
if zone_id and str(zone_id).lower() not in ("", "unknown", "none", "null"):
|
||||
return str(zone_id)
|
||||
if lat is not None and lon is not None:
|
||||
try:
|
||||
return f"geo:{round(float(lat), GEO_BUCKET_DP)},{round(float(lon), GEO_BUCKET_DP)}"
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return "unknown"
|
||||
|
||||
|
||||
class DispatchAgent(SpecializedAgent):
|
||||
"""
|
||||
@@ -274,11 +295,15 @@ class DispatchAgent(SpecializedAgent):
|
||||
try:
|
||||
data = json.loads(msg.data.decode())
|
||||
booking_id = data.get("booking_id")
|
||||
zone_id = data.get("zone_id", "unknown")
|
||||
lat = data.get("lat")
|
||||
lon = data.get("lon")
|
||||
# Falls back to a coarse geo cell while the backend omits zone_id.
|
||||
zone_id = zone_key(data.get("zone_id"), lat, lon)
|
||||
|
||||
logger.warning(f"[DISPATCH] booking.assignment_failed — booking={booking_id} zone={zone_id}")
|
||||
logger.warning(
|
||||
f"[DISPATCH] booking.assignment_failed — booking={booking_id} zone={zone_id} "
|
||||
f"reason={data.get('reason')!r}"
|
||||
)
|
||||
|
||||
facts, count = await self._gather_assignment_facts(zone_id, lat, lon)
|
||||
logger.info(f"[DISPATCH] booking={booking_id} context facts: {facts}")
|
||||
|
||||
Reference in New Issue
Block a user