Weather review fixes: zone-fetch time budget, prod NWS_USER_AGENT (Refs #237)
- Cap one refresh's NWS zone fetches at 15 s wall clock. They run under the feed lock, so a hung zone endpoint could otherwise stall every /weather/alerts request for minutes; unfinished zones retry next refresh. - Prod c2-core.env.j2 never set NWS_USER_AGENT, so the VM would have sent the contact-less placeholder. Default it to the site URL (overridable via vault_nws_user_agent); warn on the .env.example example.com placeholder too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
fb1b6956c2
commit
bdbde359a1
@@ -48,6 +48,11 @@ ZONE_CONCURRENCY = 6
|
||||
# the next refresh (the zone cache fills across refreshes); meanwhile the alert
|
||||
# shows in zone_only or with the zones resolved so far.
|
||||
MAX_ZONE_FETCHES_PER_REFRESH = 80
|
||||
# Wall-clock cap on one refresh's zone fetches. The refresh runs under the feed
|
||||
# lock, so without it a slow zone endpoint (20 s timeout x 80 fetches / 6 wide)
|
||||
# could hold every /weather/alerts request for minutes. Unfinished zones are
|
||||
# simply not cached and are retried next refresh.
|
||||
ZONE_BUDGET_S = 15.0
|
||||
|
||||
WIND_TTL_S = 5 * 60
|
||||
WIND_CELL_DEG = 0.5 # wind cache cell; one upstream call per cell per TTL
|
||||
@@ -61,7 +66,7 @@ _warned_ua = False
|
||||
def _headers(accept: str) -> Dict[str, str]:
|
||||
global _warned_ua
|
||||
ua = settings.nws_user_agent or DEFAULT_USER_AGENT
|
||||
if ua == DEFAULT_USER_AGENT and not _warned_ua:
|
||||
if (ua == DEFAULT_USER_AGENT or "example.com" in ua) and not _warned_ua:
|
||||
_warned_ua = True
|
||||
logger.warning("NWS_USER_AGENT is the placeholder -- set it to 'app (contact email)'; NWS may block us")
|
||||
return {"User-Agent": ua, "Accept": accept}
|
||||
@@ -193,7 +198,12 @@ async def _resolve_zones(client: "httpx.AsyncClient", alerts: List[Dict[str, Any
|
||||
wanted.append(z)
|
||||
if wanted:
|
||||
sem = asyncio.Semaphore(ZONE_CONCURRENCY)
|
||||
await asyncio.gather(*(_fetch_zone(client, z, sem) for z in wanted[:MAX_ZONE_FETCHES_PER_REFRESH]))
|
||||
try:
|
||||
await asyncio.wait_for(
|
||||
asyncio.gather(*(_fetch_zone(client, z, sem) for z in wanted[:MAX_ZONE_FETCHES_PER_REFRESH])),
|
||||
timeout=ZONE_BUDGET_S)
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(f"NWS zone fetches over {ZONE_BUDGET_S:.0f}s budget; the rest resolve next refresh")
|
||||
for a in alerts:
|
||||
if a["geometry"] is not None:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user