Skip to content

Commit 4808601

Browse files
author
SqlRush
committed
perf(cluster): spec-5.19 MG-D — heap-ITL WAL delta v3 (drop always-Invalid commit_scn, 48->40B/record)
Closes the MG-B single-node write-tax blocker (rule 8.B). CI nightly measured the v2 (48B/record) tax at 10.62% > the 10% hard gate; an A/B that registered the delta 8B shorter passed the gate, confirming the 8B drop is sufficient. The write-time commit_scn (8B) is ALWAYS InvalidScn at every write-path ITL emit site: heap_insert / multi_insert / delete / lock / lock-chain / update old+new only ever stamp ITL_FLAG_ACTIVE / ITL_FLAG_LOCK_ONLY_ACTIVE transitions (the slot is not yet committed; COMMITTED stamping happens via the later commit-time / delayed-cleanout page mutation, FPI-logged, not via a write-path delta). Dropping an always-Invalid field is lossless. - heapam_xlog.h: new xl_heap_itl_delta_v3 (32B) + CLUSTER_ITL_DELTA_FORMAT_V3; keeps UBA (undo_segment_head moves 24->16), only commit_scn elided. StaticAsserts pin sizeof==32 and all field offsets. - cluster_itl.c: redo dispatches v1/v2/v3 by block format_version; v3 reconstructs commit_scn=InvalidScn. v1/v2 branches retained for backward WAL replay. consumed-bytes helper extended for v3. - heapam.c: all 8 write-path emit sites switch v2->v3 (drop the commit_scn assignment; register sizeof(v3)=32). - The COMMITTED-requires-valid-SCN redo guard still fires for any v3 delta that carries ITL_FLAG_COMMITTED -> fails closed (PANIC), so v3 can never silently install a committed slot with InvalidScn (8.A). - catversion 202606330 -> 202606340: fences an old binary from replaying v3-format WAL (unknown format_version -> redo PANIC). - Per mutating heap record: 8 + 40 == 48B -> 8 + 32 == 40B. - Tests: test_cluster_itl_wal v3 layout (T30-T33); D8 L6 invariant -> 40B (v2 40B retained for backward replay); t/329 MG-D model -> 40B + decision framing (v3 GO part shipped; same-block coalesce remains a follow-up).
1 parent b64e8c6 commit 4808601

7 files changed

Lines changed: 174 additions & 52 deletions

File tree

src/backend/access/heap/heapam.c

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,7 +2185,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
21852185
int bufflags = 0;
21862186
#ifdef USE_PGRAC_CLUSTER
21872187
xl_heap_itl_delta_block cluster_itl_hdr;
2188-
xl_heap_itl_delta_v2 cluster_itl_delta; /* spec-3.4b D6 F9 — v2 40B */
2188+
xl_heap_itl_delta_v3 cluster_itl_delta; /* spec-5.19 MG-D — v3 32B (commit_scn dropped) */
21892189
#endif
21902190

21912191
/*
@@ -2241,12 +2241,11 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
22412241
{
22422242
cluster_itl_hdr.ndeltas = 1;
22432243
cluster_itl_hdr.reserved = 0;
2244-
cluster_itl_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V2;
2244+
cluster_itl_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V3;
22452245
cluster_itl_delta.slot_idx = cluster_itl_slot;
22462246
cluster_itl_delta.flags_after = ITL_FLAG_ACTIVE;
22472247
cluster_itl_delta.xid = xid;
22482248
cluster_itl_delta.write_scn = ClusterPageGetItlSlots(BufferGetPage(buffer))[cluster_itl_slot].write_scn;
2249-
cluster_itl_delta.commit_scn = InvalidScn;
22502249
cluster_itl_delta.undo_segment_head = cluster_itl_uba;
22512250

22522251
xlrec.flags |= XLH_INSERT_ITL_DELTA;
@@ -2777,16 +2776,15 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
27772776
if (cluster_mi_active)
27782777
{
27792778
xl_heap_itl_delta_block mi_hdr;
2780-
xl_heap_itl_delta_v2 mi_delta; /* spec-3.4b D6 F9 — v2 40B */
2779+
xl_heap_itl_delta_v3 mi_delta; /* spec-5.19 MG-D — v3 32B (commit_scn dropped) */
27812780

