Weather Caching Behavior
Scope
This document defines the runtime lookup policy for getWeatherPointCached(...)
in src/services/weatherService.ts. Daily work-area pre-warming uses the same
weather service module but follows its own dedicated 24-hour horizon warming
path through precacheWeatherForLocations(...).
Canonical Flow
- Validate
latandlonbefore any cache or network work. - Normalize
targetTimewithtoHourKey(...)and derive the cache cell withcalcWeatherCacheKey(...). - Try an exact cache lookup for
(cell_id, forecast_hour). - Return the cached point only when the exact row exists and
Date.now() < valid_to. - When an exact row exists but is stale, delete it before continuing. Delete failures are logged and do not block refresh.
- When the requested hour is the current wall-clock hour and the exact row is unavailable, allow a fresh same-cell row for the next forecast hour. This covers upstream NDFD responses that start one hour after the requested current hour.
- On miss or exact-read failure, fetch one NDFD series with
fetchNdfdWeatherSeries(lat, lon, 24, 12000, forecast_hour). - Persist fetched points with
putForecastPoints(...)and enforce bounded size withevictLRU(12000). - Return the fetched exact-hour point when it exists, even if cache persistence or eviction fails.
- When the requested hour is the current wall-clock hour and the fetched series omits that hour, return the fetched next-hour point when it exists.
- When refresh fails or the fetched series does not contain the requested hour or allowed current-hour substitute, query nearby cached rows for the same normalized hour.
- Nearby fallback accepts only fresh rows whose cell center is within 5 km of the requested coordinates, and returns the nearest candidate.
Daily Pre-Cache Flow
- Validate and dedupe requested work-area locations by weather cache cell.
- For each unique cell, fetch one fresh NDFD 24-hour series anchored to the requested hour, with bounded fanout of 8 in-flight requests instead of launching the full work-area batch at once.
- Persist every returned hourly point with one shared
valid_to, then enforce bounded size with one post-batchevictLRU(12000)call after the warm set completes. - Count the cell as successfully warmed when the fetched series covers either:
- the requested hour through the next 23 hours, or
- the next 24 hours starting one hour later when the current hour is unavailable upstream.
- Pre-cache failures are summarized per cell and do not change foreground runtime lookup semantics.
Failure Semantics
- Exact-cache read failures are treated as warnings and the service continues to refresh or fallback.
- Exact-hit
last_accessedupdates happen insidegetForecastPoint(...); if that update fails, the exact read is treated as failed. - Nearby-fallback
touchForecastPointAccess(...)is best effort only; touch failures do not block returning fallback weather. - When all paths fail,
getWeatherPointCached(...)throwsWeatherCacheMissError. - Miss-error cause priority is: refresh failure, stale-delete failure, nearby-fallback read failure, exact-cache read failure, then generic miss.
Supporting References
// TODO LUCAS CAN’T BE BOTHERED TO FIX THIS RN
- Schema: Local Database/weather-cache
- Diagram: ../diagrams/weather-cache-flow.mmd
- Daily pre-cache flow: ./daily-work-area-precache.md
Invariants
- Runtime orchestration lives in
src/services/weatherService.ts; persistence helpers stay insrc/db/weatherCacheDb.ts. - Cache storage is normalized to UTC hour keys via
toHourKey(...). - The fallback path never returns stale rows.