Fix #218 - could not open relation with OID - #295
Open
twooster wants to merge 2 commits into
Open
Conversation
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.
Full disclosure: This is a Fable bug-hunt and PR for bug #218
I personally don't know enough about pg-internals to fix this bug. At the very least, if you want to fix it differently, Fable wrote some repros for the bug. We hit this bug at least once a month and it wedges our replication slots badly.
Fix "could not open relation with OID" during decoding after CREATE INDEX CONCURRENTLY
The bug
wal2json calls RelationGetIndexAttrBitmap() from its change callback — on every DELETE, every UPDATE without an old tuple, and on all changes when include-pk is set. That function opens every index on the table from rd_indexlist, including invalid, in-progress ones created by CREATE INDEX CONCURRENTLY. It is a current-snapshot, lock-taking API; no in-core output plugin calls it during decoding (pgoutput uses the decoding-safe RelationGetIdentityKeyBitmap() instead).
During logical decoding, transactions are replayed in commit order but each under its own historic snapshot, so the installed snapshot moves backward across transaction boundaries while the backend's relcache persists. If decoding a newer transaction caches an index created by a concurrent CIC into rd_indexlist, and an older, longer-running transaction's change is then decoded, RelationGetIndexAttrBitmap() tries to open that index under a snapshot to which its pg_class row is not yet visible:
ERROR: could not open relation with OID
Because this is a deterministic function of WAL order, every restart fails at the same LSN — the slot is permanently wedged.
How to trip it
No special options needed; the index doesn't have to be related to the replica identity. The minimal interleaving (encoded in the new isolation test):
In the wild this shows up on busy databases running CREATE INDEX CONCURRENTLY (or drop/create, REINDEX CONCURRENTLY) while long-lived write transactions are in flight.
The fix
Replace RelationGetIndexAttrBitmap() with a helper, get_index_column_bitmap(), that builds the column bitmap from the single index actually needed — rd_replidindex for identity output, rd_pkindex for include-pk — opened via lock-free RelationIdGetRelation(), mirroring pgoutput's RelationGetIdentityKeyBitmap(). If the index can't be opened under the current historic snapshot, it returns NULL and output degrades gracefully (keys omitted) instead of erroring and wedging the slot. Semantics are unchanged in all normal cases: the PK/identity bitmaps were always derived from exactly these single indexes; PG < 10 include-pk behavior is untouched.
Testing