prop/lib/microwaveprop/workers/mrms_fetch_worker.ex
Graham McIntire 4e6c87eca2
feat(telemetry): broaden Instrument span coverage
Adds spans to 15 previously-unmeasured hot paths so every question we
might ask while tuning has a histogram to answer it:

External I/O:
- iem.fetch_iemre (gridded weather reanalysis)
- mrms.list_latest / mrms.download (precip radar)
- rtma.fetch_observation
- ncei.fetch_metar (historical 5-min METAR backfill)
- solar.fetch_indices (GFZ solar indices)
- swpc.fetch (SWPC Kp/F10.7/X-ray)
- giro.fetch (ionosonde)
- qrz.request, geocoder.geocode (callsign enrichment)
- srtm.download_tile (terrain tile download + gunzip)
- hrrr.download_grib_ranges (parallel byte-range fetch phase)

Subprocess:
- wgrib2.extract_grid / extract_grid_from_file / extract_grid_from_file_mapped

LiveView hot paths:
- propagation.scores_at (map score fetch + cache hit/miss counter)
- propagation.point_forecast (sparkline)
- propagation.point_detail (click-to-inspect)
- propagation.daily_outlook_at (/map outlook strip)

Worker-level end-to-end:
- worker.terrain_profile
- worker.mechanism_classify
- worker.mrms_fetch

Each event is registered in Microwaveprop.PromEx.InstrumentPlugin as
a Prometheus histogram (default / long buckets as appropriate) plus
a counter for the scores_at cache hit/miss ratio. Prometheus at
10.0.15.25 will start seeing the new series on the next scrape after
deploy.
2026-04-18 17:25:33 -05:00

43 lines
1.4 KiB
Elixir

defmodule Microwaveprop.Workers.MrmsFetchWorker do
@moduledoc """
Every ~2 minutes, pull the newest NOAA MRMS PrecipRate grid and cache
the regridded version (0.125° CONUS) in `Microwaveprop.Weather.MrmsCache`.
`Microwaveprop.Workers.AsosAdjustmentWorker` reads that cache on its
own tick to overlay real radar rain rates onto the score grid.
Runs on the `propagation` queue so it shares the scoring node's crontab
and isn't competing with the heavier HRRR pipeline for slots.
"""
use Oban.Worker, queue: :propagation, max_attempts: 2
alias Microwaveprop.Weather.MrmsCache
alias Microwaveprop.Weather.MrmsClient
require Logger
@impl Oban.Worker
def perform(%Oban.Job{}) do
Microwaveprop.Instrument.span([:worker, :mrms_fetch], %{}, fn ->
case MrmsClient.fetch_latest(MrmsCache.valid_time()) do
{:ok, valid_time, grid} ->
MrmsCache.broadcast_put(valid_time, grid)
Logger.info("MrmsFetch: cached #{map_size(grid)} cells at #{valid_time} (#{count_rainy(grid)} with rain)")
:ok
{:up_to_date, valid_time} ->
Logger.debug("MrmsFetch: cache already has #{valid_time}")
:ok
{:error, reason} ->
Logger.warning("MrmsFetch: skipped — #{inspect(reason)}")
:ok
end
end)
end
defp count_rainy(grid) do
Enum.count(grid, fn {_, v} -> v > 0 end)
end
end