fix(cluster): shared-catalog native-era XID authority + prehistory adoption#24
Open
sqlrush wants to merge 7 commits into
Open
fix(cluster): shared-catalog native-era XID authority + prehistory adoption#24sqlrush wants to merge 7 commits into
sqlrush wants to merge 7 commits into
Conversation
added 3 commits
July 8, 2026 16:53
Publish and seal native-era XID authority after seed shutdown, adopt prehistory from per-node anchors, and max-merge startup nextXid before ShmemVariableCache seeding. Spec: spec-6.15b-xid-authority-native-era.md
A second cluster.enabled=off seed pass on a SEALED authority re-opens the native era: clear SEALED at bootstrap so a crash of that pass leaves joiners fail-closed (53RB5, unsealed) instead of silently adopting the previous pass's stale high-water -- false-invisible for every xid the crashed pass consumed. The pass's own clean shutdown re-publishes and re-seals monotonically. Adds cluster_xid_authority_begin_native_run(), the bootstrap enabled=off arm call, unit coverage (seal kept-hw/cleared-flag/idempotent/re-seal), and t/361 N4 (sealed pass-1 -> unsealing pass-2 boot -> immediate-kill -> joiner refuses adoption). Spec: spec-6.15b-xid-authority-native-era.md
…n-gate window spec-6.15b D6 flipped both tests to online_join+xid_striping: formation now passes through the join gate, whose home-block rebuild window transiently answers 'block master is recovering ... retry is safe' -- even at connection time, via the pg_authid read. Retry the first-contact probes and first DDLs (bounded, 60s); every semantic assert stays strict. Spec: spec-6.15b-xid-authority-native-era.md
6db1da2 to
964e587
Compare
added 4 commits
July 8, 2026 17:08
…n unsupported seed, .bak read guard, multi-segment unit leg Review P2-1: the native-era publish+seal now runs only on a TRUE shutdown checkpoint (END_OF_RECOVERY excluded -- sealing mid-run after crash recovery would expose a stale high-water to joiners), and an unsupported native era (MultiXacts / past the prehistory cap) skips the seal with a WARNING instead of FATALing the checkpointer: the refusal still lands fail-closed on every joiner (53RB5, unsealed) but the seed can shut down cleanly instead of wedging in a FATAL loop. Review P3-4: roll_blob_to_bak PANICs on a read() error instead of treating it as EOF (a truncated .bak was already rejected downstream by CRC, but silently swallowing I/O errors broke the module's discipline). Review P3-3: unit multi-segment prehistory round trip (33 pages across SLRU segments 0000/0001, per-page byte pattern, short-clone extension). Spec: spec-6.15b-xid-authority-native-era.md
… hardening F1: begin_native_run/mark_cluster_era install the transitioned image as BOTH primary and .bak (write_header_both) -- rolling the pre-transition image into .bak let a later single-copy corruption fall back to a stale SEALED (or pre-CLUSTER_ERA) authority, handing joiners the previous pass's high-water or re-opening era re-entry. F2: divergent-lineage guard -- before the adopt/skip decision the joiner byte-compares its local pg_xact prefix [0, min(own_next, native_hw)) against the sealed prehistory blob at 2-bit precision; any contradiction is FATAL (a same-sysid clone that ran standalone after cloning is not a pre-seed lineage; neither trusting nor overwriting its bits is sound). Bypassed only when local oldestXid has advanced past the native range. t/361: real scram TCP login leg (hba + listener), node0 shared-heap re-reads after joiner scans (poison proof on the heap, not just CLOG), a 53RB5 SQLSTATE pin (%e before %q -- %q stops expansion for the postmaster), the N5 divergent-lineage negative, and the L9 shared_catalog=off dormant leg. Also: on-disk header layouts locked with StaticAssertDecl; consumers LOG when the authority is served from the .bak fallback; user manual trimmed to user-visible effect. Spec: spec-6.15b-xid-authority-native-era.md
A crash between write_header_both's two durable renames leaves the transitioned flag (unseal / CLUSTER_ERA stamp) in the primary copy only, with a stale pre-transition .bak behind it. Nothing on the restart path rewrote the pair -- the bootstrap gates skipped the transition call when the primary already showed the new flag, and the transition functions early-returned on the same observation -- so the stale .bak survived the whole native run. Any later transient primary read failure then fell back to it: a stale SEALED .bak hands joiners the previous pass's high-water (false-invisible + xid reissue), and a stale pre-CLUSTER_ERA .bak re-opens the native-era re-entry guard. Make the two flag transitions idempotent re-asserts: the bootstrap calls them unconditionally on every boot of their respective arm, and they rewrite BOTH copies until both validate with the post-transition flags (a missing or invalid copy counts as unsettled and is restored too). Once both copies are settled the call is a no-write no-op, so the steady-state boot cost is two header reads. All fail-closed semantics (53RB5 refusals, CLUSTER_ERA re-entry FATAL, corrupt-authority PANIC) are unchanged. Red-first unit coverage: torn-window tests for both transitions (complete the transition, re-install the old image as .bak, re-run the boot path, assert the .bak is repaired and that a primary read failure falls back to the repaired image, not the stale one). Spec: spec-6.15b-xid-authority-native-era.md
…osed) The divergent-lineage prefix check broke out of its comparison loop and returned CONSISTENT on the FIRST missing local pg_xact page. That is only sound for a limit that ends where the local tree ends; pg_xact front truncation (SimpleLruTruncate removes whole low segments once every xid they cover is frozen past oldestXid) means a joiner can have segment 0000 legitimately absent while later segments are present AND divergent -- the first-page ENOENT then declared the clone CONSISTENT without ever comparing the surviving pages, and the adopt arm overwrote the joiner's own live-xid outcome pages with the seed's bits (false-visible / false-invisible). Pass the caller's recovery-anchor oldestXid into the check and start the comparison at oldestXid's own 2-bit slot: bits below it are frozen truth that CLOG never consults again and whose on-disk survival is an accident of truncation timing, so they are exempt whether the pages survive or not (neither truncation nor tuple freezing rewrites surviving CLOG bytes, so this can never hide live evidence). Within the comparable range [oldestXid, min(own_next, native_hw)) a missing local page is an anomaly -- pg_xact always covers [oldestXid, nextXid) on a well-formed node -- and now returns UNAVAILABLE (fail-closed 53RB5 at the caller), never CONSISTENT. Sub-byte boundaries at oldestXid are masked at 2-bit precision. Red-first unit coverage: front-truncated divergent clone must DIVERGE (returned CONSISTENT pre-fix), divergence strictly below oldestXid stays CONSISTENT (frozen exemption, mid-byte lead mask), oldestXid at the divergent slot DIVERGES, and a hole inside the comparable range is UNAVAILABLE (the old truth-table leg expecting CONSISTENT for a missing segment encoded exactly the blind spot and now expects UNAVAILABLE). Spec: spec-6.15b-xid-authority-native-era.md
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.
What
Under
cluster.shared_catalog, xids consumed by the seed node's pre-formation (cluster.enabled=off) load were invisible to every other node: a joiner's transaction horizon stayed at its pre-seed clone point, so seed rows were silently judged "in the future" (catalog rows, roles and tables missing on joiners;txid_status()erroring; role login failing), and any unhinted seed tuple risked a wrong local-CLOG verdict being hint-stamped onto shared pages.This adds a durable shared XID authority + native-era pg_xact prehistory under
cluster.shared_data_dir:global/pgrac_xid_authority: never-lowered native-era xid high-water with SEALED / CLUSTER_ERA lifecycle flags (torn-safe write, .bak fallback, fail-closed read).global/pgrac_xid_prehistory: CRC-guarded raw pg_xact page run[0, native_hw), published at the seed's clean shutdown checkpoint, adopted into a pre-seed-lineage joiner's local pg_xact (proven via its per-node recovery anchor) beforeStartupCLOG(), fsynced.shared_catalognow requirescluster.xid_striping=on(startup vet, new SQLSTATE 53RB5 family).Tests
test_cluster_xid_authority(layout lock, classify truth table, seal/flag monotonicity, unseal, prehistory publish/adopt byte round trip).t/361(new, in nightly stage6-crossnode-b): green path (pre-seed clone adopts prehistory, seed rows/roles visible,txid_statusconsistent, login works) + negatives (striping-off vet, era re-entry, corrupt authority, unsealed crash-seed, crashed multi-pass).t/337/t/339/t/346flipped toonline_join+xid_stripingposture; first-contact probes retry the documented "retry is safe" join-gate window.Spec: spec-6.15b-xid-authority-native-era.md