Skip to content

Commit 79d4bf4

Browse files
Delay commit status checks until freezing executes.
pg_xact lookups are relatively expensive. Move the xmin/xmax commit status checks from the point that freeze plans are prepared to the point that they're actually executed. Otherwise we'll repeat many commit status checks whenever multiple successive VACUUM operations scan the same pages and decide against freezing each time, which is a waste of cycles. Oversight in commit 1de58df, which added page-level freezing. Author: Peter Geoghegan <pg@bowt.ie> Discussion: https://postgr.es/m/CAH2-WzkZpe4K6qMfEt8H4qYJCKc2R7TPvKsBva7jc9w7iGXQSw@mail.gmail.com
1 parent b37a083 commit 79d4bf4

File tree

2 files changed

+71
-27
lines changed

2 files changed

+71
-27
lines changed

src/backend/access/heap/heapam.c

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6208,10 +6208,10 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
62086208
* been pruned away instead, since updater XID is < OldestXmin).
62096209
* Just remove xmax.
62106210
*/
6211-
if (TransactionIdDidCommit(update_xact))
6211+
if (!TransactionIdDidAbort(update_xact))
62126212
ereport(ERROR,
62136213
(errcode(ERRCODE_DATA_CORRUPTED),
6214-
errmsg_internal("multixact %u contains committed update XID %u from before removable cutoff %u",
6214+
errmsg_internal("multixact %u contains non-aborted update XID %u from before removable cutoff %u",
62156215
multi, update_xact,
62166216
cutoffs->OldestXmin)));
62176217
*flags |= FRM_INVALIDATE_XMAX;
@@ -6500,10 +6500,11 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
65006500
freeze_xmax = false;
65016501
TransactionId xid;
65026502

6503-
frz->frzflags = 0;
6503+
frz->xmax = HeapTupleHeaderGetRawXmax(tuple);
65046504
frz->t_infomask2 = tuple->t_infomask2;
65056505
frz->t_infomask = tuple->t_infomask;
6506-
frz->xmax = HeapTupleHeaderGetRawXmax(tuple);
6506+
frz->frzflags = 0;
6507+
frz->checkflags = 0;
65076508

65086509
/*
65096510
* Process xmin, while keeping track of whether it's already frozen, or
@@ -6521,14 +6522,12 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
65216522
errmsg_internal("found xmin %u from before relfrozenxid %u",
65226523
xid, cutoffs->relfrozenxid)));
65236524

6525+
/* Will set freeze_xmin flags in freeze plan below */
65246526
freeze_xmin = TransactionIdPrecedes(xid, cutoffs->OldestXmin);
6525-
if (freeze_xmin && !TransactionIdDidCommit(xid))
6526-
ereport(ERROR,
6527-
(errcode(ERRCODE_DATA_CORRUPTED),
6528-
errmsg_internal("uncommitted xmin %u from before xid cutoff %u needs to be frozen",
6529-
xid, cutoffs->OldestXmin)));
65306527

6531-
/* Will set freeze_xmin flags in freeze plan below */
6528+
/* Verify that xmin committed if and when freeze plan is executed */
6529+
if (freeze_xmin)
6530+
frz->checkflags |= HEAP_FREEZE_CHECK_XMIN_COMMITTED;
65326531
}
65336532

65346533
/*
@@ -6551,7 +6550,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
65516550
}
65526551

65536552
/* Now process xmax */
6554-
xid = HeapTupleHeaderGetRawXmax(tuple);
6553+
xid = frz->xmax;
65556554
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
65566555
{
65576556
/* Raw xmax is a MultiXactId */
@@ -6662,21 +6661,16 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
66626661
errmsg_internal("found xmax %u from before relfrozenxid %u",
66636662
xid, cutoffs->relfrozenxid)));
66646663

6665-
if (TransactionIdPrecedes(xid, cutoffs->OldestXmin))
6666-
freeze_xmax = true;
6664+
/* Will set freeze_xmax flags in freeze plan below */
6665+
freeze_xmax = TransactionIdPrecedes(xid, cutoffs->OldestXmin);
66676666

