feat(admin): backfill-vector-indexes command to reconcile per-bank vector index coverage (#2645) - #2712
feat(admin): backfill-vector-indexes command to reconcile per-bank vector index coverage (#2645)#2712benfrank241 wants to merge 1 commit into
Conversation
…ctor index coverage (#2645)
|
@nicoloboschi flagging you on this one — it's the part-1 escape-hatch PR for #2645 (per-bank vector index coverage never self-heals on restore/upgrade/extension-switch → ~30% recall@10 miss, measured by @iRonin on a live 40-bank deployment). I built + verified it (admin CI status: our 4 new tests pass (green in @iRonin has offered to run their recall@K harness against this on a staging copy of their deployment for a real before/after — good validation path once you're happy with the shape. |
|
Ran the command's detection logic as READ-ONLY SELECTs against our live 0.8.4 deployment (the one from #2645 — prod can't run the branch binary; it already carries the manual backfill discussed there). What validates cleanly:
Finding 1 (the one I'd block on) — a stale INVALID index is mis-detected as "present", silently defeating the fix for exactly the operator state #2645 targets. Chain: (a) detection is name-based via Finding 2 — name-only detection double-builds where an operator hand-built equivalents under different names. The remaining 3 of our 36 flagged are on our single largest bank (~118k rows), which we covered by hand before this command existed using non-standard index names (identical predicate + opclass). The command would build 3 duplicate system-named indexes on the biggest table in the run — harmless to correctness, but wasted CONCURRENTLY build time + permanent write amplification in triplicate. Suggest either a coverage-aware check (any valid partial index on Nits: alphabetical build order means the largest, most recall-impacted banks converge last (consider row-count desc); the failure-cleanup Happy to provide the recall@K before/after on a staging copy — one harness note: the deployment needs |
|
Closing in favor of #2872 (merged as bd853be), which took a minimal cut of this work. Thank you @benfrank241 — this PR established the operator-escape-hatch concept and the One substantive change from this PR worth calling out: the coverage check here uses a name-only |
Addresses #2645 (part 1 of 2).
Problem
Per-(bank, fact_type) partial vector indexes are only created at fresh-bank-creation time. Banks that arrive already populated — via logical restore, cross-version upgrade, or a vector-extension switch (e.g. ScaNN→pgvector) — never hit that path, so their recall silently falls back to the global HNSW + post-filter. As @iRonin measured on a live 40-bank deployment, that's not just a latency hit: it's recall@10 of ~0.63–0.72 (only ~6.3–7.5 of 10 in-bank neighbors returned) — a silent ~30% top-10 miss, i.e. a correctness regression, on any bank in the uncovered state.
This PR — the operator escape hatch
hindsight-admin backfill-vector-indexesreconciles the missing per-bank partial indexes on populated banks — first-classing exactly what @iRonin did by hand (63 indexes across 20 banks).CREATE INDEX CONCURRENTLY— never takesACCESS EXCLUSIVEon the sharedmemory_units, so backfilling one bank can't stall the fleet's retain/recall/consolidation. (It runs on the admin CLI's raw autocommit connection, sinceCONCURRENTLYcan't run in a txn.)pg_indexesexistence check +IF NOT EXISTS; safe to re-run.--dry-runreports what would be built without touching anything.--schema)._bank_index_name/_BANK_INDEX_FACT_TYPES/_vector_index_clause) so backfilled indexes are byte-identical to create-time ones.Deliberately scoped — follow-up (part 2)
Per the design locked in #2645, the ongoing convergence layer (boot/periodic background reconcile as the primary guarantee, plus a lightweight retain-path
pg_classexistence check as belt-and-suspenders) is a separate PR — it touches the worker/retain hot paths and deserves its own review. This PR is the immediate, self-contained escape hatch that closes the current uncovered state and is directly testable.Tests
tests/test_backfill_vector_indexes.py(4 tests, deterministic, pg0):--dry-runcreates nothing;cc @iRonin — this is the command; happy to have you point it at the staging copy of your deployment with your recall@K harness for a before/after once CI is green. The boot-reconcile follow-up will land separately.