Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions src/backend/cluster/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ OBJS = \
cluster_visibility_resolve.o \
cluster_visibility_verdict.o \
cluster_undo_record.o \
cluster_undo_resid.o \
cluster_undo_gcs.o \
cluster_undo_gcs_grant.o \
cluster_undo_retention.o \
cluster_undo_srf.o \
cluster_cr.o \
Expand Down
3 changes: 2 additions & 1 deletion src/backend/cluster/cluster_cr_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ lms_undo_fetch_serve(ClusterLmsCrSlot *slot)

/* Serve only SELF-owned undo: the owner derives from this node's own
* id, never from the wire (a forged request cannot redirect the read). */
return cluster_undo_smgr_read_block(slot->undo_segment_id, (uint8)(cluster_node_id + 1),
return cluster_undo_smgr_read_block(cluster_undo_intent_for_owner((uint8)(cluster_node_id + 1)),
slot->undo_segment_id, (uint8)(cluster_node_id + 1),
slot->undo_block_no, slot->result_page);
}

Expand Down
19 changes: 19 additions & 0 deletions src/backend/cluster/cluster_grd.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "cluster/cluster_epoch.h" /* spec-4.6 D1 — accepted epoch reads */
#include "cluster/cluster_reconfig.h" /* spec-4.6 D1 — reconfig event consume */
#include "cluster/cluster_thread_recovery.h" /* spec-4.11 D3 — unfreeze gate */
#include "cluster/cluster_undo_resid.h" /* spec-5.22a D1-5 — undo-class hash-route guard */
#include "storage/procsignal.h" /* spec-4.6 D3 — redeclare broadcast */
#include "storage/sinvaladt.h" /* spec-4.6 D3 — BackendIdGetProc */
#include "utils/timestamp.h" /* spec-4.6 D1 — barrier deadline */
Expand Down Expand Up @@ -993,6 +994,24 @@ cluster_grd_lookup_master(const ClusterResId *resid)
uint32 shard_id;
int32 master;

/*
* PGRAC: spec-5.22a D1-5 -- undo resids are owner-as-master resources;
* their master is the encoded owner (cluster_undo_resid_master), never a
* shard-hash node. A hash-derived master would place the undo authority
* at a node that does not own the undo, so no caller may hash-route the
* undo class. Fail closed: no data-plane path legitimately reaches here
* with an undo resid.
*/
if (resid != NULL && resid->type == CLUSTER_UNDO_RESID_TYPE) {
Assert(false);
ereport(
ERROR,
(errcode(ERRCODE_CLUSTER_UNDO_RESID_HASH_ROUTED),
errmsg("undo resource id must not be routed through the GRD hash master lookup"),
errhint(
"Undo resources are owner-as-master; route via cluster_undo_resid_master().")));
}

Assert(cluster_grd_state != NULL);

shard_id = cluster_grd_shard_for_resource(resid);
Expand Down
52 changes: 52 additions & 0 deletions src/backend/cluster/cluster_guc.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ bool cluster_past_image = false;
/* spec-6.12i: active-runtime cross-instance recycled-slot visibility
* resolution via undo-block CF fetch (default OFF = 53R97). */
bool cluster_crossnode_runtime_visibility = false;
/* spec-5.22b D2-2: shared-undo GCS coherence master switch (default OFF =
* undo stays on the local DataDir, inert -- 6.12i unmastered fetch path; ON =
* own-instance runtime AND redo undo migrate to the shared cluster_fs root
* under owner-as-master GCS grant/PI). PGC_SIGHUP. */
bool cluster_undo_gcs_coherence = false;
/* spec-6.15 D1: xid space segmentation -- striped allocation (default
* OFF = vanilla dense per-node xid allocation). */
bool cluster_xid_striping = false;
Expand Down Expand Up @@ -985,6 +990,31 @@ check_cluster_shared_data_dir(char **newval, void **extra, GucSource source)
return true;
}

