From 5f90fe6aad1b00147980df93e61160f4d558e9a7 Mon Sep 17 00:00:00 2001 From: Rob Konsdorf Date: Mon, 24 Aug 2026 09:39:27 -0400 Subject: [PATCH] docs(readme): warn that a broken index ordering may have admitted duplicates An operator who reindexes the one index the foreign key needed will see the filler resume and reasonably stop there, while the rest of the database still carries indexes built under the old collation. Worse, the same broken ordering governs uniqueness checks, so a corrupt unique index can have accepted rows it should have rejected. That surfaces as a reindex failing on a duplicate key, which reads as a problem with the reindex rather than as the data damage it is. Naming the amcheck error text as well gives operators something to match against, since the ordering violation aborts the check before the heapallindexed pass it was nominally run for. Signed-off-by: Rob Konsdorf --- README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4c0be070..e08d6e09 100644 --- a/README.md +++ b/README.md @@ -319,17 +319,19 @@ and the lookup cannot see it, and those two causes need different repairs. Settle which one first. The constraint resolves its parent through a unique index, so an index that has lost the entry fails the check while ordinary -queries still return the row. Check the index itself, since `heapallindexed` -is what reports a heap row the index no longer points at: +queries still return the row. Check the index itself, passing `heapallindexed` +so the check also reports heap rows the index no longer points at: ```sql CREATE EXTENSION IF NOT EXISTS amcheck; SELECT bt_index_check('atomicassets_schemas_pkey'::regclass, true); ``` -An error there names the damage. A plain `SELECT` cannot stand in for this -check, because the planner may answer it from a sequential scan or a different -index and return the row either way. +An error there names the damage. `item order invariant violated` means the +entries are stored out of sort order, so lookups binary-search past rows that +exist. A plain `SELECT` cannot stand in for this check, because the planner may +answer it from a sequential scan or a different index and return the row either +way. A lost entry on `character varying` keys usually means collation. Their btrees order by the collation of the key columns, and moving a data directory onto a @@ -349,6 +351,16 @@ change as reason enough to reindex. Repair with `ALTER DATABASE REFRESH COLLATION VERSION`. Refreshing first clears the warning without repairing anything. +Reindexing one index unwedges the filler, but stop only there if the collation +is unchanged. Rebuilding concurrently needs room for a second copy of each +index and leaves invalid `_ccnew` indexes to drop if a run fails. System +catalogs are separate: they need `REINDEX SYSTEM `, which cannot run +concurrently. Read a failure of the form +`could not create unique index ... Key ... is duplicated` as data damage rather +than a reindex problem: the broken ordering also let the uniqueness check pass +rows it should have rejected, and those duplicates have to go before the index +can be rebuilt. + When the index is sound, the parent row really is missing. The rest of this entry covers that case.