- Create Microwaveprop.Valkey.Adapter behaviour with command/3 + pipeline/3
- Create Microwaveprop.Valkey.RedixAdapter as the production impl
- Inject adapter via Application config; mock with Mox in tests
- Cover all Valkey operations: get, mget, set, mset_with_ttl, zadd,
zrevrange, zrangebyscore, zrem, del, scan_match (multi-cursor)
- Full round-trip tests for encode/decode, error paths, edge cases
Coverage: 78.93% → 79.07%
Each cached HRRR valid_time is ~32 MiB compressed × cap of 8 = up to
256 MiB per pod. With 4 hot pods + 1 backfill that's ~1.25 GiB of
cluster RAM holding identical content. Replacing the per-pod ETS
replicas with a single shared Valkey copy collapses that to ~256 MiB
total (off-pod) and makes the BEAM heap on each replica meaningfully
smaller — directly addresses the headroom the 2026-05-03 OOM cascade
ate into.
* New `Microwaveprop.Valkey`: single Redix connection (named conn,
sync_connect: false, exit_on_disconnection: false). `start_link/0`
returns `:ignore` when VALKEY_URL is unset so dev/test boot without
Valkey. Helpers wrap GET/MGET/SET-with-TTL/ZADD/ZREVRANGE/SCAN with
`:erlang.term_to_binary` round-tripping under the safe atom flag.
* `Microwaveprop.Weather.GridCache` now branches on
`Valkey.configured?/0`. Public API is identical so callers (Weather,
GridCachePruneWorker, MapLive) don't change. ETS path is preserved
unchanged for dev/test.
* Storage layout when on Valkey:
prop:wg:vts ZSET of valid_time iso8601 strings
prop:wg:<vt>:<lat>:<lon> one chunk per 5°×5° spatial bucket
fetch_bounds MGETs only the chunks intersecting the viewport, so a
regional view is 1-4 round-trips instead of pulling the full grid.
* Per-key TTL = 8 h (matches the ETS valid_time cap). Eviction is
automatic; prune_keep_latest is a no-op on Valkey, prune_older_than
uses ZRANGEBYSCORE + DEL.
* All Valkey errors fall back to disk (ScalarFile.read_bounds), logged
as warnings — page works through Valkey outages, no user-visible
break beyond a one-shot latency bump.
ScoreCache, Microwaveprop.Cache, NexradCache, telemetry/Postgres
protocol/libcluster registry tables remain ETS — they're sub-ms
hot-path lookups where a network hop would be felt.
VALKEY_URL must be present in prop-secrets for production rollout
(applied out-of-band, secret.yaml is git-ignored).