Auto-warmup warms the current generation when an NCBI update is available¶
Motivation¶
Operators reported BLAST database chips stuck at warm stale on a Running
cluster, with auto-warmup enabled, that never recovered. Live triage
(elb-cluster-02) showed both 16S_ribosomal_RNA and core_nt as warm
Stale with nodes_ready=0, force_rewarm_pending=true, yet the every-2-minute
reconcile_auto_warmup returned status: ready_noop and enqueued nothing.
Root cause: the per-database reconcile loop skipped any DB whose downloaded
source_version differed from the current NCBI latest-dir with
reason="update_required" and a hard continue. NCBI rolls a new daily
snapshot, so within a day of any download the condition is permanently true.
Auto-warmup does not auto-download a new generation (that is an explicit,
hundreds-of-GB prepare-db action), so the DB was left cold/Stale forever once
node invalidation (AKS stop/start or node rotation) flipped the warm Jobs stale.
User-facing change¶
Auto-warmup now warms the currently downloaded generation even when a newer
NCBI snapshot exists, instead of skipping. Node invalidation therefore recovers
automatically: searches keep working on the warm cache the operator already
downloaded. The available update is surfaced as an informational
update_available entry on the reconcile result (per cluster) rather than a
skipped / update_required entry. Auto-downloading the new generation remains
an explicit user action (Storage card → Update).
A DB that is already healthily warm for the current generation is still skipped
(reason="Ready"), so there is no re-warm churn — only Stale / not-warm /
Failed DBs are (re)warmed, exactly as for a DB with no pending update.
API / behaviour diff summary¶
- api/services/auto_warmup_reconcile.py:
the
update_requiredbranch no longercontinues. It recordsresult["update_available"].append({db, source_version, latest_version})and falls through to the existing warm-state logic, which (re)warms the current generation when the DB is not healthily warm. No change to the warm/re-warm mechanics: the warmup task already callsk8s_release_stale_warmup_jobs(...)unconditionally, so Jobs pinned to gone nodes are released and recreated on the current Ready nodes withforce_rewarm=False. - Reconcile result shape: new optional
update_availablelist (only present on clusters with drift). Theskippedlist no longer carriesupdate_requiredentries. No frontend consumedupdate_required(grep-verified); no IaC change.
Validation evidence¶
- Rewrote
test_reconcile_auto_warmup_warms_current_generation_when_update_available(was…_skips_stale_downloaded_generation) to assertstatus="triggered",enqueued=[core_nt],update_available=[…], and noupdate_requiredskip. uv run pytest -q api/tests/test_auto_warmup.py→ 32 passed.uv run pytest -q api/tests/test_auto_warmup.py api/tests/test_warmup_jobs.py api/tests/test_warmup_route.py api/tests/test_storage_data.py→ 117 passed.uv run ruff check api/services/auto_warmup_reconcile.py api/tests/test_auto_warmup.py→ clean.- Live diagnosis evidence (read-only): NCBI
latest-dir=2026-06-06-01-05-02, downloaded16S/core_nt=2026-06-02→update_requiredskip →ready_noopevery tick while DBs satStale nodes_ready=0. Cluster nodes were Ready 5/5.
Deploy¶
Baked into the api/worker image (Celery task + service). Needs an api/worker
redeploy to take effect on the live cluster; after deploy the next beat tick
re-warms the current generation and the chips clear from warm stale.