Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2fc7af5

Browse files
committed
Add basic infrastructure for 64 bit transaction IDs.
Instead of inferring epoch progress from xids and checkpoints, introduce a 64 bit FullTransactionId type and use it to track xid generation. This fixes an unlikely bug where the epoch is reported incorrectly if the range of active xids wraps around more than once between checkpoints. The only user-visible effect of this commit is to correct the epoch used by txid_current() and txid_status(), also visible with pg_controldata, in those rare circumstances. It also creates some basic infrastructure so that later patches can use 64 bit transaction IDs in more places. The new type is a struct that we pass by value, as a form of strong typedef. This prevents the sort of accidental confusion between TransactionId and FullTransactionId that would be possible if we were to use a plain old uint64. Author: Thomas Munro Reported-by: Amit Kapila Reviewed-by: Andres Freund, Tom Lane, Heikki Linnakangas Discussion: https://postgr.es/m/CAA4eK1%2BMv%2Bmb0HFfWM9Srtc6MVe160WFurXV68iAFMcagRZ0dQ%40mail.gmail.com
1 parent 2a96909 commit 2fc7af5

File tree

24 files changed

+224
-238
lines changed

24 files changed

+224
-238
lines changed

src/backend/access/rmgrdesc/xlogdesc.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
#include "postgres.h"
1616

17+
#include "access/transam.h"
1718
#include "access/xlog.h"
1819
#include "access/xlog_internal.h"
1920
#include "catalog/pg_control.h"
@@ -52,7 +53,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
5253
checkpoint->ThisTimeLineID,
5354
checkpoint->PrevTimeLineID,
5455
checkpoint->fullPageWrites ? "true" : "false",
55-
checkpoint->nextXidEpoch, checkpoint->nextXid,
56+
EpochFromFullTransactionId(checkpoint->nextFullXid),
57+
XidFromFullTransactionId(checkpoint->nextFullXid),
5658
checkpoint->nextOid,
5759
checkpoint->nextMulti,
5860
checkpoint->nextMultiOffset,

src/backend/access/transam/clog.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -749,12 +749,12 @@ ZeroCLOGPage(int pageno, bool writeXlog)
749749

750750
/*
751751
* This must be called ONCE during postmaster or standalone-backend startup,
752-
* after StartupXLOG has initialized ShmemVariableCache->nextXid.
752+
* after StartupXLOG has initialized ShmemVariableCache->nextFullXid.
753753
*/
754754
void
755755
StartupCLOG(void)
756756
{
757-
TransactionId xid = ShmemVariableCache->nextXid;
757+
TransactionId xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
758758
int pageno = TransactionIdToPage(xid);
759759

760760
LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
@@ -773,7 +773,7 @@ StartupCLOG(void)
773773
void
774774
TrimCLOG(void)
775775
{
776-
TransactionId xid = ShmemVariableCache->nextXid;
776+
TransactionId xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
777777
int pageno = TransactionIdToPage(xid);
778778

779779
LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
@@ -792,7 +792,7 @@ TrimCLOG(void)
792792
* but makes no WAL entry). Let's just be safe. (We need not worry about
793793
* pages beyond the current one, since those will be zeroed when first
794794
* used. For the same reason, there is no need to do anything when
795-
* nextXid is exactly at a page boundary; and it's likely that the
795+
* nextFullXid is exactly at a page boundary; and it's likely that the
796796
* "current" page doesn't exist yet in that case.)
797797
*/
798798
if (TransactionIdToPgIndex(xid) != 0)

src/backend/access/transam/commit_ts.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ ZeroCommitTsPage(int pageno, bool writeXlog)
553553

554554
/*
555555
* This must be called ONCE during postmaster or standalone-backend startup,
556-
* after StartupXLOG has initialized ShmemVariableCache->nextXid.
556+
* after StartupXLOG has initialized ShmemVariableCache->nextFullXid.
557557
*/
558558
void
559559
StartupCommitTs(void)
@@ -643,7 +643,7 @@ ActivateCommitTs(void)
643643
}
644644
LWLockRelease(CommitTsLock);
645645

646-
xid = ShmemVariableCache->nextXid;
646+
xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
647647
pageno = TransactionIdToCTsPage(xid);
648648

