docs(readme): separate a lost index entry from genuinely missing data on 23503 - #188
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Expands troubleshooting for PostgreSQL 23503 failures by distinguishing missing parent rows from stale or corrupt indexes.
Changes:
- Adds index-versus-sequential-scan diagnostics.
- Documents collation-version checks and index repair.
- Clarifies missing-data investigation guidance.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
… on 23503 The entry sent every 23503 report down the missing-data path, but a present row the constraint cannot resolve produces the identical error. The foreign key finds its parent through a unique index on three varchar columns, so a btree left stale by a glibc or ICU change fails the check while the operator's own queries still return the row, and the counts they are told to compare come back clean. That reading points at a dump restore when the database is intact and one REINDEX away from correct, and it leaves every other text index in the same database quietly wrong. The forced-seqscan comparison separates the two in one step, so it now runs before the row-count work rather than after it. Signed-off-by: Rob Konsdorf <rob@facings.io>
robrigo
force-pushed
the
docs/fk-lookup-index-corruption
branch
from
August 22, 2026 00:26
ca06e7a to
cb7c252
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The Troubleshooting entry for
23503assumed the parent row is absent. A row that is present but unreachable through the constraint's unique index raises the identical error, and an operator following the entry reaches the wrong repair.Real case: a node reported
atomicassets_assets_schemas_fkeyon(drawncompany, karenbsk, atomicassets), and every diagnostic the entry prescribed came back clean. The schema row was present with the right creation block, the schema count matched a healthy node exactly, the block histogram showed no gap, and a scoped orphan scan returned nothing. The row was there the whole time. What differs is which index each lookup uses: the entry's own diagnostic filters oncollection_namewith anILIKEonschema_name, which cannot use the primary key, while the constraint resolves its parent through that primary key.All three key columns are
character varying, so those btrees order by collation. Moving a data directory onto a base image with a different glibc or ICU invalidates them silently, which is exactly what an image upgrade does. The failure then looks like missing data and is not, and pointing that operator at an 88 GB dump restore would have left every other text index in the database equally wrong.Validation
Documentation only, no code paths touched. Verified against the code: nothing in
src/ordefinitions/deletes fromatomicassets_schemas, and the processor throws on a non-present delta rather than deleting, so a vanished parent row cannot come from the filler. Confirmed the two indexes involved differ,atomicassets_schemas_pkeyon(contract, collection_name, schema_name)againstatomicassets_schemas_collection_schemaon(collection_name, schema_name), which is what lets one lookup miss while the other succeeds. Checked the added lines for em-dash and over-length wrapping, and the fence count stays even.