Skip to content

Commit cd78f34

Browse files
author
Commitfest Bot
committed
[PATCH]: ./update-unused-lp-40-xid2fxid-v1.patch
Merge copies of converting an XID to a FullTransactionId. Assume twophase.c is the performance-sensitive caller, and preserve its choice of unlikely() branch hint. Add some retrospective rationale for that choice. Back-patch to v17, for the next commit to use it. Reviewed (in earlier versions) by Michael Paquier. Discussion: https://postgr.es/m/[email protected] Discussion: https://postgr.es/m/[email protected]
1 parent cc2d710 commit cd78f34

File tree

5 files changed

+77
-93
lines changed

5 files changed

+77
-93
lines changed

contrib/amcheck/verify_heapam.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -1877,7 +1877,9 @@ check_tuple(HeapCheckContext *ctx, bool *xmin_commit_status_ok,
18771877
/*
18781878
* Convert a TransactionId into a FullTransactionId using our cached values of
18791879
* the valid transaction ID range. It is the caller's responsibility to have
1880-
* already updated the cached values, if necessary.
1880+
* already updated the cached values, if necessary. This is akin to
1881+
* FullTransactionIdFromAllowableAt(), but it tolerates corruption in the form
1882+
* of an xid before epoch 0.
18811883
*/
18821884
static FullTransactionId
18831885
FullTransactionIdFromXidAndCtx(TransactionId xid, const HeapCheckContext *ctx)

src/backend/access/transam/twophase.c

+5-21
Original file line numberDiff line numberDiff line change
@@ -929,32 +929,16 @@ TwoPhaseGetDummyProc(TransactionId xid, bool lock_held)
929929
/*
930930
* Compute the FullTransactionId for the given TransactionId.
931931
*
932-
* The wrap logic is safe here because the span of active xids cannot exceed one
933-
* epoch at any given time.
932+
* This is safe if the xid has not yet reached COMMIT PREPARED or ROLLBACK
933+
* PREPARED. After those commands, concurrent vac_truncate_clog() may make
934+
* the xid cease to qualify as allowable. XXX Not all callers limit their
935+
* calls accordingly.
934936
*/
935937
static inline FullTransactionId
936938
AdjustToFullTransactionId(TransactionId xid)
937939
{
938-
FullTransactionId nextFullXid;
939-
TransactionId nextXid;
940-
uint32 epoch;
941-
942940
Assert(TransactionIdIsValid(xid));
943-
944-
LWLockAcquire(XidGenLock, LW_SHARED);
945-
nextFullXid = TransamVariables->nextXid;
946-
LWLockRelease(XidGenLock);
947-
948-
nextXid = XidFromFullTransactionId(nextFullXid);
949-
epoch = EpochFromFullTransactionId(nextFullXid);
950-
if (unlikely(xid > nextXid))
951-
{
952-
/* Wraparound occurred, must be from a prev epoch. */
953-
Assert(epoch > 0);
954-
epoch--;
955-
}
956-
957-
return FullTransactionIdFromEpochAndXid(epoch, xid);
941+
return FullTransactionIdFromAllowableAt(ReadNextFullTransactionId(), xid);
958942
}
959943

960944
static inline int

src/backend/access/transam/xlogreader.c

+2-16
Original file line numberDiff line numberDiff line change
@@ -2166,28 +2166,14 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
21662166
FullTransactionId
21672167
XLogRecGetFullXid(XLogReaderState *record)
21682168
{
2169-
TransactionId xid,
2170-
next_xid;
2171-
uint32 epoch;
2172-
21732169
/*
21742170
* This function is only safe during replay, because it depends on the
21752171
* replay state. See AdvanceNextFullTransactionIdPastXid() for more.
21762172
*/
21772173
Assert(AmStartupProcess() || !IsUnderPostmaster);
21782174

2179-
xid = XLogRecGetXid(record);
2180-
next_xid = XidFromFullTransactionId(TransamVariables->nextXid);
2181-
epoch = EpochFromFullTransactionId(TransamVariables->nextXid);
2182-
2183-
/*
2184-
* If xid is numerically greater than next_xid, it has to be from the last
2185-
* epoch.
2186-
*/
2187-
if (unlikely(xid > next_xid))
2188-
--epoch;
2189-
2190-
return FullTransactionIdFromEpochAndXid(epoch, xid);
2175+
return FullTransactionIdFromAllowableAt(TransamVariables->nextXid,
2176+
XLogRecGetXid(record));
21912177
}
21922178

21932179
#endif

src/backend/utils/adt/xid8funcs.c

+24-55
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,11 @@ static bool
9797
TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
9898
{
9999
TransactionId xid = XidFromFullTransactionId(fxid);
100-
uint32 now_epoch;
101-
TransactionId now_epoch_next_xid;
102100
FullTransactionId now_fullxid;
103-
TransactionId oldest_xid;
104-
FullTransactionId oldest_fxid;
101+
TransactionId oldest_clog_xid;
102+
FullTransactionId oldest_clog_fxid;
105103

106104
now_fullxid = ReadNextFullTransactionId();
107-
now_epoch_next_xid = XidFromFullTransactionId(now_fullxid);
108-
now_epoch = EpochFromFullTransactionId(now_fullxid);
109105

110106
if (extracted_xid != NULL)
111107
*extracted_xid = xid;
@@ -135,52 +131,19 @@ TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
135131

136132
/*
137133
* If fxid is not older than TransamVariables->oldestClogXid, the relevant
138-
* CLOG entry is guaranteed to still exist. Convert
139-
* TransamVariables->oldestClogXid into a FullTransactionId to compare it
140-
* with fxid. Determine the right epoch knowing that oldest_fxid
141-
* shouldn't be more than 2^31 older than now_fullxid.
142-
*/
143-
oldest_xid = TransamVariables->oldestClogXid;
144-
Assert(TransactionIdPrecedesOrEquals(oldest_xid, now_epoch_next_xid));
145-
if (oldest_xid <= now_epoch_next_xid)
146-
{
147-
oldest_fxid = FullTransactionIdFromEpochAndXid(now_epoch, oldest_xid);
148-
}
149-
else
150-
{
151-
Assert(now_epoch > 0);
152-
oldest_fxid = FullTransactionIdFromEpochAndXid(now_epoch - 1, oldest_xid);
153-
}
154-
return !FullTransactionIdPrecedes(fxid, oldest_fxid);
155-
}
156-
157-
/*
158-
* Convert a TransactionId obtained from a snapshot held by the caller to a
159-
* FullTransactionId. Use next_fxid as a reference FullTransactionId, so that
160-
* we can compute the high order bits. It must have been obtained by the
161-
* caller with ReadNextFullTransactionId() after the snapshot was created.
162-
*/
163-
static FullTransactionId
164-
widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
165-
{
166-
TransactionId next_xid = XidFromFullTransactionId(next_fxid);
167-
uint32 epoch = EpochFromFullTransactionId(next_fxid);
168-
169-
/* Special transaction ID. */
170-
if (!TransactionIdIsNormal(xid))
171-
return FullTransactionIdFromEpochAndXid(0, xid);
172-
173-
/*
174-
* The 64 bit result must be <= next_fxid, since next_fxid hadn't been
175-
* issued yet when the snapshot was created. Every TransactionId in the
176-
* snapshot must therefore be from the same epoch as next_fxid, or the
177-
* epoch before. We know this because next_fxid is never allow to get
178-
* more than one epoch ahead of the TransactionIds in any snapshot.
134+
* CLOG entry is guaranteed to still exist.
135+
*
136+
* TransamVariables->oldestXid governs allowable XIDs. Usually,
137+
* oldestClogXid==oldestXid. It's also possible for oldestClogXid to
138+
* follow oldestXid, in which case oldestXid might advance after our
139+
* ReadNextFullTransactionId() call. If oldestXid has advanced, that
140+
* advancement reinstated the usual oldestClogXid==oldestXid. Whether or
141+
* not that happened, oldestClogXid is allowable relative to now_fullxid.
179142
*/
180-
if (xid > next_xid)
181-
epoch--;
182-
183-
return FullTransactionIdFromEpochAndXid(epoch, xid);
143+
oldest_clog_xid = TransamVariables->oldestClogXid;
144+
oldest_clog_fxid =
145+
FullTransactionIdFromAllowableAt(now_fullxid, oldest_clog_xid);
146+
return !FullTransactionIdPrecedes(fxid, oldest_clog_fxid);
184147
}
185148

186149
/*
@@ -420,12 +383,18 @@ pg_current_snapshot(PG_FUNCTION_ARGS)
420383
nxip = cur->xcnt;
421384
snap = palloc(PG_SNAPSHOT_SIZE(nxip));
422385

423-
/* fill */
424-
snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
425-
snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
386+
/*
387+
* Fill. This is the current backend's active snapshot, so MyProc->xmin
388+
* is <= all these XIDs. As long as that remains so, oldestXid can't
389+
* advance past any of these XIDs. Hence, these XIDs remain allowable
390+
* relative to next_fxid.
391+
*/
392+
snap->xmin = FullTransactionIdFromAllowableAt(next_fxid, cur->xmin);
393+
snap->xmax = FullTransactionIdFromAllowableAt(next_fxid, cur->xmax);
426394
snap->nxip = nxip;
427395
for (i = 0; i < nxip; i++)
428-
snap->xip[i] = widen_snapshot_xid(cur->xip[i], next_fxid);
396+
snap->xip[i] =
397+
FullTransactionIdFromAllowableAt(next_fxid, cur->xip[i]);
429398

430399
/*
431400
* We want them guaranteed to be in ascending order. This also removes

src/include/access/transam.h

+43
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,49 @@ FullTransactionIdNewer(FullTransactionId a, FullTransactionId b)
370370
return b;
371371
}
372372

373+
/*
374+
* Compute FullTransactionId for the given TransactionId, assuming xid was
375+
* between [oldestXid, nextXid] at the time when TransamVariables->nextXid was
376+
* nextFullXid. When adding calls, evaluate what prevents xid from preceding
377+
* oldestXid if SetTransactionIdLimit() runs between the collection of xid and
378+
* the collection of nextFullXid.
379+
*/
380+
static inline FullTransactionId
381+
FullTransactionIdFromAllowableAt(FullTransactionId nextFullXid,
382+
TransactionId xid)
383+
{
384+
uint32 epoch;
385+
386+
/* Special transaction ID. */
387+
if (!TransactionIdIsNormal(xid))
388+
return FullTransactionIdFromEpochAndXid(0, xid);
389+
390+
Assert(TransactionIdPrecedesOrEquals(xid,
391+
XidFromFullTransactionId(nextFullXid)));
392+
393+
/*
394+
* The 64 bit result must be <= nextFullXid, since nextFullXid hadn't been
395+
* issued yet when xid was in the past. The xid must therefore be from
396+
* the epoch of nextFullXid or the epoch before. We know this because we
397+
* must remove (by freezing) an XID before assigning the XID half an epoch
398+
* ahead of it.
399+
*
400+
* The unlikely() branch hint is dubious. It's perfect for the first 2^32
401+
* XIDs of a cluster's life. Right at 2^32 XIDs, misprediction shoots to
402+
* 100%, then improves until perfection returns 2^31 XIDs later. Since
403+
* current callers pass relatively-recent XIDs, expect >90% prediction
404+
* accuracy overall. This favors average latency over tail latency.
405+
*/
406+
epoch = EpochFromFullTransactionId(nextFullXid);
407+
if (unlikely(xid > XidFromFullTransactionId(nextFullXid)))
408+
{
409+
Assert(epoch != 0);
410+
epoch--;
411+
}
412+
413+
return FullTransactionIdFromEpochAndXid(epoch, xid);
414+
}
415+
373416
#endif /* FRONTEND */
374417

375418
#endif /* TRANSAM_H */

0 commit comments

Comments
 (0)