649649
/*

src/backend/access/transam/multixact.c

+4-16
Original file line numberDiff line numberDiff line change
@@ -3267,9 +3267,9 @@ multixact_redo(XLogReaderState *record)
32673267
xlrec->moff + xlrec->nmembers);
32683268

32693269
/*
3270-
* Make sure nextXid is beyond any XID mentioned in the record. This
3271-
* should be unnecessary, since any XID found here ought to have other
3272-
* evidence in the XLOG, but let's be safe.
3270+
* Make sure nextFullXid is beyond any XID mentioned in the record.
3271+
* This should be unnecessary, since any XID found here ought to have
3272+
* other evidence in the XLOG, but let's be safe.
32733273
*/
32743274
max_xid = XLogRecGetXid(record);
32753275
for (i = 0; i < xlrec->nmembers; i++)
@@ -3278,19 +3278,7 @@ multixact_redo(XLogReaderState *record)
32783278
max_xid = xlrec->members[i].xid;
32793279
}
32803280

3281-
/*
3282-
* We don't expect anyone else to modify nextXid, hence startup
3283-
* process doesn't need to hold a lock while checking this. We still
3284-
* acquire the lock to modify it, though.
3285-
*/
3286-
if (TransactionIdFollowsOrEquals(max_xid,
3287-
ShmemVariableCache->nextXid))
3288-
{
3289-
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
3290-
ShmemVariableCache->nextXid = max_xid;
3291-
TransactionIdAdvance(ShmemVariableCache->nextXid);
3292-
LWLockRelease(XidGenLock);
3293-
}
3281+
AdvanceNextFullTransactionIdPastXid(max_xid);
32943282
}
32953283
else if (info == XLOG_MULTIXACT_TRUNCATE_ID)
32963284
{

src/backend/access/transam/subtrans.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,15 @@ ZeroSUBTRANSPage(int pageno)
241241

242242
/*
243243
* This must be called ONCE during postmaster or standalone-backend startup,
244-
* after StartupXLOG has initialized ShmemVariableCache->nextXid.
244+
* after StartupXLOG has initialized ShmemVariableCache->nextFullXid.
245245
*
246-
* oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
246+
* oldestActiveXID is the oldest XID of any prepared transaction, or nextFullXid
247247
* if there are none.
248248
*/
249249
void
250250
StartupSUBTRANS(TransactionId oldestActiveXID)
251251
{
252+
FullTransactionId nextFullXid;
252253
int startPage;
253254
int endPage;
254255

@@ -261,7 +262,8 @@ StartupSUBTRANS(TransactionId oldestActiveXID)
261262
LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
262263

263264
startPage = TransactionIdToPage(oldestActiveXID);
264-
endPage = TransactionIdToPage(ShmemVariableCache->nextXid);
265+
nextFullXid = ShmemVariableCache->nextFullXid;
266+
endPage = TransactionIdToPage(XidFromFullTransactionId(nextFullXid));
265267

266268
while (startPage != endPage)
267269
{

src/backend/access/transam/twophase.c

+13-27
Original file line numberDiff line numberDiff line change
@@ -1878,16 +1878,16 @@ restoreTwoPhaseData(void)
18781878
*
18791879
* Scan the shared memory entries of TwoPhaseState and determine the range
18801880
* of valid XIDs present. This is run during database startup, after we
1881-
* have completed reading WAL. ShmemVariableCache->nextXid has been set to
1881+
* have completed reading WAL. ShmemVariableCache->nextFullXid has been set to
18821882
* one more than the highest XID for which evidence exists in WAL.
18831883
*
1884-
* We throw away any prepared xacts with main XID beyond nextXid --- if any
1884+
* We throw away any prepared xacts with main XID beyond nextFullXid --- if any
18851885
* are present, it suggests that the DBA has done a PITR recovery to an
18861886
* earlier point in time without cleaning out pg_twophase. We dare not
18871887
* try to recover such prepared xacts since they likely depend on database
18881888
* state that doesn't exist now.
18891889
*
1890-
* However, we will advance nextXid beyond any subxact XIDs belonging to
1890+
* However, we will advance nextFullXid beyond any subxact XIDs belonging to
18911891
* valid prepared xacts. We need to do this since subxact commit doesn't
18921892
* write a WAL entry, and so there might be no evidence in WAL of those
18931893
* subxact XIDs.
@@ -1897,7 +1897,7 @@ restoreTwoPhaseData(void)
18971897
* backup should be rolled in.
18981898
*
18991899
* Our other responsibility is to determine and return the oldest valid XID
1900-
* among the prepared xacts (if none, return ShmemVariableCache->nextXid).
1900+
* among the prepared xacts (if none, return ShmemVariableCache->nextFullXid).
19011901
* This is needed to synchronize pg_subtrans startup properly.
19021902
*
19031903
* If xids_p and nxids_p are not NULL, pointer to a palloc'd array of all
@@ -1907,7 +1907,8 @@ restoreTwoPhaseData(void)
19071907
TransactionId
19081908
PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
19091909
{
1910-
TransactionId origNextXid = ShmemVariableCache->nextXid;
1910+
FullTransactionId nextFullXid = ShmemVariableCache->nextFullXid;
1911+
TransactionId origNextXid = XidFromFullTransactionId(nextFullXid);
19111912
TransactionId result = origNextXid;
19121913
TransactionId *xids = NULL;
19131914
int nxids = 0;
@@ -2123,7 +2124,7 @@ RecoverPreparedTransactions(void)
21232124
*
21242125
* If setParent is true, set up subtransaction parent linkages.
21252126
*
2126-
* If setNextXid is true, set ShmemVariableCache->nextXid to the newest
2127+
* If setNextXid is true, set ShmemVariableCache->nextFullXid to the newest
21272128
* value scanned.
21282129
*/
21292130
static char *
@@ -2132,7 +2133,8 @@ ProcessTwoPhaseBuffer(TransactionId xid,
21322133
bool fromdisk,
21332134
bool setParent, bool setNextXid)
21342135
{
2135-
TransactionId origNextXid = ShmemVariableCache->nextXid;
2136+
FullTransactionId nextFullXid = ShmemVariableCache->nextFullXid;
2137+
TransactionId origNextXid = XidFromFullTransactionId(nextFullXid);
21362138
TransactionId *subxids;
21372139
char *buf;
21382140
TwoPhaseFileHeader *hdr;
@@ -2212,7 +2214,7 @@ ProcessTwoPhaseBuffer(TransactionId xid,
22122214

22132215
/*
22142216
* Examine subtransaction XIDs ... they should all follow main XID, and
2215-
* they may force us to advance nextXid.
2217+
* they may force us to advance nextFullXid.
22162218
*/
22172219
subxids = (TransactionId *) (buf +
22182220
MAXALIGN(sizeof(TwoPhaseFileHeader)) +
@@ -2223,25 +2225,9 @@ ProcessTwoPhaseBuffer(TransactionId xid,
22232225

22242226
Assert(TransactionIdFollows(subxid, xid));
22252227

2226-
/* update nextXid if needed */
2227-
if (setNextXid &&
2228-
TransactionIdFollowsOrEquals(subxid,
2229-
ShmemVariableCache->nextXid))
2230-
{
2231-
/*
2232-
* We don't expect anyone else to modify nextXid, hence we don't
2233-
* need to hold a lock while examining it. We still acquire the
2234-
* lock to modify it, though, so we recheck.
2235-
*/
2236-
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
2237-
if (TransactionIdFollowsOrEquals(subxid,
2238-
ShmemVariableCache->nextXid))
2239-
{
2240-
ShmemVariableCache->nextXid = subxid;
2241-
TransactionIdAdvance(ShmemVariableCache->nextXid);
2242-
}
2243-
LWLockRelease(XidGenLock);
2244-
}
2228+
/* update nextFullXid if needed */
2229+
if (setNextXid)
2230+
AdvanceNextFullTransactionIdPastXid(subxid);
22452231

22462232
if (setParent)
22472233
SubTransSetParent(subxid, xid);

src/backend/access/transam/varsup.c

+61-15
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ GetNewTransactionId(bool isSubXact)
7373

7474
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
7575

76-
xid = ShmemVariableCache->nextXid;
76+
xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
7777

7878
/*----------
7979
* Check to see if it's safe to assign another XID. This protects against
@@ -156,7 +156,7 @@ GetNewTransactionId(bool isSubXact)
156156

157157
/* Re-acquire lock and start over */
158158
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
159-
xid = ShmemVariableCache->nextXid;
159+
xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
160160
}
161161

162162
/*
@@ -173,12 +173,12 @@ GetNewTransactionId(bool isSubXact)
173173
ExtendSUBTRANS(xid);
174174

175175
/*
176-
* Now advance the nextXid counter. This must not happen until after we
177-
* have successfully completed ExtendCLOG() --- if that routine fails, we
178-
* want the next incoming transaction to try it again. We cannot assign
179-
* more XIDs until there is CLOG space for them.
176+
* Now advance the nextFullXid counter. This must not happen until after
177+
* we have successfully completed ExtendCLOG() --- if that routine fails,
178+
* we want the next incoming transaction to try it again. We cannot
179+
* assign more XIDs until there is CLOG space for them.
180180
*/
181-
TransactionIdAdvance(ShmemVariableCache->nextXid);
181+
FullTransactionIdAdvance(&ShmemVariableCache->nextFullXid);
182182

183183
/*
184184
* We must store the new XID into the shared ProcArray before releasing
@@ -236,18 +236,64 @@ GetNewTransactionId(bool isSubXact)
236236
}
237237

238238
/*
239-
* Read nextXid but don't allocate it.
239+
* Read nextFullXid but don't allocate it.
240240
*/
241-
TransactionId
242-
ReadNewTransactionId(void)
241+
FullTransactionId
242+
ReadNextFullTransactionId(void)
243243
{
244-
TransactionId xid;
244+
FullTransactionId fullXid;
245245

246246
LWLockAcquire(XidGenLock, LW_SHARED);
247-
xid = ShmemVariableCache->nextXid;
247+
fullXid = ShmemVariableCache->nextFullXid;
248248
LWLockRelease(XidGenLock);
249249

250-
return xid;
250+
return fullXid;
251+
}
252+
253+
/*
254+
* Advance nextFullXid to the value after a given xid. The epoch is inferred.
255+
* This must only be called during recovery or from two-phase start-up code.
256+
*/
257+
void
258+
AdvanceNextFullTransactionIdPastXid(TransactionId xid)
259+
{
260+
FullTransactionId newNextFullXid;
261+
TransactionId next_xid;
262+
uint32 epoch;
263+
264+
/*
265+
* It is safe to read nextFullXid without a lock, because this is only
266+
* called from the startup process or single-process mode, meaning that no
267+
* other process can modify it.
268+
*/
269+
Assert(AmStartupProcess() || !IsUnderPostmaster);
270+
271+
/* Fast return if this isn't an xid high enough to move the needle. */
272+
next_xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
273+
if (!TransactionIdFollowsOrEquals(xid, next_xid))
274+
return;
275+
276+
/*
277+
* Compute the FullTransactionId that comes after the given xid. To do
278+
* this, we preserve the existing epoch, but detect when we've wrapped
279+
* into a new epoch. This is necessary because WAL records and 2PC state
280+
* currently contain 32 bit xids. The wrap logic is safe in those cases
281+
* because the span of active xids cannot exceed one epoch at any given
282+
* point in the WAL stream.
283+
*/
284+
TransactionIdAdvance(xid);
285+
epoch = EpochFromFullTransactionId(ShmemVariableCache->nextFullXid);
286+
if (unlikely(xid < next_xid))
287+
++epoch;
288+
newNextFullXid = FullTransactionIdFromEpochAndXid(epoch, xid);
289+
290+
/*
291+
* We still need to take a lock to modify the value when there are
292+
* concurrent readers.
293+
*/
294+
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
295+
ShmemVariableCache->nextFullXid = newNextFullXid;
296+
LWLockRelease(XidGenLock);
251297
}
252298

253299
/*
@@ -351,7 +397,7 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
351397
ShmemVariableCache->xidStopLimit = xidStopLimit;
352398
ShmemVariableCache->xidWrapLimit = xidWrapLimit;
353399
ShmemVariableCache->oldestXidDB = oldest_datoid;
354-
curXid = ShmemVariableCache->nextXid;
400+
curXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
355401
LWLockRelease(XidGenLock);
356402

357403
/* Log the info */
@@ -427,7 +473,7 @@ ForceTransactionIdLimitUpdate(void)
427473

428474
/* Locking is probably not really necessary, but let's be careful */
429475
LWLockAcquire(XidGenLock, LW_SHARED);
430-
nextXid = ShmemVariableCache->nextXid;
476+
nextXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
431477
xidVacLimit = ShmemVariableCache->xidVacLimit;
432478
oldestXid = ShmemVariableCache->oldestXid;
433479
oldestXidDB = ShmemVariableCache->oldestXidDB;

0 commit comments

Comments
 (0)