66686667
/*
6669-
* If we freeze xmax, make absolutely sure that it's not an XID that
6670-
* is important. (Note, a lock-only xmax can be removed independent
6671-
* of committedness, since a committed lock holder has released the
6672-
* lock).
6668+
* Verify that xmax aborted if and when freeze plan is executed,
6669+
* provided it's from an update. (A lock-only xmax can be removed
6670+
* independent of this, since the lock is released at xact end.)
66736671
*/
6674-
if (freeze_xmax && !HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) &&
6675-
TransactionIdDidCommit(xid))
6676-
ereport(ERROR,
6677-
(errcode(ERRCODE_DATA_CORRUPTED),
6678-
errmsg_internal("cannot freeze committed xmax %u",
6679-
xid)));
6672+
if (freeze_xmax && !HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
6673+
frz->checkflags |= HEAP_FREEZE_CHECK_XMAX_ABORTED;
66806674
}
66816675
else if (!TransactionIdIsValid(xid))
66826676
{
@@ -6804,19 +6798,60 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer,
68046798

68056799
Assert(ntuples > 0);
68066800

6807-
START_CRIT_SECTION();
6801+
/*
6802+
* Perform xmin/xmax XID status sanity checks before critical section.
6803+
*
6804+
* heap_prepare_freeze_tuple doesn't perform these checks directly because
6805+
* pg_xact lookups are relatively expensive. They shouldn't be repeated
6806+
* by successive VACUUMs that each decide against freezing the same page.
6807+
*/
6808+
for (int i = 0; i < ntuples; i++)
6809+
{
6810+
HeapTupleFreeze *frz = tuples + i;
6811+
ItemId itemid = PageGetItemId(page, frz->offset);
6812+
HeapTupleHeader htup;
68086813

6809-
MarkBufferDirty(buffer);
6814+
htup = (HeapTupleHeader) PageGetItem(page, itemid);
6815+
6816+
/* Deliberately avoid relying on tuple hint bits here */
6817+
if (frz->checkflags & HEAP_FREEZE_CHECK_XMIN_COMMITTED)
6818+
{
6819+
TransactionId xmin = HeapTupleHeaderGetRawXmin(htup);
6820+
6821+
Assert(!HeapTupleHeaderXminFrozen(htup));
6822+
if (unlikely(!TransactionIdDidCommit(xmin)))
6823+
ereport(ERROR,
6824+
(errcode(ERRCODE_DATA_CORRUPTED),
6825+
errmsg_internal("uncommitted xmin %u needs to be frozen",
6826+
xmin)));
6827+
}
6828+
if (frz->checkflags & HEAP_FREEZE_CHECK_XMAX_ABORTED)
6829+
{
6830+
TransactionId xmax = HeapTupleHeaderGetRawXmax(htup);
6831+
6832+
Assert(TransactionIdIsNormal(xmax));
6833+
if (unlikely(!TransactionIdDidAbort(xmax)))
6834+
ereport(ERROR,
6835+
(errcode(ERRCODE_DATA_CORRUPTED),
6836+
errmsg_internal("cannot freeze non-aborted xmax %u",
6837+
xmax)));
6838+
}
6839+
}
6840+
6841+
START_CRIT_SECTION();
68106842

68116843
for (int i = 0; i < ntuples; i++)
68126844
{
6845+
HeapTupleFreeze *frz = tuples + i;
6846+
ItemId itemid = PageGetItemId(page, frz->offset);
68136847
HeapTupleHeader htup;
6814-
ItemId itemid = PageGetItemId(page, tuples[i].offset);
68156848

68166849
htup = (HeapTupleHeader) PageGetItem(page, itemid);
6817-
heap_execute_freeze_tuple(htup, &tuples[i]);
6850+
heap_execute_freeze_tuple(htup, frz);
68186851
}
68196852

6853+
MarkBufferDirty(buffer);
6854+
68206855
/* Now WAL-log freezing if necessary */
68216856
if (RelationNeedsWAL(rel))
68226857
{

src/include/access/heapam.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ typedef enum
100100
HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
101101
} HTSV_Result;
102102

103+
/*
104+
* heap_prepare_freeze_tuple may request that heap_freeze_execute_prepared
105+
* check any tuple's to-be-frozen xmin and/or xmax status using pg_xact
106+
*/
107+
#define HEAP_FREEZE_CHECK_XMIN_COMMITTED 0x01
108+
#define HEAP_FREEZE_CHECK_XMAX_ABORTED 0x02
109+
103110
/* heap_prepare_freeze_tuple state describing how to freeze a tuple */
104111
typedef struct HeapTupleFreeze
105112
{
@@ -109,6 +116,8 @@ typedef struct HeapTupleFreeze
109116
uint16 t_infomask;
110117
uint8 frzflags;
111118

119+
/* xmin/xmax check flags */
120+
uint8 checkflags;
112121
/* Page offset number for tuple */
113122
OffsetNumber offset;
114123
} HeapTupleFreeze;

0 commit comments

Comments
 (0)