Archive ENTLN lightning clusters (#132) #156
No reviewers
Labels
No labels
area:ai
area:ci-cd
area:notifications
area:observability
area:public-pages
backlog
bug
duplicate
enhancement
help wanted
invalid
question
type:decision
type:feature
type:infra
type:maintenance
type:security
v1.0.1
v1.1.0
v1.2.0
v1.3.0
v2.0.0
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/WeatherBot!156
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lightning-archive"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #132. Final capture-phase issue of the v2.0.0 Historical Explorer epic (#22).
Licensing confirmed by the operator to permit durable storage, so this archives the full cluster data rather than falling back to proximity-events-only.
Problem
Lightning persisted nothing. Parsed clusters lived in a 60-second in-memory cache (
lightning._LIGHTNING_CACHE), and proximity alerts dispatched a syntheticNWSAlertthat was never written tosent_alerts— the only durable trace wasLocation.lightning_last_seen_at/lightning_last_alerted_at. An event timeline could never show lightning activity, however severe the storm.Change
lightning_clusterstable (migration0032) with a UNIQUEdedup_key._process_location, so it costs no extra upstream requests against a paid subscriber feed.RETENTION_LIGHTNING_CLUSTERS_DAYS, default 90.LIGHTNING_ARCHIVE_ENABLED, default true, and a no-op unlessLIGHTNING_PLACEFILE_URLis set.Three decisions worth reviewing
Dedup granularity.
poll_lightningruns every couple of minutes and the feed keeps returning the same recent clusters, so without dedup the row count would track the poll rate rather than actual lightning. The key is built from the cluster's own coordinates and time at ~11 m resolution — lossless within a location's polls, and deliberately not rounded to the ~1.1 km granularity of the fetch cache, which would discard genuinely distinct clusters. It is imperfect across locations, because the placefile is generated per requested centre so two distant locations can receive slightly different groupings of the same strikes. That is inherent to the feed, not something the schema can fix.Archiving happens before the radius filter. The Explorer wants activity across the event window, not only what came close enough to alert on — and
_process_location'sif not nearby: returnwould otherwise skip archiving on exactly the polls where storms are building nearby but not yet in range.Retention is shorter than the record horizon, on purpose. Lightning is by far the highest-volume history the app stores — thousands of rows per storm against a handful of alerts — and its value decays faster, being event-replay texture rather than the alert record itself. 90 days is a starting point to revisit with real numbers in #133.
Known coverage limit
Only locations with
lightning_alert_radius_miset are polled, so archive coverage follows proximity-alert opt-in. If no location has a radius, nothing is archived. Widening that would mean fetching for every location and multiplying requests against a paid feed — a deliberate decision, not something to slip into this PR. Flagging it because it is the same shape as the #129 gap (capture gated on an unrelated opt-in), just with a real cost attached to fixing it.Safety
Fail-soft throughout — the caller is the lightning alert path, and a bookkeeping failure must never cost someone a proximity warning. Batch-internal duplicates are collapsed before insert; a race with an overlapping poll is absorbed by the UNIQUE constraint rather than logging a traceback. The archive is authenticated-only and does not reach the public pages.
Verification
ruff check .python -m compileall appalembic upgrade headon fresh Postgres 1600320032downgrade → re-upgrade round triptests/test_lightning_archive.pycovers dedup-key stability, that nearby-but-distinct clusters do not collide, timezone normalisation, and the core property: three identical polls store 2 rows, not 6. Plus batch-internal collapsing, new-alongside-known, the disabled flag, malformed input survivability, and retention pruning byoccurred_at.🤖 Generated with Claude Code