/*
* check_cluster_undo_gcs_coherence -- GUC check_hook for
* cluster.undo_gcs_coherence (spec-5.22b D2-2).
*
* Turning the shared-undo data plane ON physically migrates own-instance
* runtime + redo undo onto the shared cluster_fs root, so it REQUIRES
* cluster.shared_data_dir to name that root. Reject the ON transition when
* the root is unset: a clear config-time error beats a runtime read that has
* to fail closed with a generic SQLSTATE. (The runtime path additionally
* fails closed -- an unresolvable shared path yields the 53R97 visibility
* fail-close, never a bogus local fallback -- so this hook is operability,
* not the correctness backstop.)
*/
static bool
check_cluster_undo_gcs_coherence(bool *newval, void **extra, GucSource source)
{
if (*newval && (cluster_shared_data_dir == NULL || cluster_shared_data_dir[0] == '\0')) {
GUC_check_errdetail(
"cluster.undo_gcs_coherence requires cluster.shared_data_dir to name the shared "
"cluster_fs mount (the same root used by shared_catalog and the recovery anchor).");
return false;
}
return true;
}

static bool
check_cluster_block_device_path(char **newval, void **extra, GucSource source)
{
Expand Down Expand Up @@ -1588,6 +1618,28 @@ cluster_init_guc(void)
"(SQLSTATE 53R97)."),
&cluster_crossnode_runtime_visibility, false, PGC_SUSET, 0, NULL, NULL, NULL);

/*
* cluster.undo_gcs_coherence -- spec-5.22b D2 master switch for the
* shared-undo block data plane. OFF (default): undo segments stay on the
* local DataDir and cross-instance undo reads take the 6.12i unmastered
* fetch path (fresh ref => 53R97) -- byte-identical to pre-D2, a safe
* rollback surface. ON: own-instance runtime AND redo undo writes migrate
* to the shared cluster_fs root (cluster.shared_data_dir), and undo blocks
* become owner-as-master GCS resources (grant / PI land in D2-3/D2-4).
* Gating physical migration on this switch (not merely peer-mode) keeps
* runtime and redo writes consistent -- both move only when it is on, so a
* default deployment never splits runtime-shared vs redo-local
* (Hardening v1.0.1 裁决 A). PGC_SIGHUP: flippable for the D6 end-to-end
* validation window; a default-ON flip is a separate decision after D3-D6.
*/
DefineCustomBoolVariable(
"cluster.undo_gcs_coherence",
gettext_noop("Enable the shared-undo block GCS data plane (spec-5.22b)."),
gettext_noop("Off keeps undo on the local data dir and cross-instance undo "
"fail-closed (SQLSTATE 53R97). Requires cluster.shared_data_dir when on."),
&cluster_undo_gcs_coherence, false, PGC_SIGHUP, 0, check_cluster_undo_gcs_coherence, NULL,
NULL);

/*
* cluster.xid_striping -- spec-6.15 D1 (AD-012 exception 10 xid
* space segmentation). When on, this node only issues 32-bit xids
Expand Down
38 changes: 22 additions & 16 deletions src/backend/cluster/cluster_tt_durable.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ tt_slot_write_committed(uint32 segment_id, uint8 owner, uint16 slot_offset, Tran

cluster_tt_durable_io_wait_start();

if (!cluster_undo_smgr_read_header_bytes(segment_id, owner, off, (char *)&slot, sizeof(slot))) {
if (!cluster_undo_smgr_read_header_bytes(cluster_undo_intent_for_owner(owner), segment_id,
owner, off, (char *)&slot, sizeof(slot))) {
cluster_tt_durable_io_wait_end();
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("cluster durable TT: cannot read slot %u of undo segment %u",
Expand All @@ -196,8 +197,8 @@ tt_slot_write_committed(uint32 segment_id, uint8 owner, uint16 slot_offset, Tran
slot.commit_scn = commit_scn;
slot.first_undo_block = InvalidUbaVal; /* spec-4.8 D7-A (P1#1): no stale head */