27822781
mi_hdr.ndeltas = 1;
27832782
mi_hdr.reserved = 0;
2784-
mi_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V2;
2783+
mi_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V3;
27852784
mi_delta.slot_idx = cluster_mi_slot;
27862785
mi_delta.flags_after = ITL_FLAG_ACTIVE;
27872786
mi_delta.xid = xid;
27882787
mi_delta.write_scn = ClusterPageGetItlSlots(page)[cluster_mi_slot].write_scn;
2789-
mi_delta.commit_scn = InvalidScn;
27902788
mi_delta.undo_segment_head = cluster_mi_uba;
27912789

27922790
XLogRegisterData((char *) &mi_hdr,
@@ -3603,7 +3601,7 @@ heap_delete(Relation relation, ItemPointer tid,
36033601
XLogRecPtr recptr;
36043602
#ifdef USE_PGRAC_CLUSTER
36053603
xl_heap_itl_delta_block cluster_itl_hdr;
3606-
xl_heap_itl_delta_v2 cluster_itl_delta; /* spec-3.4b D6 F9 — v2 40B */
3604+
xl_heap_itl_delta_v3 cluster_itl_delta; /* spec-5.19 MG-D — v3 32B (commit_scn dropped) */
36073605
#endif
36083606

36093607
/*
@@ -3638,12 +3636,11 @@ heap_delete(Relation relation, ItemPointer tid,
36383636
{
36393637
cluster_itl_hdr.ndeltas = 1;
36403638
cluster_itl_hdr.reserved = 0;
3641-
cluster_itl_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V2;
3639+
cluster_itl_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V3;
36423640
cluster_itl_delta.slot_idx = cluster_itl_slot;
36433641
cluster_itl_delta.flags_after = ITL_FLAG_ACTIVE;
36443642
cluster_itl_delta.xid = xid;
36453643
cluster_itl_delta.write_scn = ClusterPageGetItlSlots(page)[cluster_itl_slot].write_scn;
3646-
cluster_itl_delta.commit_scn = InvalidScn;
36473644
cluster_itl_delta.undo_segment_head = cluster_itl_uba;
36483645
xlrec.flags |= XLH_DELETE_ITL_DELTA;
36493646
}
@@ -6464,7 +6461,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
64646461
XLogRecPtr recptr;
64656462
#ifdef USE_PGRAC_CLUSTER
64666463
xl_heap_itl_delta_block hdr;
6467-
xl_heap_itl_delta_v2 delta;
6464+
xl_heap_itl_delta_v3 delta;
64686465
#endif
64696466

64706467
XLogBeginInsert();
@@ -6485,15 +6482,15 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
64856482

64866483
#ifdef USE_PGRAC_CLUSTER
64876484
/*
6488-
* PGRAC (spec-3.4d D4 WAL emit / Q4 A2): append v2 40B ITL delta
6485+
* PGRAC (spec-3.4d D4 WAL emit / Q4 A2): append v3 32B ITL delta (spec-5.19 MG-D)
64896486
* + 4B block header inside same xlrec so heap_xlog_lock can replay
64906487
* the lock-only ITL slot stamp on standbys. Layout mirrors
64916488
* spec-3.4b D6 single-block delta WAL ABI.
64926489
*/
64936490
if (cluster_did_lock_stamp)
64946491
{
64956492
memset(&hdr, 0, sizeof(hdr));
6496-
hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V2;
6493+
hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V3;
64976494
hdr.ndeltas = 1;
64986495
hdr.reserved = 0;
64996496
XLogRegisterData((char *) &hdr, offsetof(xl_heap_itl_delta_block, deltas));
@@ -6503,7 +6500,6 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
65036500
delta.flags_after = ITL_FLAG_LOCK_ONLY_ACTIVE;
65046501
delta.xid = xid;
65056502
delta.write_scn = cluster_lock_write_scn;
6506-
delta.commit_scn = InvalidScn;
65076503
delta.undo_segment_head = cluster_lock_uba;
65086504
XLogRegisterData((char *) &delta, sizeof(delta));
65096505
}
@@ -7366,7 +7362,7 @@ heap_lock_updated_tuple_rec(Relation rel, TransactionId priorXmax,
73667362
Page page = BufferGetPage(buf);
73677363
#ifdef USE_PGRAC_CLUSTER
73687364
xl_heap_itl_delta_block chain_hdr;
7369-
xl_heap_itl_delta_v2 chain_delta;
7365+
xl_heap_itl_delta_v3 chain_delta;
73707366
#endif
73717367

73727368
XLogBeginInsert();
@@ -7388,7 +7384,7 @@ heap_lock_updated_tuple_rec(Relation rel, TransactionId priorXmax,
73887384
if (cluster_chain_lock_stamp)
73897385
{
73907386
memset(&chain_hdr, 0, sizeof(chain_hdr));
7391-
chain_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V2;
7387+
chain_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V3;
73927388
chain_hdr.ndeltas = 1;
73937389
chain_hdr.reserved = 0;
73947390
XLogRegisterData((char *) &chain_hdr,
@@ -7399,7 +7395,6 @@ heap_lock_updated_tuple_rec(Relation rel, TransactionId priorXmax,
73997395
chain_delta.flags_after = ITL_FLAG_LOCK_ONLY_ACTIVE;
74007396
chain_delta.xid = xid;
74017397
chain_delta.write_scn = cluster_chain_write_scn;
7402-
chain_delta.commit_scn = InvalidScn;
74037398
chain_delta.undo_segment_head = cluster_chain_uba;
74047399
XLogRegisterData((char *) &chain_delta, sizeof(chain_delta));
74057400
}
@@ -10590,9 +10585,9 @@ log_heap_update(Relation reln, Buffer oldbuf,
1059010585
int bufflags;
1059110586
#ifdef USE_PGRAC_CLUSTER
1059210587
xl_heap_itl_delta_block cluster_itl_old_hdr;
10593-
xl_heap_itl_delta_v2 cluster_itl_old_delta; /* spec-3.4b D6 F9 — v2 40B */
10588+
xl_heap_itl_delta_v3 cluster_itl_old_delta; /* spec-5.19 MG-D — v3 32B (commit_scn dropped) */
1059410589
xl_heap_itl_delta_block cluster_itl_new_hdr;
10595-
xl_heap_itl_delta_v2 cluster_itl_new_delta; /* spec-3.4b D6 F9 — v2 40B */
10590+
xl_heap_itl_delta_v3 cluster_itl_new_delta; /* spec-5.19 MG-D — v3 32B (commit_scn dropped) */
1059610591
#endif
1059710592

1059810593
/* Caller should not call me on a non-WAL-logged relation */
@@ -10695,24 +10690,22 @@ log_heap_update(Relation reln, Buffer oldbuf,
1069510690
{
1069610691
cluster_itl_new_hdr.ndeltas = 1;
1069710692
cluster_itl_new_hdr.reserved = 0;
10698-
cluster_itl_new_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V2;
10693+
cluster_itl_new_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V3;
1069910694
cluster_itl_new_delta.slot_idx = cluster_itl_new_slot;
1070010695
cluster_itl_new_delta.flags_after = ITL_FLAG_ACTIVE;
1070110696
cluster_itl_new_delta.xid = cluster_itl_xid;
1070210697
cluster_itl_new_delta.write_scn = ClusterPageGetItlSlots(BufferGetPage(newbuf))[cluster_itl_new_slot].write_scn;
10703-
cluster_itl_new_delta.commit_scn = InvalidScn;
1070410698
cluster_itl_new_delta.undo_segment_head = cluster_itl_uba;
1070510699
}
1070610700
if (cluster_itl_old_active && oldbuf != newbuf)
1070710701
{
1070810702
cluster_itl_old_hdr.ndeltas = 1;
1070910703
cluster_itl_old_hdr.reserved = 0;
10710-
cluster_itl_old_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V2;
10704+
cluster_itl_old_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V3;
1071110705
cluster_itl_old_delta.slot_idx = cluster_itl_old_slot;
1071210706
cluster_itl_old_delta.flags_after = ITL_FLAG_ACTIVE;
1071310707
cluster_itl_old_delta.xid = cluster_itl_xid;
1071410708
cluster_itl_old_delta.write_scn = ClusterPageGetItlSlots(BufferGetPage(oldbuf))[cluster_itl_old_slot].write_scn;
10715-
cluster_itl_old_delta.commit_scn = InvalidScn;
1071610709
cluster_itl_old_delta.undo_segment_head = cluster_itl_uba;
1071710710
}
1071810711
else if (cluster_itl_old_active)
@@ -10724,12 +10717,11 @@ log_heap_update(Relation reln, Buffer oldbuf,
1072410717
*/
1072510718
cluster_itl_new_hdr.ndeltas = 1;
1072610719
cluster_itl_new_hdr.reserved = 0;
10727-
cluster_itl_new_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V2;
10720+
cluster_itl_new_hdr.format_version = CLUSTER_ITL_DELTA_FORMAT_V3;
1072810721
cluster_itl_new_delta.slot_idx = cluster_itl_old_slot;
1072910722
cluster_itl_new_delta.flags_after = ITL_FLAG_ACTIVE;
1073010723
cluster_itl_new_delta.xid = cluster_itl_xid;
1073110724
cluster_itl_new_delta.write_scn = ClusterPageGetItlSlots(BufferGetPage(newbuf))[cluster_itl_old_slot].write_scn;
10732-
cluster_itl_new_delta.commit_scn = InvalidScn;
1073310725
cluster_itl_new_delta.undo_segment_head = cluster_itl_uba;
1073410726
}
1073510727
}
@@ -11524,7 +11516,7 @@ heap_xlog_delete(XLogReaderState *record)
1152411516
/*
1152511517
* PGRAC (spec-3.4a D9 / spec-3.4b D6): replay block-local ITL
1152611518
* delta when XLH_DELETE_ITL_DELTA is set. The helper dispatches
11527-
* by format_version (v1 24B legacy / v2 40B with UBA).
11519+
* by format_version (v1 24B legacy / v2 40B / v3 32B, all with UBA).
1152811520
*/
1152911521
if (xlrec->flags & XLH_DELETE_ITL_DELTA)
1153011522
{
@@ -11666,7 +11658,7 @@ heap_xlog_insert(XLogReaderState *record)
1166611658
/*
1166711659
* PGRAC (spec-3.4a D9 / spec-3.4b D6): replay block-local ITL
1166811660
* delta array when XLH_INSERT_ITL_DELTA is set. The helper
11669-
* dispatches by format_version (v1 24B legacy / v2 40B with UBA).
11661+
* dispatches by format_version (v1 24B legacy / v2 40B / v3 32B, all with UBA).
1167011662
*/
1167111663
if (xlrec->flags & XLH_INSERT_ITL_DELTA)
1167211664
{
@@ -12343,7 +12335,7 @@ heap_xlog_lock(XLogReaderState *record)
1234312335
#ifdef USE_PGRAC_CLUSTER
1234412336
/*
1234512337
* PGRAC (spec-3.4d D6 redo / Q4 A2): replay lock-only ITL slot
12346-
* stamp from v2 40B delta appended after xlrec. See spec-3.4b D6
12338+
* stamp from the dispatched ITL delta (v1 24B / v2 40B / v3 32B) appended after xlrec. See spec-3.4b D6
1234712339
* for delta block layout. htup tuple header has no
1234812340
* t_lock_itl_slot_idx field (per F2 raw_xmax scan derivation), so
1234912341
* we do not patch any tuple header field; the slot stamp itself
@@ -12419,7 +12411,7 @@ heap_xlog_lock_updated(XLogReaderState *record)
1241912411

1242012412
#ifdef USE_PGRAC_CLUSTER
1242112413
/* PGRAC (spec-3.4d D6 redo / follow_updates): replay lock-only
12422-
* ITL slot for successor tuple from v2 40B delta. */
12414+
* ITL slot for successor tuple from the dispatched ITL delta (v2 40B / v3 32B). */
1242312415
if (xlrec->flags & XLH_LOCK_UPDATED_ITL_DELTA)
1242412416
{
1242512417
const char *delta_start = ((const char *) xlrec) + SizeOfHeapLockUpdated;

src/backend/cluster/cluster_itl.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,8 @@ cluster_itl_redo_apply_block_local_delta(Page page, HeapTupleHeader htup,
968968
delta_size = sizeof(xl_heap_itl_delta);
969969
else if (hdr.format_version == CLUSTER_ITL_DELTA_FORMAT_V2)
970970
delta_size = sizeof(xl_heap_itl_delta_v2);
971+
else if (hdr.format_version == CLUSTER_ITL_DELTA_FORMAT_V3)
972+
delta_size = sizeof(xl_heap_itl_delta_v3);
971973
else
972974
elog(PANIC, "spec-3.4b D6: unknown xl_heap_itl_delta_block.format_version %u",
973975
(unsigned)hdr.format_version);
@@ -996,7 +998,7 @@ cluster_itl_redo_apply_block_local_delta(Page page, HeapTupleHeader htup,
996998
* slot's existing UBA on page is preserved. Legacy ACTIVE
997999
* stamps wrote InvalidUba to the page anyway, so reader
9981000
* 3-branch (D7) will fall back to zero triple. */
999-
} else {
1001+
} else if (hdr.format_version == CLUSTER_ITL_DELTA_FORMAT_V2) {
10001002
xl_heap_itl_delta_v2 d;
10011003

10021004
memcpy(&d, p, sizeof(d));
@@ -1006,6 +1008,24 @@ cluster_itl_redo_apply_block_local_delta(Page page, HeapTupleHeader htup,
10061008
d_write_scn = d.write_scn;
10071009
d_commit_scn = d.commit_scn;
10081010
d_uba = d.undo_segment_head;
1011+
} else {
1012+
/* CLUSTER_ITL_DELTA_FORMAT_V3 (delta_size dispatch above already
1013+
* PANICked on any other value). */
1014+
xl_heap_itl_delta_v3 d;
1015+
1016+
memcpy(&d, p, sizeof(d));
1017+
slot_idx = d.slot_idx;
1018+
flags_after = d.flags_after;
1019+
d_xid = d.xid;
1020+
d_write_scn = d.write_scn;
1021+
/* spec-5.19 MG-D: v3 elides the write-time commit_scn (it is
1022+
* always InvalidScn for the ACTIVE / LOCK_ONLY_ACTIVE transitions
1023+
* the write path emits). Reconstruct it as InvalidScn. If a v3
1024+
* delta ever carries ITL_FLAG_COMMITTED, the COMMITTED-requires-
1025+
* valid-SCN guard below fails closed (PANIC) -- v3 must never be
1026+
* used for a COMMITTED transition. */
1027+
d_commit_scn = InvalidScn;
1028+
d_uba = d.undo_segment_head;
10091029
}
10101030

10111031
if (flags_after == ITL_FLAG_COMMITTED && !SCN_VALID(d_commit_scn))
@@ -1085,6 +1105,8 @@ cluster_itl_wal_block_consumed_bytes(const char *itl_block_start)
10851105
delta_size = sizeof(xl_heap_itl_delta);
10861106
else if (hdr.format_version == CLUSTER_ITL_DELTA_FORMAT_V2)
10871107
delta_size = sizeof(xl_heap_itl_delta_v2);
1108+
else if (hdr.format_version == CLUSTER_ITL_DELTA_FORMAT_V3)
1109+
delta_size = sizeof(xl_heap_itl_delta_v3);
10881110
else
10891111
elog(PANIC, "spec-3.4b D6: unknown xl_heap_itl_delta_block.format_version %u",
10901112
(unsigned)hdr.format_version);

src/include/access/heapam_xlog.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,34 @@ StaticAssertDecl(offsetof(xl_heap_itl_delta_block, deltas) == 8,
545545
* falls back to zero triple → PG-native).
546546
* xl_heap_itl_delta_block.format_version == 1 → v2 (40B deltas;
547547
* UBA bytes restored from delta).
548+
* xl_heap_itl_delta_block.format_version == 2 → v3 (32B deltas;
549+
* UBA bytes restored from delta; commit_scn elided, see below).
548550
* Other values → PANIC (corruption).
549551
*
552+
* PGRAC (spec-5.19 MG-D): v3 ITL delta drops the write-time commit_scn
553+
* field (8B). Every write-path emit site (heap_insert / multi_insert /
554+
* delete / lock / lock-chain / update old+new) only ever stamps an
555+
* ITL_FLAG_ACTIVE / ITL_FLAG_LOCK_ONLY_ACTIVE transition, for which
556+
* commit_scn is *always* InvalidScn at write time (the slot is not yet
557+
* committed -- COMMITTED stamping happens later via the commit-time /
558+
* delayed-cleanout page mutation, which is FPI-logged, not via a write-path
559+
* delta). Dropping an always-Invalid field is therefore lossless: redo
560+
* reconstructs commit_scn = InvalidScn for every v3 delta. This shrinks
561+
* the per-mutating-record heap-ITL WAL footprint from 8 + 40 == 48 B to
562+
* 8 + 32 == 40 B (MG-D measure baseline). The COMMITTED-requires-valid-SCN
563+
* redo guard (heap_redo) still fires for any v3 delta that somehow carries
564+
* ITL_FLAG_COMMITTED, so a mis-emitted COMMITTED v3 delta fails closed
565+
* (PANIC) rather than installing a committed slot with InvalidScn.
566+
*
567+
* Wire-stable layout (cluster_unit test_cluster_itl_wal enforces):
568+
* xl_heap_itl_delta_v3 (32 bytes):
569+
* offset 0, 2B : slot_idx
570+
* offset 2, 2B : flags_after
571+
* offset 4, 4B : xid
572+
* offset 8, 8B : write_scn
573+
* offset 16, 16B : undo_segment_head (UBA; InvalidUba on finish
574+
* deltas that do not re-bind -- same semantic as v2)
575+
*
550576
* Wire-stable layout (cluster_unit test_cluster_itl_wal enforces):
551577
* xl_heap_itl_delta_v2 (40 bytes):
552578
* offset 0, 2B : slot_idx
@@ -567,6 +593,7 @@ StaticAssertDecl(offsetof(xl_heap_itl_delta_block, deltas) == 8,
567593
*/
568594
#define CLUSTER_ITL_DELTA_FORMAT_V1 ((uint32) 0)
569595
#define CLUSTER_ITL_DELTA_FORMAT_V2 ((uint32) 1)
596+
#define CLUSTER_ITL_DELTA_FORMAT_V3 ((uint32) 2)
570597

571598
typedef struct xl_heap_itl_delta_v2
572599
{
@@ -593,6 +620,28 @@ StaticAssertDecl(offsetof(xl_heap_itl_delta_v2, commit_scn) == 16,
593620
StaticAssertDecl(offsetof(xl_heap_itl_delta_v2, undo_segment_head) == 24,
594621
"spec-3.4b D6 — undo_segment_head at offset 24");
595622

623+
typedef struct xl_heap_itl_delta_v3
624+
{
625+
uint16 slot_idx; /* offset 0, 2B */
626+
uint16 flags_after; /* offset 2, 2B (ClusterItlFlags) */
627+
TransactionId xid; /* offset 4, 4B */
628+
SCN write_scn; /* offset 8, 8B */
629+
UBA undo_segment_head; /* offset 16, 16B (commit_scn elided) */
630+
} xl_heap_itl_delta_v3;
631+
632+
StaticAssertDecl(sizeof(xl_heap_itl_delta_v3) == 32,
633+
"spec-5.19 MG-D — xl_heap_itl_delta_v3 must be 32 bytes (v2 40B minus the always-Invalid 8B commit_scn)");
634+
StaticAssertDecl(offsetof(xl_heap_itl_delta_v3, slot_idx) == 0,
635+
"spec-5.19 MG-D — slot_idx at offset 0");
636+
StaticAssertDecl(offsetof(xl_heap_itl_delta_v3, flags_after) == 2,
637+
"spec-5.19 MG-D — flags_after at offset 2");
638+
StaticAssertDecl(offsetof(xl_heap_itl_delta_v3, xid) == 4,
639+
"spec-5.19 MG-D — xid at offset 4");
640+
StaticAssertDecl(offsetof(xl_heap_itl_delta_v3, write_scn) == 8,
641+
"spec-5.19 MG-D — write_scn at offset 8");
642+
StaticAssertDecl(offsetof(xl_heap_itl_delta_v3, undo_segment_head) == 16,
643+
"spec-5.19 MG-D — undo_segment_head at offset 16 (commit_scn dropped vs v2)");
644+
596645

597646
#endif /* USE_PGRAC_CLUSTER */
598647

src/include/catalog/catversion.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,14 @@
706706
/* spec-5.18: permanent node removal — pg_cluster_node_removal_state SRF (oid 8963)
707707
* + pg_cluster_remove_node UDF (oid 8964) + pg_cluster_membership +2 cols
708708
* (removed/removed_epoch) + 53R63/53R64 SQLSTATE. Bump 202606320 -> 202606330. */
709-
#define CATALOG_VERSION_NO 202606330
709+
/* spec-5.19 MG-D (2026-06-29): heap-ITL WAL delta v3 — new xl_heap_itl_delta_v3
710+
* (32B) + CLUSTER_ITL_DELTA_FORMAT_V3; the always-Invalid write-time commit_scn
711+
* (8B) is dropped from every write-path ITL delta, shrinking the per-mutating-
712+
* record footprint 8+40==48B -> 8+32==40B. Redo keeps v1/v2 branches for
713+
* backward replay and reconstructs commit_scn=InvalidScn for v3. No catalog
714+
* surface change; the bump fences an old binary from replaying v3-format WAL
715+
* (unknown format_version -> redo PANIC). Bump 202606330 -> 202606340. */
716+
#define CATALOG_VERSION_NO 202606340
710717

711718
/* spec-5.13 (2026-06-27): clean-leave catalog surface — cluster_get_clean_leave_state
712719
* SRF (oid 8960) + pg_cluster_clean_leave_state view + pg_cluster_clean_leave_request

0 commit comments

Comments
 (0)