Thermograph monorepo: graded-climate API + SSR frontend + infra, domain-specific containerized deploys
Find a file
Emi Griffith da1b2c8be1 Gate the subscription notifier to one worker (#161)
Prod runs 3 uvicorn workers, but the in-process subscription notifier was
started in every worker's lifespan (it was written assuming a single-worker
deploy). Its sweep fetches recent-forecast/archive data from Open-Meteo on a
15-min timer independent of any request, so running it 3× tripled the
background upstream load against a shared quota — the source of the overnight
429/503 rate-limit errors in logs/errors, and 3× redundant subscription scans.

Elect one worker to own the notifier via a non-blocking exclusive flock on a
lockfile (THERMOGRAPH_SINGLETON_LOCK): the first worker wins and holds the fd
for its lifetime; others stand down; the OS releases the lock if the leader
dies, so a restart re-elects cleanly. Unset (single worker / dev / tests)
always wins, so behavior there is unchanged. Mirrors the shared-metrics store's
env-selected cross-worker coordination.

The neighbor warmer stays per-worker on purpose: each worker drains its own
request-fed queue, so gating it would leave non-leader queues undrained.

- backend/singleton.py: flock-based leader election (claim()).
- backend/app.py: gate notify.start() on singleton.claim().
- deploy/thermograph.service: default THERMOGRAPH_SINGLETON_LOCK to
  data/notifier.lock (needs the unit reinstalled on prod to take effect).
- deploy/thermograph.env.example: document it alongside WORKERS.
- Tests: leader election — single-worker default, first-wins, idempotent
  re-claim, second-holder stands down, re-election after release. 183 passed.

Co-authored-by: root <root@vmi3417050.contaboserver.net>
2026-07-17 12:54:55 +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 Gate the subscription notifier to one worker (#161) 2026-07-17 12:54:55 +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 Add IndexNow, stable sitemap lastmod, and search-verification meta (#127) 2026-07-16 18:08:09 +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 Sync main into dev (multi-worker prod + shared metrics store) (#160) 2026-07-17 02:43:22 +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 Add PWA + Web Push delivery for weather alerts (#95) 2026-07-15 23:21:06 +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