Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
f28bd4e
perf(cluster): wake LMON on GES work-queue enqueue
Jul 7, 2026
ad764c5
perf(cluster): lower GCS retransmit initial backoff default 100ms -> …
Jul 7, 2026
dbd8280
perf(cluster): wake LMON when LMS publishes a READY serve slot
Jul 7, 2026
d68d03c
perf(cluster): on-demand gating for lazy-able LMON duty-chain families
Jul 7, 2026
127b6eb
feat(cluster): requester-side block-ship latency histogram (16 us buc…
Jul 7, 2026
9dcbc4f
fix(cluster): sync backoff C-var initializer with lowered boot_val
Jul 7, 2026
7b9b713
test(cluster): gcs dump-key count baselines 67 -> 83 (ship histogram …
Jul 7, 2026
7844181
feat(cluster): pgrac.conf per-node data_addr field (DATA-plane listener)
Jul 7, 2026
6d6370e
feat(cluster): tier1 per-plane substrate + HELLO plane/conn_epoch bytes
Jul 7, 2026
ca7341f
fix(cluster): DATA HELLO conn_epoch check is equality vs current epoch
Jul 7, 2026
391a40f
feat(cluster): LMS-owned DATA-plane interconnect loop
Jul 7, 2026
9ddb5ff
test(cluster): harnesses declare per-node data_addr
Jul 7, 2026
8722f79
feat(cluster): msg_type plane field + router plane gates
Jul 7, 2026
5af21a0
feat(cluster): route the X->S downgrade notify through the staging he…
Jul 7, 2026
fe2da00
feat(cluster): DATA-plane outbound ring + registry-probed twin plumbing
Jul 7, 2026
52e087f
fix(cluster): request the DATA-ring named LWLock tranche
Jul 7, 2026
08a8f28
feat(cluster): INV-7.2-CONN-EPOCH substrate + DATA-plane fence linkage
Jul 7, 2026
f494f50
feat(cluster): LMS data-plane wait events + injection points
Jul 7, 2026
d91cb91
test(cluster): sync injection-registry and wait-event pins to spec-7.…
Jul 7, 2026
81b61b9
fix(cluster): spawn LMS on hot-standby postmaster state
Jul 7, 2026
82598e0
fix(cluster): bind DATA conn_epoch to the local current epoch
Jul 7, 2026
0ff0f1c
fix(cluster): fence-gate only the image-bearing LMS data-plane legs
Jul 7, 2026
c18c1b6
feat(cluster): flip the GCS block family onto the LMS DATA plane
Jul 7, 2026
88ab007
test(cluster): sync stale count baselines to spec-7.2 D6 additions
Jul 7, 2026
38236ad
test(cluster): spec-7.2 D7 value gate + honest-SKIP fault legs (t/360)
Jul 7, 2026
590b3e1
fix(cluster): make qvotec markers async for lmon
Jul 8, 2026
3eeeea1
ci: include marker async TAP in nightly
Jul 8, 2026
c7a2c9f
test: stub assertions in marker async unit
Jul 8, 2026
8924f6e
fix(cluster): shared catalog native-era XID authority
Jul 8, 2026
adf62e0
fix(cluster): unseal XID authority when a follow-up native-era run boots
Jul 8, 2026
964e587
test(cluster): settle first-contact probes in t/337/t/339 for the joi…
Jul 8, 2026
a76d08c
fix(cluster): marker-async review r1 — PREPARE best-effort drain, CL-…
Jul 8, 2026
e71fc34
fix(cluster): review fixes — clean-shutdown-only seal, WARNING-skip o…
Jul 8, 2026
51cdc67
fix(cluster): dual-copy flag writes, divergent-lineage guard, TAP/doc…
Jul 8, 2026
0dca50f
fix(cluster): GRD recovery liveness — same-episode HW remaster retry,…
Jul 8, 2026
e7ca433
ci(nightly): own shard for the t/362 4-node kill self-heal TAP
Jul 8, 2026
8853bc7
test(cluster): suppress pre-existing cppcheck constParameter in pi_sh…
Jul 8, 2026
0204d22
fix(cluster): close two spec-2.29a nightly regressions — dump-categor…
Jul 8, 2026
87f7930
fix(cluster): clean-leave coherence excludes the leaving node (regres…
Jul 8, 2026
55dd3f3
fix(cluster): converge REDECLARE_DONE on (episode_epoch, dead_bitmap_…
Jul 8, 2026
ddefc00
fix(cluster): dead-master block serve proof is unconditional again
Jul 8, 2026
0d57fa9
fix(cluster): remaster retry ordering, runtime-structural warning, up…
Jul 8, 2026
1be30fe
fix(cluster): r2 P2-1 barrier-tick scalar conjunct + t/274 backend pr…
Jul 8, 2026
ee2c09f
test(cluster): pin evidence-over-inference own-commit latch (known-red)
Jul 8, 2026
1bd7629
fix(cluster): repair torn dual-copy xid-authority header on restart
Jul 9, 2026
d8b1d6d
fix(cluster): close prefix-check front-truncation blind spot (fail-cl…
Jul 9, 2026
ea2cdc9
fix(cluster): clean-leave own-commit latch consumes marker evidence, …
Jul 9, 2026
0155eb9
fix(cluster): re-consume same-epoch dead-set growth mid recovery episode
Jul 9, 2026
2a35cb2
fix(cluster): pair the HW remaster terminal-state fence with a read b…
Jul 9, 2026
bf11f6a
docs(cluster): ground the dead-master unseal predicate safety proof
Jul 9, 2026
5b8e8ef
test(cluster): lock the dead-master serve gate with a must-fail 53R9L…
Jul 9, 2026
d95ef45
chore(stage7-p0): reconfig liveness r3 (BUG-B/C2, PR#26 @ 5b8e8ef040)
Jul 9, 2026
732a9d9
chore(stage7-p0): fuse unit fixtures + dedup cluster_grd_recovery_eve…
Jul 9, 2026
e9969a6
chore(stage7-p0): shared-catalog native-era XID authority r3 (BUG-A1/…
Jul 9, 2026
bfe00bc
fix(ci): fuse nightly TAP shard matrix after 3-way merge (362 + 363 s…
Jul 9, 2026
ea777c7
chore(stage7): fuse spec-7.2 IC data-plane decoupling onto the stage7…
Jul 9, 2026
152b579
fix(cluster): feed the join fence from an unconditional done-epoch axis
Jul 9, 2026
589409c
test(cluster): refresh grd dump baselines surfaced by nightly
Jul 9, 2026
6e4ca73
fix(cluster): spec-7.2 D7 F6-1 -- one-shot conn-reset injection, un-S…
Jul 9, 2026
04ca161
test(cluster): configure wal_threads_root for t/353 reborn HW remaster
Jul 9, 2026
a3fe868
fix(cluster): spec-7.2 F6-1 review r2 -- convergence probe, conn-rese…
Jul 9, 2026
2d95cce
chore(stage7): fold reconfig-liveness tip 04ca16191e into 72-integration
Jul 9, 2026
b62331e
fix(cluster): add rule-11 Author/IDENTIFICATION banner to t/358
Jul 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,20 @@ jobs:
# the release-cut 4-node evidence is HG#2a-CI or HG#2a-EXT (external).
- { name: stage5-beta-chaos-soak, ranges: "344-345", unit: false, regress: false }
# spec-6.12/6.15 cross-node perf + correctness family (t/346-350:
# aged-seed verdict / ledger / 3-node nudge / PI rebuild + crash) and
# the renumbered t/351-356 family + t/357 multi-xmax alias floor.
# aged-seed verdict / ledger / 3-node nudge / PI rebuild + crash),
# the renumbered t/351-356 family, t/357 multi-xmax alias floor, and
# t/361 XID authority native-era adoption.
# Closes the 346+ shard hole (every new t/ file must land in a shard
# the same commit; see docs/code-review-checklist.md).
- { name: stage6-crossnode-a, ranges: "346-350", unit: false, regress: false }
- { name: stage6-crossnode-b, ranges: "351-357", unit: false, regress: false }
- { name: stage6-crossnode-b, ranges: "351-357 361", unit: false, regress: false }
# t/362 spec-4.6a 4-node shared_catalog kill self-heal (t/358-360
# reserved by the parallel spec-7.2 lane; L464 occupancy verified
# against origin branches 2026-07-08). Own shard: 4-node formation
# + kill + convergence needs the wall clock.
- { name: stage7-reconfig-liveness, ranges: "362-362", unit: false, regress: false }
# t/363 spec-2.29a marker-async LMON liveness.
- { name: stage7-marker-async, ranges: "363-363", unit: false, regress: false }
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
3 changes: 3 additions & 0 deletions docs/reference/system-views.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ must not be read as evidence that early transfer is active.
| `smart_fusion` | `origin_suspect_count` | Pending dependencies associated with an origin suspected dead or unavailable. |
| `smart_fusion` | `dep_lost_failclosed_count` | Missing or malformed dependency evidence rejected fail-closed. |
| `smart_fusion` | `retry_failclosed_count` | Retryable Smart Fusion fail-closed outcomes, primarily commit-brake timeout. |
| `catalog` | `xid_authority_native_hw` | Sealed native-era xid high-water for shared-catalog formation; `0` when the authority is inactive or unreadable. |
| `catalog` | `xid_authority_sealed` | `t` when the seed completed a clean native-era shutdown and published adoptable XID prehistory. |
| `catalog` | `xid_prehistory_adopted` | Process-local marker showing whether this postmaster boot adopted native-era pg_xact prehistory. |

Example:

Expand Down
47 changes: 47 additions & 0 deletions docs/user-guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,9 @@ Requirements and behaviour:
* `cluster.node_id` must be between 0 and 15; startup is refused
otherwise. Striped clusters are therefore limited to **16 nodes**.
* `cluster.online_join` must be `on`; startup is refused otherwise.
* `cluster.shared_catalog = on` with more than one declared node requires
`cluster.xid_striping = on`; startup fails closed with SQLSTATE `53RB5`
if a multi-node shared-catalog cluster is formed without striping.
* All nodes must run with the same `cluster.xid_striping` setting.
The cluster records its striping state durably on the voting disk;
a node whose configuration disagrees with that recorded state (or
Expand All @@ -791,6 +794,50 @@ cluster.xid_striping = on
cluster.online_join = on
```

### Shared-catalog native-era XID authority

A shared-catalog cluster that is formed from a single seed node has a
short native era: the seed runs with `cluster.enabled = off` while it
creates initial catalog rows, roles, schemas, and seed data. Those
transactions consume the seed node's local xids before other nodes have
joined. During the seed node's clean shutdown, pgrac publishes two
formation files under `cluster.shared_data_dir/global`:
`pgrac_xid_authority` (the sealed native xid high-water) and
`pgrac_xid_prehistory` (the seed node's pg_xact truth for those xids).

Provisioning sequence:

1. Create or clone joiner data directories before the seed load if they
must share the seed node's system identifier.
2. Start the seed with `cluster.shared_catalog = on`,
`cluster.controlfile_shared_authority = on`, `cluster.enabled = off`,
and a fixed `cluster.node_id`; perform the seed load.
3. Stop the seed cleanly. Do not use immediate shutdown for the final
native-era stop; joiners refuse an unsealed authority with SQLSTATE
`53RB5`.
4. Start all nodes with `cluster.enabled = on`, `cluster.online_join = on`,
and `cluster.xid_striping = on`. Joiners adopt the seed's transaction
history at startup, so seed tables, roles, and data are visible on
every node.

Do not delete or re-create the authority files to recover a failed join.
A missing, unsealed, or corrupt authority/prehistory is intentionally
fail-closed (`53RB5`); restore the shared tree files from backup or
re-provision the cluster. After the first `cluster.enabled = on` boot,
the native era is permanently closed and a later `cluster.enabled = off`
boot against the same shared tree is refused.

Observe formation state with:

```sql
SELECT key, value
FROM pg_cluster_state
WHERE category = 'catalog'
AND key IN ('xid_authority_native_hw',
'xid_authority_sealed',
'xid_prehistory_adopted');
```

### `cluster.xid_herding_slack`

| | |
Expand Down
90 changes: 90 additions & 0 deletions src/backend/access/transam/xlog.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
#include "cluster/cluster_guc.h" /* PGRAC: spec-5.6 cluster_controlfile_shared_authority */
#include "cluster/cluster_hw_snapshot.h" /* PGRAC: spec-5.7 D3 HW authority checkpoint snapshot */
#include "cluster/cluster_xid_stripe_xlog.h" /* PGRAC: spec-6.15 D5d checkpoint re-emit */
#include "cluster/cluster_xid_authority.h" /* PGRAC: spec-6.15b native-era XID authority */
#include "cluster/cluster_recovery_anchor.h" /* PGRAC: spec-5.6a per-node recovery anchor */
#include "cluster/cluster_lms.h" /* PGRAC: spec-5.6 GES-ready boundary for CF X */
#endif
Expand Down Expand Up @@ -5593,6 +5594,42 @@ StartupXLOG(void)
*/
if (cluster_recovery_anchor_active() && !InRecovery)
checkPoint = cluster_recovery_anchor_get()->checkPointCopy;

/*
* PGRAC (spec-6.15b D5): shared_catalog nodes must reconcile their
* transaction horizon with the sealed native-era XID authority before
* ShmemVariableCache is seeded and before StartupCLOG computes its latest
* page. This deliberately consumes a dedicated never-lowered authority,
* not the shared pg_control CheckPoint copy (spec-5.6a keeps that
* per-writer).
*/
if (cluster_shared_catalog && cluster_enabled)
{
ClusterXidAuthorityHeader auth;
bool auth_from_bak = false;
bool auth_ok;

auth_ok = cluster_xid_authority_read_checked(&auth, &auth_from_bak);
if (auth_ok && auth_from_bak)
elog(LOG, "cluster shared_catalog: XID authority served from .bak fallback; "
"the primary copy failed validation");

if (!auth_ok ||
(auth.flags & CLUSTER_XID_AUTHORITY_FLAG_SEALED) == 0)
ereport(FATAL, (errcode(ERRCODE_CLUSTER_XID_AUTHORITY_UNAVAILABLE),
errmsg("shared XID authority is unavailable during WAL startup"),
errdetail("StartupXLOG cannot seed nextXid without a sealed native-era high-water."),
errhint("Complete a clean native-era seed shutdown before starting shared_catalog nodes.")));

if (U64FromFullTransactionId(checkPoint.nextXid) < auth.native_hw_full)
{
uint64 own_next = U64FromFullTransactionId(checkPoint.nextXid);

checkPoint.nextXid = FullTransactionIdFromU64(auth.native_hw_full);
elog(LOG, "cluster shared_catalog: merged nextXid with XID authority native high-water %llu (own was %llu)",
(unsigned long long)auth.native_hw_full, (unsigned long long)own_next);
}
}
#endif

/* initialize shared memory variables from the checkpoint record */
Expand Down Expand Up @@ -7549,6 +7586,59 @@ CreateCheckPoint(int flags)
*/
END_CRIT_SECTION();

#ifdef USE_PGRAC_CLUSTER
/*
* PGRAC (spec-6.15b D3): after the shutdown checkpoint record,
* control-file update, CLOG flush, and per-node recovery anchor publish
* are complete, publish the native-era pg_xact prehistory and seal the
* shared XID authority. This does durable file I/O and may fail closed,
* so it must stay outside the checkpoint critical section.
*
* Two deliberate exclusions (review P2-1):
*
* - END_OF_RECOVERY checkpoints carry `shutdown` but are NOT a clean
* close of the native run: the seed keeps consuming xids afterwards,
* so sealing here would let a joiner adopt a mid-run stale high-water
* (the same 8.A window the begin_native_run unseal closes for the
* multi-pass case). Only a true shutdown checkpoint seals.
*
* - An unsupported native era (MultiXacts created, or past the
* prehistory cap) SKIPS the seal with a WARNING instead of FATALing:
* the refusal still lands fail-closed on every joiner (53RB5,
* authority unsealed), but the seed itself can shut down cleanly
* instead of wedging every later shutdown/boot in a FATAL loop.
*/
if (shutdown && !(flags & CHECKPOINT_END_OF_RECOVERY)
&& cluster_shared_catalog && !cluster_enabled)
{
uint64 native_hw = U64FromFullTransactionId(checkPoint.nextXid);

if (checkPoint.nextMulti > FirstMultiXactId)
ereport(WARNING,
(errmsg("native-era seed loads that create MultiXacts are not supported; "
"leaving the shared XID authority unsealed"),
errdetail("Shutdown checkpoint nextMulti is %u, but only %u is supported.",
checkPoint.nextMulti, FirstMultiXactId),
errhint("Joiners will fail closed (53RB5); recreate the seed without "
"MultiXact-producing operations, or move the load in-protocol.")));
else if (cluster_xid_prehistory_payload_bytes(native_hw) == 0)
ereport(WARNING,
(errmsg("native-era xid high-water %llu is outside the prehistory publish "
"range; leaving the shared XID authority unsealed",
(unsigned long long) native_hw),
errhint("Joiners will fail closed (53RB5); split the seed load or move "
"it in-protocol.")));
else
{
cluster_xid_prehistory_publish(DataDir, native_hw);
cluster_xid_authority_publish_native(native_hw, checkPoint.nextMulti, true);
elog(LOG, "cluster shared_catalog: published and sealed native-era XID authority high-water %llu",
(unsigned long long) native_hw);
}
}

#endif

/*
* Let smgr do post-checkpoint cleanup (eg, deleting old files).
*/
Expand Down
10 changes: 9 additions & 1 deletion src/backend/access/transam/xlogrecovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -3273,8 +3273,16 @@ ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *repl

/*
* ShmemVariableCache->nextXid must be beyond record's xid.
*
* PGRAC: a merged-recovery FOREIGN record belongs to a peer's xid
* authority, not this node's allocator. Advancing the local nextXid here
* would make StartupCLOG/TrimCLOG expect local pg_xact pages for peer
* striped xids, while the peer outcome truth lives in pg_xact_remote.
*/
AdvanceNextFullTransactionIdPastXid(record->xl_xid);
#ifdef USE_PGRAC_CLUSTER
if (!(cluster_recmerge_window_active && cluster_recmerge_apply_foreign))
#endif
AdvanceNextFullTransactionIdPastXid(record->xl_xid);

/*
* Before replaying this record, check if this record causes the current
Expand Down
4 changes: 4 additions & 0 deletions src/backend/cluster/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ OBJS = \
cluster_lmd_wait_state.o \
cluster_lmon.o \
cluster_lms.o \
cluster_lms_data_plane.o \
cluster_lms_outbound.o \
cluster_lns.o \
cluster_membership.o \
cluster_mrp.o \
Expand Down Expand Up @@ -220,6 +222,8 @@ OBJS = \
cluster_views.o \
cluster_wal_state.o \
cluster_wal_thread.o \
cluster_xid_authority.o \
cluster_xid_prehistory.o \
cluster_xid_stripe.o \
cluster_xid_stripe_boot.o \
cluster_xid_stripe_xlog.o \
Expand Down
26 changes: 26 additions & 0 deletions src/backend/cluster/cluster_backup.c
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,7 @@ cluster_backup_coord_request(ClusterBackupWireOp op, const char *backup_id,
}
cluster_backup_state->coordinator_send_pending = true;
LWLockRelease(&cluster_backup_state->lock.lock);
cluster_lmon_duty_mark_dirty(CLUSTER_LMON_DUTY_BACKUP); /* spec-7.2 D1 */
cluster_lmon_wakeup();

started_at = GetCurrentTimestamp();
Expand Down Expand Up @@ -1278,6 +1279,7 @@ cluster_backup_lmon_queue_peer_reply(const ClusterBackupWireAck *reply, int32 de
cluster_backup_state->peer_reply_dest = dest;
cluster_backup_state->peer_reply_pending = true;
LWLockRelease(&cluster_backup_state->lock.lock);
cluster_lmon_duty_mark_dirty(CLUSTER_LMON_DUTY_BACKUP); /* spec-7.2 D1 */
}

static bool
Expand All @@ -1301,6 +1303,7 @@ cluster_backup_lmon_start_restore_point_prepare(const ClusterBackupWireRequest *
cluster_backup_lmon_prepare_started_at = cluster_backup_restore_point_fence_start();
cluster_backup_lmon_restore_point_prepare_pending = true;
cluster_backup_lmon_set_prepare_pending(true);
cluster_lmon_duty_mark_dirty(CLUSTER_LMON_DUTY_BACKUP); /* spec-7.2 D1 */
return false;
}

Expand Down Expand Up @@ -1712,6 +1715,7 @@ cluster_backup_request_handler(const ClusterICEnvelope *env, const void *payload
cluster_backup_state->peer_command_pending = true;
}
LWLockRelease(&cluster_backup_state->lock.lock);
cluster_lmon_duty_mark_dirty(CLUSTER_LMON_DUTY_BACKUP); /* spec-7.2 D1 */
cluster_lmon_wakeup();
}

Expand Down Expand Up @@ -1896,6 +1900,8 @@ cluster_backup_lmon_process_peer_command(void)
void
cluster_backup_lmon_tick(void)
{
bool in_flight;

if (cluster_backup_state == NULL || !cluster_enabled)
return;

Expand All @@ -1906,6 +1912,26 @@ cluster_backup_lmon_tick(void)
cluster_backup_lmon_advance_restore_point_prepare();
cluster_backup_lmon_send_peer_reply();
cluster_backup_maybe_auto_restore_point();

/*
* PGRAC: spec-7.2 D1 -- the backup protocol is multi-phase and its
* advance steps are time-driven (fence quiesce), not producer-driven.
* While any operation is in flight, self-re-mark the duty so the
* lazy gate keeps the legacy every-iteration cadence until the
* protocol settles; steady state (no backup) reaps the lazy skip.
*/
if (cluster_backup_lmon_restore_point_prepare_pending)
in_flight = true;
else {
LWLockAcquire(&cluster_backup_state->lock.lock, LW_SHARED);
in_flight = cluster_backup_state->peer_reply_pending
|| cluster_backup_state->coordinator_send_pending
|| cluster_backup_state->peer_command_pending
|| cluster_backup_state->peer_restore_point_prepare_pending;
LWLockRelease(&cluster_backup_state->lock.lock);
}
if (in_flight)
cluster_lmon_duty_mark_dirty(CLUSTER_LMON_DUTY_BACKUP);
}

static void
Expand Down
Loading
Loading