-- pg_turbovec 1.28.4 -- -- Corruption fix: eliminate the dual row-counter drift + add an -- operator-facing integrity check. Patch bump -- no wire-format -- change (stays v7), no REINDEX required by the upgrade itself -- (a currently-corrupt index still needs DROP + CREATE; see below). -- -- This file is a *reference mirror*. The authoritative install -- script (including the new turbovec.turbovec_check(regclass) -- function) is generated by `cargo pgrx schema`. -- -- Changes: -- -- B (root cause) -- the deferred aminsert flush -- (xact::flush_to_relfile) used to persist PersistState.n_vectors -- (a SEPARATELY-incremented counter) as the on-disk row count, -- passed alongside idx.slot_to_id() as an independent argument. -- If the two drifted, the meta page could claim MORE rows than the -- ids chain held, and reload (read_full) would over-read the ids -- chain into zeroed trailing slots -- surfacing as "duplicate ids -- in the .tvim id table (id 0 appears in more than one slot)" (a -- real CTID never encodes to 0, so an id-0 slot is always zeroed -- bytes). The flush now DERIVES the persisted count from -- idx.slot_to_id().len() (the single source of truth) via -- xact::reconciled_row_count, and a hard runtime guard aborts the -- transaction if the mirror ever drifts (belt-and-suspenders with -- the existing assert_eq! in relfile::write_full_inner) rather than -- letting a mismatched meta page land on disk. -- -- A (recovery) -- with B fixed, a REINDEX (which allocates a fresh -- relfilenode; the per-backend cache drops the stale entry on the -- relfilenode mismatch) produces a clean bijection that the write -- path can no longer re-corrupt, so REINDEX now durably repairs. -- -- C (crash-safety) -- KNOWN REMAINING GAP, scoped honestly here, -- NOT closed by this release. An unclean shutdown / `pg_resetwal` -- can still discard WAL that extended the .tvim id chain, leaving -- two slots claiming id 0. The insert AND read paths already ERROR -- loudly on such a relfile with `HINT: REINDEX INDEX ;` -- (v1.28.2), and this release adds turbovec_check() so it's -- detectable WITHOUT attempting a write. Full WAL-crash-safety of -- the id table is a larger durability change, deliberately not -- attempted in a corruption patch. Recovery for an -- already-corrupt index: REINDEX INDEX ; (now durable), or -- DROP + CREATE if the corruption predates 1.28.4. -- -- D (integrity check) -- new read-only, ownership-checked function: -- -- turbovec.turbovec_check(regclass) -- RETURNS TABLE( -- wire_version integer, -- kind text, -- n_vectors bigint, -- slot_count bigint, -- count_matches boolean, -- duplicate_id bigint, -- is_corrupt boolean, -- tombstone_density double precision) -- -- Reads the meta + ids chain and reports duplicate-id presence -- (flat kind), n_vectors-vs-slot_to_id-length mismatch, wire -- version, index kind, and tombstone density. Takes only -- AccessShareLock, so it never blocks writers. Non-owners get a -- permission-denied ERROR. Monitoring should alert on is_corrupt. -- -- `ALTER EXTENSION pg_turbovec UPDATE TO '1.28.4';` is sufficient and -- cannot fail on existing indexes.