No description
Find a file
Graham McIntire 1ec10bec1f
Split ERA5 backfill into submit/poll workers with persistent CDS state
The single Era5MonthBatchWorker pinned an Oban slot for the full 30-45
min a CDS month-tile takes to assemble, and lost the work entirely on a
rolling deploy because the in-flight Task died with the pod. This splits
the flow into two tiny workers and persists the CDS job IDs so deploys
survive.

New pipeline:

1. Era5SubmitWorker (:era5_submit queue) — POSTs both CDS requests in
   parallel, writes an `era5_cds_jobs` row with the returned job IDs,
   and enqueues an Era5PollWorker scheduled +5 min. ~1s of real work.
   Short-circuits when the month-tile is already cached or when a row
   already exists (in-flight from a previous attempt).

2. Era5PollWorker (:era5_poll queue) — reads the row, calls
   Era5Client.check_status/1 for both CDS job IDs, and:
     - returns {:snooze, 300} if either job is still running (Oban
       re-schedules without counting an attempt and releases the slot
       immediately — a pod can keep dozens of tile-months in flight
       without pinning workers)
     - streams both GRIB files to disk via Req into: File.stream!,
       decodes via Wgrib2.extract_grid_messages_from_file, bulk-inserts
       via Era5BatchClient.decode_and_insert/6, deletes the row, and
       DELETEs both completed jobs from CDS to free server-side quota
     - if either leg CDS-reports failed, deletes the row + both CDS
       jobs and returns {:error, reason}

Era5Client gains four testable building blocks:
  submit_job/2               (bare POST → {:ok, job_id})
  check_status/1             (GET → :running | {:done, src} | {:failed, reason})
  download_source_to_file/3  (streams {:url, href} or writes {:body, bin})
  delete_job/1               (DELETE /jobs/:id, treats 200/202/204/404 as :ok)

All Req calls now route through `era5_req_options` so tests can stub
CDS responses via Req.Test.stub(Era5Client, fn).

Era5MonthBatchWorker is retained as a thin forwarder to Era5SubmitWorker
so any jobs already in the :era5_batch queue on prod pods drain cleanly
on the next rolling deploy. Safe to delete in a follow-up.

Adds era5_cds_jobs table with a unique index on
(year, month, tile_lat, tile_lon) so duplicate submits collapse.

New queue config in runtime.exs:
  era5_submit: local_limit 4, rate_limit 30/hour (burst protection)
  era5_poll:   local_limit 20 (polls are cheap GETs)
  era5_batch:  kept at 1 for legacy job drain, delete next cycle
2026-04-13 16:26:26 -05:00
.forgejo/workflows Install Docker CLI in CI runner container 2026-04-11 09:04:22 -05:00
.github Add contact edit approval system with admin review queue 2026-04-11 16:15:49 -05:00
assets Refresh viewshed on band change when a point is selected 2026-04-13 09:41:58 -05:00
bin Fix livebook path resolution in bin/notebook 2026-04-07 11:15:49 -05:00
config Split ERA5 backfill into submit/poll workers with persistent CDS state 2026-04-13 16:26:26 -05:00
docs Refresh 2026-04-13 recalibration report 2026-04-13 12:29:51 -05:00
k8s chore: update prop image to git.mcintire.me/graham/prop:main-1776114041-156b170 [skip ci] 2026-04-13 21:01:38 +00:00
lib Split ERA5 backfill into submit/poll workers with persistent CDS state 2026-04-13 16:26:26 -05:00
lib_ml Move ML deps to dev/test only, exclude from production build 2026-04-01 12:10:31 -05:00
notebooks Auto-deploy analysis notebook as Livebook app 2026-04-07 11:15:49 -05:00
priv Split ERA5 backfill into submit/poll workers with persistent CDS state 2026-04-13 16:26:26 -05:00
rel Add Erlang clustering via libcluster and expose LiveDashboard 2026-04-06 09:46:48 -05:00
scripts Extend recalibrate_algo.py with NEXRAD, native-duct, commercial sections 2026-04-13 12:14:05 -05:00
test Split ERA5 backfill into submit/poll workers with persistent CDS state 2026-04-13 16:26:26 -05:00
vendor Vendor oban_pro 1.6.13 2026-04-09 14:14:49 -05:00
.credo.exs credo 2026-04-12 10:26:53 -05:00
.dockerignore Optimize Dockerfile layer caching 2026-04-01 10:32:56 -05:00
.formatter.exs initial 2026-03-28 11:28:47 -05:00
.gitignore Fix /weather 500 on cold cache, untrack k8s secret 2026-04-12 13:56:59 -05:00
.tool-versions update deps 2026-04-11 12:03:43 -05:00
AGENTS.md initial 2026-03-28 11:28:47 -05:00
algo.md Wire NEXRAD, native ducts, and commercial links into scoring 2026-04-13 12:08:15 -05:00
app.json Migrate healthchecks from CHECKS to app.json 2026-04-04 11:24:56 -05:00
CLAUDE.md docs: update deployment notes for Kubernetes 2026-04-13 15:30:59 -05:00
Dockerfile Mount Oban Web dashboard at /admin/oban 2026-04-09 14:07:33 -05:00
elevation.md add elevation findings 2026-03-29 16:23:03 -05:00
mix.exs Unblock CI: pull igniter into :prod for live_table compile 2026-04-12 17:58:13 -05:00
mix.lock Add live_table dep and convert /users to use it 2026-04-12 17:07:07 -05:00
prediction.md HRRR forecast hours (f00-f18) with timeline map UI 2026-03-31 16:44:47 -05:00
README.md initial 2026-03-28 11:28:47 -05:00
tail_logs.sh Add type filter to backfill enqueue 2026-04-06 11:42:26 -05:00
updates.md Update seasonal scores from sounding data, document commercial link patterns 2026-04-07 11:45:44 -05:00

Microwaveprop

To start your Phoenix server:

  • Run mix setup to install and setup dependencies
  • Start Phoenix endpoint with mix phx.server or inside IEx with iex -S mix phx.server

Now you can visit localhost:4000 from your browser.

Ready to run in production? Please check our deployment guides.

Learn more