Thermograph monorepo: graded-climate API + SSR frontend + infra, domain-specific containerized deploys
Find a file
Emi Griffith 956153691a Report the notifier's health by heartbeat, not per-worker threads (#166)
The ops dashboard marked subscription-notifier DOWN ~2/3 of the time after the
notifier was gated to a single leader worker. It judged the daemon by whether
its thread was present on whichever worker answered /api/v2/metrics, but only
the leader runs it — so polls landing on the other two workers saw no thread and
reported DOWN for a perfectly healthy notifier. Per-worker thread checks can't
describe a single-leader daemon, and worse, they could no longer distinguish a
real crash from a normal non-leader.

Report it by liveness instead: the notifier stamps a heartbeat (timestamp +
expected interval) into the shared metrics store each loop iteration, including
once at startup so the dashboard shows it up immediately after a restart. Since
the beat lives in the shared SQLite store, any worker's metrics response carries
it, so the dashboard sees the notifier's health whichever worker it polls.

- metrics.py: both stores record/expose heartbeats (SQLite reuses the meta
  table, so every worker's beats share one file); snapshot() converts each beat
  to a server-computed age so readers needn't reconcile clock skew.
- notify.py: beat at loop start and every wake.
- dashboard.py: judge heartbeat daemons by freshness (up to 2 missed intervals
  before DOWN, with beat age shown); neighbor-warmer keeps its per-worker thread
  check, which is correct since every worker runs it.
- Tests: heartbeat store/snapshot shape + cross-worker sharing; dashboard
  fresh/stale/missing rendering and the unchanged warmer check. 194 passed.

Co-authored-by: root <root@vmi3417050.contaboserver.net>
2026-07-17 13:49:30 +00:00
templates Add an Alerts page to the nav with a "what are alerts" explainer (#153) 2026-07-16 16:41:24 -07:00
tests Report the notifier's health by heartbeat, not per-worker threads (#166) 2026-07-17 13:49:30 +00:00
api_accounts.py Push: surface delivery failures (no longer a silent success) (#157) 2026-07-17 00:12:14 +00:00
app.py Gate the subscription notifier to one worker (#161) 2026-07-17 12:54:55 +00:00
audit.py Dashboard: accounts totals only; log client IPs per request (#140) 2026-07-16 21:58:40 +00:00
cities.json SEO: values filter — trim cities in anti-LGBTQ countries, keep notable hubs (#102) 2026-07-16 02:20:41 +00:00
cities.py Climate hub: client-side search + alphabetical results (#110) 2026-07-16 04:44:18 +00:00
cities_flavor.json SEO: values filter — trim cities in anti-LGBTQ countries, keep notable hubs (#102) 2026-07-16 02:20:41 +00:00
city_events.py SEO: broaden curated weather events beyond the US/Anglosphere (#101) 2026-07-16 01:42:49 +00:00
climate.py Add ops metrics endpoint + SSH/Termux dashboard (#131) 2026-07-16 20:46:59 +00:00
content.py Fix BreadcrumbList JSON-LD: omit unlinked intermediate crumbs (#164) 2026-07-17 13:04:51 +00:00
db.py Fix mobile alerts dropdown overflow; add account API tests (#93) 2026-07-15 22:00:47 +00:00
gen_cities.py SEO: values filter — trim cities in anti-LGBTQ countries, keep notable hubs (#102) 2026-07-16 02:20:41 +00:00
gen_flavor.py SEO: expand city set to 1000 (extended English-speaking / high-proficiency) (#99) 2026-07-16 01:14:30 +00:00
grading.py Records: show longest dry streak instead of record-low precipitation (#105) 2026-07-16 03:28:26 +00:00
grid.py Warm neighbor cells server-side (/cell?neighbors=1) (#51) 2026-07-11 20:47:31 +00:00
indexnow.py Auto-submit IndexNow on deploy when the URL set changes (#130) 2026-07-16 13:38:59 -07:00
metrics.py Report the notifier's health by heartbeat, not per-worker threads (#166) 2026-07-17 13:49:30 +00:00
migrate.py Migrate backend dataframe layer from pandas to polars (#90) 2026-07-15 19:07:38 +00:00
models.py Add PWA + Web Push delivery for weather alerts (#95) 2026-07-15 23:21:06 +00:00
notify.py Report the notifier's health by heartbeat, not per-worker threads (#166) 2026-07-17 13:49:30 +00:00
places.py Move the suggestion policy into places.py (#45) 2026-07-11 20:00:24 +00:00
push.py Push: surface delivery failures (no longer a silent success) (#157) 2026-07-17 00:12:14 +00:00
requirements-dev.txt Add backend test suite; gate direct pushes; serialize LAN deploys (#41) 2026-07-11 19:37:49 +00:00
requirements.txt SEO: crawlable programmatic climate pages + technical hygiene (#96) 2026-07-15 23:53:11 +00:00
schemas.py Add PWA + Web Push delivery for weather alerts (#95) 2026-07-15 23:21:06 +00:00
singleton.py Gate the subscription notifier to one worker (#161) 2026-07-17 12:54:55 +00:00
store.py Compare/calendar mobile polish: overflow fix, sheet load button, 6-yr default, country in names (#80) 2026-07-12 06:32:10 +00:00
users.py Account system with weather-notification subscriptions (#89) 2026-07-15 18:46:46 +00:00
views.py Migrate backend dataframe layer from pandas to polars (#90) 2026-07-15 19:07:38 +00:00
warm_cities.py SEO: crawlable programmatic climate pages + technical hygiene (#96) 2026-07-15 23:53:11 +00:00