if (!cluster_undo_smgr_write_header_bytes(segment_id, owner, off, (const char *)&slot,
sizeof(slot))) {
if (!cluster_undo_smgr_write_header_bytes(cluster_undo_intent_for_owner(owner), segment_id,
owner, off, (const char *)&slot, sizeof(slot))) {
cluster_tt_durable_io_wait_end();
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("cluster durable TT: cannot write slot %u of undo segment %u",
Expand Down Expand Up @@ -283,7 +284,8 @@ cluster_tt_slot_durable_abort(uint32 segment_id, uint16 slot_offset, Transaction

cluster_tt_durable_io_wait_start();

if (!cluster_undo_smgr_read_header_bytes(segment_id, owner, off, (char *)&slot, sizeof(slot))) {
if (!cluster_undo_smgr_read_header_bytes(cluster_undo_intent_for_owner(owner), segment_id,
owner, off, (char *)&slot, sizeof(slot))) {
cluster_tt_durable_io_wait_end();
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("cluster durable TT: cannot read slot %u of undo segment %u",
Expand All @@ -296,8 +298,8 @@ cluster_tt_slot_durable_abort(uint32 segment_id, uint16 slot_offset, Transaction
slot.commit_scn = InvalidScn;
slot.first_undo_block = InvalidUbaVal; /* spec-4.8 D7-A (P1#1): cleared; 0x90 re-attaches */

if (!cluster_undo_smgr_write_header_bytes(segment_id, owner, off, (const char *)&slot,
sizeof(slot))) {
if (!cluster_undo_smgr_write_header_bytes(cluster_undo_intent_for_owner(owner), segment_id,
owner, off, (const char *)&slot, sizeof(slot))) {
cluster_tt_durable_io_wait_end();
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("cluster durable TT: cannot write slot %u of undo segment %u",
Expand Down Expand Up @@ -336,7 +338,8 @@ cluster_tt_slot_durable_set_head(uint32 segment_id, uint16 slot_offset, Transact

cluster_tt_durable_io_wait_start();

if (!cluster_undo_smgr_read_header_bytes(segment_id, owner, off, (char *)&slot, sizeof(slot))) {
if (!cluster_undo_smgr_read_header_bytes(cluster_undo_intent_for_owner(owner), segment_id,
owner, off, (char *)&slot, sizeof(slot))) {
cluster_tt_durable_io_wait_end();
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("cluster durable TT: cannot read slot %u of undo segment %u",
Expand All @@ -347,8 +350,8 @@ cluster_tt_slot_durable_set_head(uint32 segment_id, uint16 slot_offset, Transact
* (xid, wrap); a recycled slot belongs to a newer owner -> leave untouched. */
if (slot.xid == xid && slot.wrap == wrap) {
slot.first_undo_block = first_undo_block;
if (!cluster_undo_smgr_write_header_bytes(segment_id, owner, off, (const char *)&slot,
sizeof(slot))) {
if (!cluster_undo_smgr_write_header_bytes(cluster_undo_intent_for_owner(owner), segment_id,
owner, off, (const char *)&slot, sizeof(slot))) {
cluster_tt_durable_io_wait_end();
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("cluster durable TT: cannot write slot %u of undo segment %u",
Expand All @@ -375,7 +378,8 @@ cluster_tt_slot_durable_lookup(uint32 segment_id, uint16 slot_offset, Transactio
off = tt_slot_file_offset(slot_offset);

cluster_tt_durable_io_wait_start();
if (!cluster_undo_smgr_read_header_bytes(segment_id, owner, off, (char *)&slot, sizeof(slot))) {
if (!cluster_undo_smgr_read_header_bytes(cluster_undo_intent_for_owner(owner), segment_id,
owner, off, (char *)&slot, sizeof(slot))) {
cluster_tt_durable_io_wait_end();
cluster_tt_durable_count_lookup(false);
return false; /* segment absent / I/O -> miss (caller fail-closes) */
Expand Down Expand Up @@ -416,8 +420,8 @@ cluster_tt_slot_durable_lookup_committed_stable(uint32 segment_id, uint16 slot_o
off = tt_slot_file_offset(slot_offset);

cluster_tt_durable_io_wait_start();
if (!cluster_undo_smgr_read_header_bytes(segment_id, owner, off, (char *)&first,
sizeof(first))) {
if (!cluster_undo_smgr_read_header_bytes(cluster_undo_intent_for_owner(owner), segment_id,
owner, off, (char *)&first, sizeof(first))) {
cluster_tt_durable_io_wait_end();
cluster_tt_durable_count_lookup(false);
return false;
Expand All @@ -436,8 +440,8 @@ cluster_tt_slot_durable_lookup_committed_stable(uint32 segment_id, uint16 slot_o
}

cluster_tt_durable_io_wait_start();
if (!cluster_undo_smgr_read_header_bytes(segment_id, owner, off, (char *)&second,
sizeof(second))) {
if (!cluster_undo_smgr_read_header_bytes(cluster_undo_intent_for_owner(owner), segment_id,
owner, off, (char *)&second, sizeof(second))) {
cluster_tt_durable_io_wait_end();
cluster_tt_durable_count_lookup(false);
return false;
Expand Down Expand Up @@ -665,7 +669,8 @@ cluster_tt_slot_durable_resolve_by_xid_origin(int origin_node, TransactionId xid
UndoSegmentHeaderData *hdr;
uint16 i;

if (!cluster_undo_smgr_read_block(segment_id, owner, 0, blockbuf.data)) {
if (!cluster_undo_smgr_read_block(cluster_undo_intent_for_owner(owner), segment_id, owner,
0, blockbuf.data)) {
/* absent segment -> sound skip; existing+unreadable -> incomplete. */
if (cluster_undo_segment_file_exists(owner, segment_id))
scan_complete = false;
Expand Down Expand Up @@ -763,7 +768,8 @@ cluster_undo_segment_tt_header_scan_pass(uint32 segment_id, uint8 owner_instance

/* Whole-block read mirrors the by-xid scan shape (one smgr surface). */
cluster_undo_cleaner_scan_wait_start();
if (!cluster_undo_smgr_read_block(segment_id, owner_instance, 0, block.data)) {
if (!cluster_undo_smgr_read_block(cluster_undo_intent_for_owner(owner_instance), segment_id,
owner_instance, 0, block.data)) {
cluster_undo_cleaner_scan_wait_end();
return false; /* absent / I/O: caller counts and moves on */
}
Expand Down
9 changes: 6 additions & 3 deletions src/backend/cluster/cluster_tt_recovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ cluster_tt_recovery_resolve_active_slots(void)
UndoSegmentHeaderData *hdr;
uint16 i;

if (!cluster_undo_smgr_read_block(segment_id, owner, 0, blockbuf.data))
if (!cluster_undo_smgr_read_block(cluster_undo_intent_for_owner(owner), segment_id, owner,
0, blockbuf.data))
continue; /* absent / unreadable -> skip (never false-abort) */

hdr = (UndoSegmentHeaderData *)blockbuf.data;
Expand Down Expand Up @@ -235,7 +236,8 @@ cluster_tt_recovery_observe_scn_highwater(void)
UndoSegmentHeaderData *hdr;
uint16 i;

if (!cluster_undo_smgr_read_block(segment_id, owner, 0, blockbuf.data))
if (!cluster_undo_smgr_read_block(cluster_undo_intent_for_owner(owner), segment_id, owner,
0, blockbuf.data))
continue; /* absent / unreadable -> skip */

hdr = (UndoSegmentHeaderData *)blockbuf.data;
Expand Down Expand Up @@ -483,7 +485,8 @@ cluster_tt_recovery_physical_rollback(void)
UndoSegmentHeaderData *hdr;
uint16 i;

if (!cluster_undo_smgr_read_block(segment_id, owner, 0, blockbuf.data))
if (!cluster_undo_smgr_read_block(cluster_undo_intent_for_owner(owner), segment_id, owner,
0, blockbuf.data))
continue; /* absent / unreadable -> skip */

hdr = (UndoSegmentHeaderData *)blockbuf.data;
Expand Down
Loading
Loading