From 2f893e10dcf9ec82d9cabcdaeedce2681632b87d Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Mon, 9 Jul 2018 21:54:03 +1200 Subject: [PATCH] Track the next xid using 64 bits. Instead of tracking the epoch independently, start using a 64 bit transaction ID in several places. This fix an unlikely bug where an epoch increment could be missed if you managed to consume more than 2^32 transactions between checkpoints. Work in progress! Author: Thomas Munro Reviewed-by: Andres Freund Diagnosis-by: Amit Kapila Discussion: https://postgr.es/m/CAA4eK1%2BMv%2Bmb0HFfWM9Srtc6MVe160WFurXV68iAFMcagRZ0dQ%40mail.gmail.com --- src/backend/access/nbtree/nbtpage.c | 4 +- src/backend/access/rmgrdesc/xlogdesc.c | 6 +- src/backend/access/transam/clog.c | 4 +- src/backend/access/transam/commit_ts.c | 5 +- src/backend/access/transam/multixact.c | 9 +- src/backend/access/transam/subtrans.c | 4 +- src/backend/access/transam/twophase.c | 19 ++--- src/backend/access/transam/varsup.c | 53 +++++++++--- src/backend/access/transam/xact.c | 21 +---- src/backend/access/transam/xlog.c | 105 ++++++------------------ src/backend/commands/vacuum.c | 10 +-- src/backend/postmaster/autovacuum.c | 4 +- src/backend/replication/walreceiver.c | 5 +- src/backend/replication/walsender.c | 31 ++----- src/backend/storage/ipc/procarray.c | 21 ++--- src/backend/storage/lmgr/predicate.c | 2 +- src/backend/utils/adt/txid.c | 13 ++- src/backend/utils/misc/pg_controldata.c | 6 +- src/bin/pg_controldata/pg_controldata.c | 6 +- src/bin/pg_resetwal/pg_resetwal.c | 20 +++-- src/include/access/transam.h | 43 +++++++++- src/include/access/xlog.h | 1 - src/include/catalog/pg_control.h | 4 +- src/include/storage/standby.h | 2 +- 24 files changed, 186 insertions(+), 212 deletions(-) diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index 2e959da5f85..3e1f3683734 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -1946,7 +1946,7 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty) * Mark the page itself deleted. It can be recycled when all current * transactions are gone. Storing GetTopTransactionId() would work, but * we're in VACUUM and would not otherwise have an XID. Having already - * updated links to the target, ReadNewTransactionId() suffices as an + * updated links to the target, ReadNextFullTransactionId() suffices as an * upper bound. Any scan having retained a now-stale link is advertising * in its PGXACT an xmin less than or equal to the value we read here. It * will continue to do so, holding back RecentGlobalXmin, for the duration @@ -1956,7 +1956,7 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty) opaque = (BTPageOpaque) PageGetSpecialPointer(page); opaque->btpo_flags &= ~BTP_HALF_DEAD; opaque->btpo_flags |= BTP_DELETED; - opaque->btpo.xact = ReadNewTransactionId(); + opaque->btpo.xact = XidFromFullTransactionId(ReadNextFullTransactionId()); /* And update the metapage, if needed */ if (BufferIsValid(metabuf)) diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c index 00741c7b09e..3655bac9ef1 100644 --- a/src/backend/access/rmgrdesc/xlogdesc.c +++ b/src/backend/access/rmgrdesc/xlogdesc.c @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "access/transam.h" #include "access/xlog.h" #include "access/xlog_internal.h" #include "catalog/pg_control.h" @@ -44,7 +45,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record) CheckPoint *checkpoint = (CheckPoint *) rec; appendStringInfo(buf, "redo %X/%X; " - "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; " + "tli %u; prev tli %u; fpw %s; xid " UINT64_FORMAT ";" + " oid %u; multi %u; offset %u; " "oldest xid %u in DB %u; oldest multi %u in DB %u; " "oldest/newest commit timestamp xid: %u/%u; " "oldest running xid %u; %s", @@ -52,7 +54,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record) checkpoint->ThisTimeLineID, checkpoint->PrevTimeLineID, checkpoint->fullPageWrites ? "true" : "false", - checkpoint->nextXidEpoch, checkpoint->nextXid, + U64FromFullTransactionId(checkpoint->nextFullXid), checkpoint->nextOid, checkpoint->nextMulti, checkpoint->nextMultiOffset, diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c index 8b7ff5b0c24..4fccbc9516c 100644 --- a/src/backend/access/transam/clog.c +++ b/src/backend/access/transam/clog.c @@ -754,7 +754,7 @@ ZeroCLOGPage(int pageno, bool writeXlog) void StartupCLOG(void) { - TransactionId xid = ShmemVariableCache->nextXid; + TransactionId xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); int pageno = TransactionIdToPage(xid); LWLockAcquire(CLogControlLock, LW_EXCLUSIVE); @@ -773,7 +773,7 @@ StartupCLOG(void) void TrimCLOG(void) { - TransactionId xid = ShmemVariableCache->nextXid; + TransactionId xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); int pageno = TransactionIdToPage(xid); LWLockAcquire(CLogControlLock, LW_EXCLUSIVE); diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c index 73fac1ba81d..da85904b3d6 100644 --- a/src/backend/access/transam/commit_ts.c +++ b/src/backend/access/transam/commit_ts.c @@ -644,7 +644,7 @@ ActivateCommitTs(void) } LWLockRelease(CommitTsLock); - xid = ShmemVariableCache->nextXid; + xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); pageno = TransactionIdToCTsPage(xid); /* @@ -671,7 +671,8 @@ ActivateCommitTs(void) if (ShmemVariableCache->oldestCommitTsXid == InvalidTransactionId) { ShmemVariableCache->oldestCommitTsXid = - ShmemVariableCache->newestCommitTsXid = ReadNewTransactionId(); + ShmemVariableCache->newestCommitTsXid = + XidFromFullTransactionId(ReadNextFullTransactionId()); } LWLockRelease(CommitTsLock); diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index a9a51055e96..7478beed44d 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -3283,14 +3283,7 @@ multixact_redo(XLogReaderState *record) * process doesn't need to hold a lock while checking this. We still * acquire the lock to modify it, though. */ - if (TransactionIdFollowsOrEquals(max_xid, - ShmemVariableCache->nextXid)) - { - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = max_xid; - TransactionIdAdvance(ShmemVariableCache->nextXid); - LWLockRelease(XidGenLock); - } + AdvanceNextFullTransactionIdPast(max_xid, true); } else if (info == XLOG_MULTIXACT_TRUNCATE_ID) { diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c index 4faa21f5aef..0cded8ac3c6 100644 --- a/src/backend/access/transam/subtrans.c +++ b/src/backend/access/transam/subtrans.c @@ -249,6 +249,7 @@ ZeroSUBTRANSPage(int pageno) void StartupSUBTRANS(TransactionId oldestActiveXID) { + FullTransactionId nextFullXid; int startPage; int endPage; @@ -261,7 +262,8 @@ StartupSUBTRANS(TransactionId oldestActiveXID) LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); startPage = TransactionIdToPage(oldestActiveXID); - endPage = TransactionIdToPage(ShmemVariableCache->nextXid); + nextFullXid = ShmemVariableCache->nextFullXid; + endPage = TransactionIdToPage(XidFromFullTransactionId(nextFullXid)); while (startPage != endPage) { diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index e8d4e37fe30..36603a9a34c 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -1879,7 +1879,8 @@ restoreTwoPhaseData(void) TransactionId PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p) { - TransactionId origNextXid = ShmemVariableCache->nextXid; + FullTransactionId nextFullXid = ShmemVariableCache->nextFullXid; + TransactionId origNextXid = XidFromFullTransactionId(nextFullXid); TransactionId result = origNextXid; TransactionId *xids = NULL; int nxids = 0; @@ -2104,7 +2105,8 @@ ProcessTwoPhaseBuffer(TransactionId xid, bool fromdisk, bool setParent, bool setNextXid) { - TransactionId origNextXid = ShmemVariableCache->nextXid; + FullTransactionId nextFullXid = ShmemVariableCache->nextFullXid; + TransactionId origNextXid = XidFromFullTransactionId(nextFullXid); TransactionId *subxids; char *buf; TwoPhaseFileHeader *hdr; @@ -2210,23 +2212,14 @@ ProcessTwoPhaseBuffer(TransactionId xid, Assert(TransactionIdFollows(subxid, xid)); /* update nextXid if needed */ - if (setNextXid && - TransactionIdFollowsOrEquals(subxid, - ShmemVariableCache->nextXid)) + if (setNextXid) { /* * We don't expect anyone else to modify nextXid, hence we don't * need to hold a lock while examining it. We still acquire the * lock to modify it, though, so we recheck. */ - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - if (TransactionIdFollowsOrEquals(subxid, - ShmemVariableCache->nextXid)) - { - ShmemVariableCache->nextXid = subxid; - TransactionIdAdvance(ShmemVariableCache->nextXid); - } - LWLockRelease(XidGenLock); + AdvanceNextFullTransactionIdPast(subxid, true); } if (setParent) diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c index 394843f7e91..d1396f3f0e1 100644 --- a/src/backend/access/transam/varsup.c +++ b/src/backend/access/transam/varsup.c @@ -73,7 +73,7 @@ GetNewTransactionId(bool isSubXact) LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - xid = ShmemVariableCache->nextXid; + xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); /*---------- * Check to see if it's safe to assign another XID. This protects against @@ -156,7 +156,7 @@ GetNewTransactionId(bool isSubXact) /* Re-acquire lock and start over */ LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - xid = ShmemVariableCache->nextXid; + xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); } /* @@ -173,12 +173,12 @@ GetNewTransactionId(bool isSubXact) ExtendSUBTRANS(xid); /* - * Now advance the nextXid counter. This must not happen until after we + * Now advance the nextFullXid counter. This must not happen until after we * have successfully completed ExtendCLOG() --- if that routine fails, we * want the next incoming transaction to try it again. We cannot assign * more XIDs until there is CLOG space for them. */ - TransactionIdAdvance(ShmemVariableCache->nextXid); + FullTransactionIdAdvance(&ShmemVariableCache->nextFullXid); /* * We must store the new XID into the shared ProcArray before releasing @@ -244,18 +244,47 @@ GetNewTransactionId(bool isSubXact) } /* - * Read nextXid but don't allocate it. + * Read nextFullXid but don't allocate it. */ -TransactionId -ReadNewTransactionId(void) +FullTransactionId +ReadNextFullTransactionId(void) { - TransactionId xid; + FullTransactionId fullXid; LWLockAcquire(XidGenLock, LW_SHARED); - xid = ShmemVariableCache->nextXid; + fullXid = ShmemVariableCache->nextFullXid; LWLockRelease(XidGenLock); - return xid; + return fullXid; +} + +/* + * Advance nextFullXid to the value after a given xid. The epoch is inferred. + * If lock_free_check is true, then the caller must be sure that it's safe to + * read nextFullXid without holding XidGenLock (ie during recovery). + */ +void +AdvanceNextFullTransactionIdPast(TransactionId xid, bool lock_free_check) +{ + TransactionId current_xid; + uint32 epoch; + + if (lock_free_check && + !TransactionIdFollowsOrEquals(xid, + XidFromFullTransactionId(ShmemVariableCache->nextFullXid))) + return; + + LWLockAcquire(XidGenLock, LW_EXCLUSIVE); + current_xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); + if (TransactionIdFollowsOrEquals(xid, current_xid)) + { + epoch = EpochFromFullTransactionId(ShmemVariableCache->nextFullXid); + if (xid < current_xid) + ++epoch; /* epoch wrapped */ + ShmemVariableCache->nextFullXid = MakeFullTransactionId(epoch, xid); + FullTransactionIdAdvance(&ShmemVariableCache->nextFullXid); + } + LWLockRelease(XidGenLock); } /* @@ -359,7 +388,7 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid) ShmemVariableCache->xidStopLimit = xidStopLimit; ShmemVariableCache->xidWrapLimit = xidWrapLimit; ShmemVariableCache->oldestXidDB = oldest_datoid; - curXid = ShmemVariableCache->nextXid; + curXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); LWLockRelease(XidGenLock); /* Log the info */ @@ -435,7 +464,7 @@ ForceTransactionIdLimitUpdate(void) /* Locking is probably not really necessary, but let's be careful */ LWLockAcquire(XidGenLock, LW_SHARED); - nextXid = ShmemVariableCache->nextXid; + nextXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); xidVacLimit = ShmemVariableCache->xidVacLimit; oldestXid = ShmemVariableCache->oldestXid; oldestXidDB = ShmemVariableCache->oldestXidDB; diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 1da1f13ef33..6ccccc760d5 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -468,7 +468,7 @@ GetStableLatestTransactionId(void) lxid = MyProc->lxid; stablexid = GetTopTransactionIdIfAny(); if (!TransactionIdIsValid(stablexid)) - stablexid = ReadNewTransactionId(); + stablexid = XidFromFullTransactionId(ReadNextFullTransactionId()); } Assert(TransactionIdIsValid(stablexid)); @@ -5529,14 +5529,7 @@ xact_redo_commit(xl_xact_parsed_commit *parsed, * hold a lock while checking this. We still acquire the lock to modify * it, though. */ - if (TransactionIdFollowsOrEquals(max_xid, - ShmemVariableCache->nextXid)) - { - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = max_xid; - TransactionIdAdvance(ShmemVariableCache->nextXid); - LWLockRelease(XidGenLock); - } + AdvanceNextFullTransactionIdPast(max_xid, true); Assert(((parsed->xinfo & XACT_XINFO_HAS_ORIGIN) == 0) == (origin_id == InvalidRepOriginId)); @@ -5688,15 +5681,7 @@ xact_redo_abort(xl_xact_parsed_abort *parsed, TransactionId xid) max_xid = TransactionIdLatest(xid, parsed->nsubxacts, parsed->subxacts); - - if (TransactionIdFollowsOrEquals(max_xid, - ShmemVariableCache->nextXid)) - { - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = max_xid; - TransactionIdAdvance(ShmemVariableCache->nextXid); - LWLockRelease(XidGenLock); - } + AdvanceNextFullTransactionIdPast(max_xid, true); if (standbyState == STANDBY_DISABLED) { diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 20b23cb3609..101a03d7c45 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -579,8 +579,7 @@ typedef struct XLogCtlData /* Protected by info_lck: */ XLogwrtRqst LogwrtRqst; XLogRecPtr RedoRecPtr; /* a recent copy of Insert->RedoRecPtr */ - uint32 ckptXidEpoch; /* nextXID & epoch of latest checkpoint */ - TransactionId ckptXid; + FullTransactionId ckptFullXid; /* nextXID & epoch of latest checkpoint */ XLogRecPtr asyncXactLSN; /* LSN of newest async commit/abort */ XLogRecPtr replicationSlotMinLSN; /* oldest LSN needed by any slot */ @@ -5102,8 +5101,7 @@ BootStrapXLOG(void) checkPoint.ThisTimeLineID = ThisTimeLineID; checkPoint.PrevTimeLineID = ThisTimeLineID; checkPoint.fullPageWrites = fullPageWrites; - checkPoint.nextXidEpoch = 0; - checkPoint.nextXid = FirstNormalTransactionId; + checkPoint.nextFullXid = MakeFullTransactionId(0, FirstNormalTransactionId); checkPoint.nextOid = FirstBootstrapObjectId; checkPoint.nextMulti = FirstMultiXactId; checkPoint.nextMultiOffset = 0; @@ -5116,7 +5114,7 @@ BootStrapXLOG(void) checkPoint.time = (pg_time_t) time(NULL); checkPoint.oldestActiveXid = InvalidTransactionId; - ShmemVariableCache->nextXid = checkPoint.nextXid; + ShmemVariableCache->nextFullXid = checkPoint.nextFullXid; ShmemVariableCache->nextOid = checkPoint.nextOid; ShmemVariableCache->oidCount = 0; MultiXactSetNextMXact(checkPoint.nextMulti, checkPoint.nextMultiOffset); @@ -6699,8 +6697,8 @@ StartupXLOG(void) (uint32) (checkPoint.redo >> 32), (uint32) checkPoint.redo, wasShutdown ? "true" : "false"))); ereport(DEBUG1, - (errmsg_internal("next transaction ID: %u:%u; next OID: %u", - checkPoint.nextXidEpoch, checkPoint.nextXid, + (errmsg_internal("next transaction ID: " UINT64_FORMAT "; next OID: %u", + U64FromFullTransactionId(checkPoint.nextFullXid), checkPoint.nextOid))); ereport(DEBUG1, (errmsg_internal("next MultiXactId: %u; next MultiXactOffset: %u", @@ -6715,12 +6713,12 @@ StartupXLOG(void) (errmsg_internal("commit timestamp Xid oldest/newest: %u/%u", checkPoint.oldestCommitTsXid, checkPoint.newestCommitTsXid))); - if (!TransactionIdIsNormal(checkPoint.nextXid)) + if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextFullXid))) ereport(PANIC, (errmsg("invalid next transaction ID"))); /* initialize shared memory variables from the checkpoint record */ - ShmemVariableCache->nextXid = checkPoint.nextXid; + ShmemVariableCache->nextFullXid = checkPoint.nextFullXid; ShmemVariableCache->nextOid = checkPoint.nextOid; ShmemVariableCache->oidCount = 0; MultiXactSetNextMXact(checkPoint.nextMulti, checkPoint.nextMultiOffset); @@ -6729,8 +6727,7 @@ StartupXLOG(void) SetMultiXactIdLimit(checkPoint.oldestMulti, checkPoint.oldestMultiDB, true); SetCommitTsLimit(checkPoint.oldestCommitTsXid, checkPoint.newestCommitTsXid); - XLogCtl->ckptXidEpoch = checkPoint.nextXidEpoch; - XLogCtl->ckptXid = checkPoint.nextXid; + XLogCtl->ckptFullXid = checkPoint.nextFullXid; /* * Initialize replication slots, before there's a chance to remove @@ -7000,7 +6997,7 @@ StartupXLOG(void) Assert(TransactionIdIsValid(oldestActiveXID)); /* Tell procarray about the range of xids it has to deal with */ - ProcArrayInitRecovery(ShmemVariableCache->nextXid); + ProcArrayInitRecovery(XidFromFullTransactionId(ShmemVariableCache->nextFullXid)); /* * Startup commit log and subtrans only. MultiXact and commit @@ -7030,9 +7027,9 @@ StartupXLOG(void) running.xcnt = nxids; running.subxcnt = 0; running.subxid_overflow = false; - running.nextXid = checkPoint.nextXid; + running.nextXid = XidFromFullTransactionId(checkPoint.nextFullXid); running.oldestRunningXid = oldestActiveXID; - latestCompletedXid = checkPoint.nextXid; + latestCompletedXid = XidFromFullTransactionId(checkPoint.nextFullXid); TransactionIdRetreat(latestCompletedXid); Assert(TransactionIdIsNormal(latestCompletedXid)); running.latestCompletedXid = latestCompletedXid; @@ -7208,14 +7205,7 @@ StartupXLOG(void) * don't need to hold a lock while examining it. We still * acquire the lock to modify it, though. */ - if (TransactionIdFollowsOrEquals(record->xl_xid, - ShmemVariableCache->nextXid)) - { - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = record->xl_xid; - TransactionIdAdvance(ShmemVariableCache->nextXid); - LWLockRelease(XidGenLock); - } + AdvanceNextFullTransactionIdPast(record->xl_xid, true); /* * Before replaying this record, check if this record causes @@ -7782,7 +7772,7 @@ StartupXLOG(void) /* also initialize latestCompletedXid, to nextXid - 1 */ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); - ShmemVariableCache->latestCompletedXid = ShmemVariableCache->nextXid; + ShmemVariableCache->latestCompletedXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); TransactionIdRetreat(ShmemVariableCache->latestCompletedXid); LWLockRelease(ProcArrayLock); @@ -8375,41 +8365,6 @@ GetLastSegSwitchData(XLogRecPtr *lastSwitchLSN) return result; } -/* - * GetNextXidAndEpoch - get the current nextXid value and associated epoch - * - * This is exported for use by code that would like to have 64-bit XIDs. - * We don't really support such things, but all XIDs within the system - * can be presumed "close to" the result, and thus the epoch associated - * with them can be determined. - */ -void -GetNextXidAndEpoch(TransactionId *xid, uint32 *epoch) -{ - uint32 ckptXidEpoch; - TransactionId ckptXid; - TransactionId nextXid; - - /* Must read checkpoint info first, else have race condition */ - SpinLockAcquire(&XLogCtl->info_lck); - ckptXidEpoch = XLogCtl->ckptXidEpoch; - ckptXid = XLogCtl->ckptXid; - SpinLockRelease(&XLogCtl->info_lck); - - /* Now fetch current nextXid */ - nextXid = ReadNewTransactionId(); - - /* - * nextXid is certainly logically later than ckptXid. So if it's - * numerically less, it must have wrapped into the next epoch. - */ - if (nextXid < ckptXid) - ckptXidEpoch++; - - *xid = nextXid; - *epoch = ckptXidEpoch; -} - /* * This must be called ONCE during postmaster or standalone-backend shutdown */ @@ -8819,7 +8774,7 @@ CreateCheckPoint(int flags) * there. */ LWLockAcquire(XidGenLock, LW_SHARED); - checkPoint.nextXid = ShmemVariableCache->nextXid; + checkPoint.nextFullXid = ShmemVariableCache->nextFullXid; checkPoint.oldestXid = ShmemVariableCache->oldestXid; checkPoint.oldestXidDB = ShmemVariableCache->oldestXidDB; LWLockRelease(XidGenLock); @@ -8829,11 +8784,6 @@ CreateCheckPoint(int flags) checkPoint.newestCommitTsXid = ShmemVariableCache->newestCommitTsXid; LWLockRelease(CommitTsLock); - /* Increase XID epoch if we've wrapped around since last checkpoint */ - checkPoint.nextXidEpoch = ControlFile->checkPointCopy.nextXidEpoch; - if (checkPoint.nextXid < ControlFile->checkPointCopy.nextXid) - checkPoint.nextXidEpoch++; - LWLockAcquire(OidGenLock, LW_SHARED); checkPoint.nextOid = ShmemVariableCache->nextOid; if (!shutdown) @@ -8977,8 +8927,7 @@ CreateCheckPoint(int flags) /* Update shared-memory copy of checkpoint XID/epoch */ SpinLockAcquire(&XLogCtl->info_lck); - XLogCtl->ckptXidEpoch = checkPoint.nextXidEpoch; - XLogCtl->ckptXid = checkPoint.nextXid; + XLogCtl->ckptFullXid = checkPoint.nextFullXid; SpinLockRelease(&XLogCtl->info_lck); /* @@ -9733,7 +9682,7 @@ xlog_redo(XLogReaderState *record) memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint)); /* In a SHUTDOWN checkpoint, believe the counters exactly */ LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = checkPoint.nextXid; + ShmemVariableCache->nextFullXid = checkPoint.nextFullXid; LWLockRelease(XidGenLock); LWLockAcquire(OidGenLock, LW_EXCLUSIVE); ShmemVariableCache->nextOid = checkPoint.nextOid; @@ -9787,9 +9736,9 @@ xlog_redo(XLogReaderState *record) running.xcnt = nxids; running.subxcnt = 0; running.subxid_overflow = false; - running.nextXid = checkPoint.nextXid; + running.nextXid = XidFromFullTransactionId(checkPoint.nextFullXid); running.oldestRunningXid = oldestActiveXID; - latestCompletedXid = checkPoint.nextXid; + latestCompletedXid = XidFromFullTransactionId(checkPoint.nextFullXid); TransactionIdRetreat(latestCompletedXid); Assert(TransactionIdIsNormal(latestCompletedXid)); running.latestCompletedXid = latestCompletedXid; @@ -9801,13 +9750,11 @@ xlog_redo(XLogReaderState *record) } /* ControlFile->checkPointCopy always tracks the latest ckpt XID */ - ControlFile->checkPointCopy.nextXidEpoch = checkPoint.nextXidEpoch; - ControlFile->checkPointCopy.nextXid = checkPoint.nextXid; + ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid; /* Update shared-memory copy of checkpoint XID/epoch */ SpinLockAcquire(&XLogCtl->info_lck); - XLogCtl->ckptXidEpoch = checkPoint.nextXidEpoch; - XLogCtl->ckptXid = checkPoint.nextXid; + XLogCtl->ckptFullXid = checkPoint.nextFullXid; SpinLockRelease(&XLogCtl->info_lck); /* @@ -9828,9 +9775,9 @@ xlog_redo(XLogReaderState *record) memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint)); /* In an ONLINE checkpoint, treat the XID counter as a minimum */ LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - if (TransactionIdPrecedes(ShmemVariableCache->nextXid, - checkPoint.nextXid)) - ShmemVariableCache->nextXid = checkPoint.nextXid; + if (FullTransactionIdPrecedes(ShmemVariableCache->nextFullXid, + checkPoint.nextFullXid)) + ShmemVariableCache->nextFullXid = checkPoint.nextFullXid; LWLockRelease(XidGenLock); /* @@ -9860,13 +9807,11 @@ xlog_redo(XLogReaderState *record) SetTransactionIdLimit(checkPoint.oldestXid, checkPoint.oldestXidDB); /* ControlFile->checkPointCopy always tracks the latest ckpt XID */ - ControlFile->checkPointCopy.nextXidEpoch = checkPoint.nextXidEpoch; - ControlFile->checkPointCopy.nextXid = checkPoint.nextXid; + ControlFile->checkPointCopy.nextFullXid = checkPoint.nextFullXid; /* Update shared-memory copy of checkpoint XID/epoch */ SpinLockAcquire(&XLogCtl->info_lck); - XLogCtl->ckptXidEpoch = checkPoint.nextXidEpoch; - XLogCtl->ckptXid = checkPoint.nextXid; + XLogCtl->ckptFullXid = checkPoint.nextFullXid; SpinLockRelease(&XLogCtl->info_lck); /* TLI should not change in an on-line checkpoint */ diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index d90cb9a9022..33f0d7ad299 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -647,7 +647,7 @@ vacuum_set_xid_limits(Relation rel, * autovacuum_freeze_max_age / 2 XIDs old), complain and force a minimum * freeze age of zero. */ - safeLimit = ReadNewTransactionId() - autovacuum_freeze_max_age; + safeLimit = XidFromFullTransactionId(ReadNextFullTransactionId()) - autovacuum_freeze_max_age; if (!TransactionIdIsNormal(safeLimit)) safeLimit = FirstNormalTransactionId; @@ -725,7 +725,7 @@ vacuum_set_xid_limits(Relation rel, * Compute XID limit causing a full-table vacuum, being careful not to * generate a "permanent" XID. */ - limit = ReadNewTransactionId() - freezetable; + limit = XidFromFullTransactionId(ReadNextFullTransactionId()) - freezetable; if (!TransactionIdIsNormal(limit)) limit = FirstNormalTransactionId; @@ -944,7 +944,7 @@ vac_update_relstats(Relation relation, if (TransactionIdIsNormal(frozenxid) && pgcform->relfrozenxid != frozenxid && (TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid) || - TransactionIdPrecedes(ReadNewTransactionId(), + TransactionIdPrecedes(XidFromFullTransactionId(ReadNextFullTransactionId()), pgcform->relfrozenxid))) { pgcform->relfrozenxid = frozenxid; @@ -1021,7 +1021,7 @@ vac_update_datfrozenxid(void) * validly see during the scan. These are conservative values, but it's * not really worth trying to be more exact. */ - lastSaneFrozenXid = ReadNewTransactionId(); + lastSaneFrozenXid = XidFromFullTransactionId(ReadNextFullTransactionId()); lastSaneMinMulti = ReadNextMultiXactId(); /* @@ -1157,7 +1157,7 @@ vac_truncate_clog(TransactionId frozenXID, TransactionId lastSaneFrozenXid, MultiXactId lastSaneMinMulti) { - TransactionId nextXID = ReadNewTransactionId(); + TransactionId nextXID = XidFromFullTransactionId(ReadNextFullTransactionId()); Relation relation; HeapScanDesc scan; HeapTuple tuple; diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 02e6d8131e0..3a07a0c3530 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -1172,7 +1172,7 @@ do_start_worker(void) * pass without forcing a vacuum. (This limit can be tightened for * particular tables, but not loosened.) */ - recentXid = ReadNewTransactionId(); + recentXid = XidFromFullTransactionId(ReadNextFullTransactionId()); xidForceLimit = recentXid - autovacuum_freeze_max_age; /* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */ /* this can cause the limit to go backwards by 3, but that's OK */ @@ -1703,7 +1703,7 @@ AutoVacWorkerMain(int argc, char *argv[]) pg_usleep(PostAuthDelay * 1000000L); /* And do an appropriate amount of work */ - recentXid = ReadNewTransactionId(); + recentXid = XidFromFullTransactionId(ReadNextFullTransactionId()); recentMulti = ReadNextMultiXactId(); do_autovacuum(); } diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c index 987bb84683c..6a11f8a06c1 100644 --- a/src/backend/replication/walreceiver.c +++ b/src/backend/replication/walreceiver.c @@ -1194,6 +1194,7 @@ static void XLogWalRcvSendHSFeedback(bool immed) { TimestampTz now; + FullTransactionId nextFullXid; TransactionId nextXid; uint32 xmin_epoch, catalog_xmin_epoch; @@ -1272,7 +1273,9 @@ XLogWalRcvSendHSFeedback(bool immed) * Get epoch and adjust if nextXid and oldestXmin are different sides of * the epoch boundary. */ - GetNextXidAndEpoch(&nextXid, &xmin_epoch); + nextFullXid = ReadNextFullTransactionId(); + nextXid = XidFromFullTransactionId(nextFullXid); + xmin_epoch = EpochFromFullTransactionId(nextFullXid); catalog_xmin_epoch = xmin_epoch; if (nextXid < xmin) xmin_epoch--; diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index e47ddca6bca..6903dbc9ca1 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -1888,35 +1888,20 @@ PhysicalReplicationSlotNewXmin(TransactionId feedbackXmin, TransactionId feedbac * Check that the provided xmin/epoch are sane, that is, not in the future * and not so far back as to be already wrapped around. * - * Epoch of nextXid should be same as standby, or if the counter has - * wrapped, then one greater than standby. - * * This check doesn't care about whether clog exists for these xids * at all. */ static bool TransactionIdInRecentPast(TransactionId xid, uint32 epoch) { - TransactionId nextXid; - uint32 nextEpoch; - - GetNextXidAndEpoch(&nextXid, &nextEpoch); - - if (xid <= nextXid) - { - if (epoch != nextEpoch) - return false; - } - else - { - if (epoch + 1 != nextEpoch) - return false; - } - - if (!TransactionIdPrecedesOrEquals(xid, nextXid)) - return false; /* epoch OK, but it's wrapped around */ - - return true; + FullTransactionId nextFullXid = ReadNextFullTransactionId(); + FullTransactionId fullXid = MakeFullTransactionId(epoch, xid); + + /* TODO: this is not nice */ + return + FullTransactionIdPrecedesOrEquals(fullXid, nextFullXid) && + U64FromFullTransactionId(nextFullXid) - + U64FromFullTransactionId(fullXid) < INT64CONST(1) << 32; } /* diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index bd20497d81a..0bf2a11e931 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -883,15 +883,10 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running) * it, though. */ nextXid = latestObservedXid; + AdvanceNextFullTransactionIdPast(nextXid, true); TransactionIdAdvance(nextXid); - if (TransactionIdFollows(nextXid, ShmemVariableCache->nextXid)) - { - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = nextXid; - LWLockRelease(XidGenLock); - } - Assert(TransactionIdIsValid(ShmemVariableCache->nextXid)); + Assert(TransactionIdIsValid(XidFromFullTransactionId(ShmemVariableCache->nextFullXid))); KnownAssignedXidsDisplay(trace_recovery(DEBUG3)); if (standbyState == STANDBY_SNAPSHOT_READY) @@ -1979,7 +1974,7 @@ GetRunningTransactionData(void) latestCompletedXid = ShmemVariableCache->latestCompletedXid; - oldestRunningXid = ShmemVariableCache->nextXid; + oldestRunningXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); /* * Spin over procArray collecting all xids @@ -2068,7 +2063,7 @@ GetRunningTransactionData(void) CurrentRunningXacts->xcnt = count - subcount; CurrentRunningXacts->subxcnt = subcount; CurrentRunningXacts->subxid_overflow = suboverflowed; - CurrentRunningXacts->nextXid = ShmemVariableCache->nextXid; + CurrentRunningXacts->nextXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); CurrentRunningXacts->oldestRunningXid = oldestRunningXid; CurrentRunningXacts->latestCompletedXid = latestCompletedXid; @@ -2113,7 +2108,7 @@ GetOldestActiveTransactionId(void) * have already completed), when we spin over it. */ LWLockAcquire(XidGenLock, LW_SHARED); - oldestRunningXid = ShmemVariableCache->nextXid; + oldestRunningXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); LWLockRelease(XidGenLock); /* @@ -2181,7 +2176,7 @@ GetOldestSafeDecodingTransactionId(bool catalogOnly) * a safe, albeit pessimal, value. */ LWLockAcquire(XidGenLock, LW_SHARED); - oldestSafeXid = ShmemVariableCache->nextXid; + oldestSafeXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); /* * If there's already a slot pegging the xmin horizon, we can start with @@ -3237,10 +3232,8 @@ RecordKnownAssignedTransactionIds(TransactionId xid) /* ShmemVariableCache->nextXid must be beyond any observed xid */ next_expected_xid = latestObservedXid; + AdvanceNextFullTransactionIdPast(next_expected_xid, false); TransactionIdAdvance(next_expected_xid); - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = next_expected_xid; - LWLockRelease(XidGenLock); } } diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index e8390311d03..880e6c14ef1 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -3270,7 +3270,7 @@ ReleasePredicateLocks(bool isCommit) * transaction to complete before freeing some RAM; correctness of visible * behavior is not affected. */ - MySerializableXact->finishedBefore = ShmemVariableCache->nextXid; + MySerializableXact->finishedBefore = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); /* * If it's not a commit it's a rollback, and we can clear our locks diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c index 7974c0bd3d8..4c34e215d26 100644 --- a/src/backend/utils/adt/txid.c +++ b/src/backend/utils/adt/txid.c @@ -91,7 +91,10 @@ typedef struct static void load_xid_epoch(TxidEpoch *state) { - GetNextXidAndEpoch(&state->last_xid, &state->epoch); + FullTransactionId fullXid = ReadNextFullTransactionId(); + + state->last_xid = XidFromFullTransactionId(fullXid); + state->epoch = EpochFromFullTransactionId(fullXid); } /* @@ -114,8 +117,11 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid) TransactionId xid = (TransactionId) xid_with_epoch; uint32 now_epoch; TransactionId now_epoch_last_xid; + FullTransactionId now_xid; - GetNextXidAndEpoch(&now_epoch_last_xid, &now_epoch); + now_xid = ReadNextFullTransactionId(); + now_epoch_last_xid = XidFromFullTransactionId(now_xid); + now_epoch = EpochFromFullTransactionId(now_xid); if (extracted_xid != NULL) *extracted_xid = xid; @@ -128,8 +134,7 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid) return true; /* If the transaction ID is in the future, throw an error. */ - if (xid_epoch > now_epoch - || (xid_epoch == now_epoch && xid > now_epoch_last_xid)) + if (xid_with_epoch > U64FromFullTransactionId(now_xid)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transaction ID %s is in the future", diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c index 3fc8b6a8a84..51a200544e5 100644 --- a/src/backend/utils/misc/pg_controldata.c +++ b/src/backend/utils/misc/pg_controldata.c @@ -16,6 +16,7 @@ #include "postgres.h" #include "access/htup_details.h" +#include "access/transam.h" #include "access/xlog_internal.h" #include "access/xlog.h" #include "catalog/pg_control.h" @@ -163,9 +164,8 @@ pg_control_checkpoint(PG_FUNCTION_ARGS) values[5] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites); nulls[5] = false; - values[6] = CStringGetTextDatum(psprintf("%u:%u", - ControlFile->checkPointCopy.nextXidEpoch, - ControlFile->checkPointCopy.nextXid)); + values[6] = CStringGetTextDatum(psprintf(UINT64_FORMAT, + U64FromFullTransactionId(ControlFile->checkPointCopy.nextFullXid))); nulls[6] = false; values[7] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid); diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 895a51f89d5..a1c55e0c0d5 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -20,6 +20,7 @@ #include +#include "access/transam.h" #include "access/xlog.h" #include "access/xlog_internal.h" #include "catalog/pg_control.h" @@ -255,9 +256,8 @@ main(int argc, char *argv[]) ControlFile->checkPointCopy.PrevTimeLineID); printf(_("Latest checkpoint's full_page_writes: %s\n"), ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off")); - printf(_("Latest checkpoint's NextXID: %u:%u\n"), - ControlFile->checkPointCopy.nextXidEpoch, - ControlFile->checkPointCopy.nextXid); + printf(_("Latest checkpoint's NextXID: " UINT64_FORMAT "\n"), + U64FromFullTransactionId(ControlFile->checkPointCopy.nextFullXid)); printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile->checkPointCopy.nextOid); printf(_("Latest checkpoint's NextMultiXactId: %u\n"), diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index 8cff5356925..3fb15789d62 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -431,11 +431,15 @@ main(int argc, char *argv[]) * if any, includes these values.) */ if (set_xid_epoch != -1) - ControlFile.checkPointCopy.nextXidEpoch = set_xid_epoch; + ControlFile.checkPointCopy.nextFullXid = + MakeFullTransactionId(set_xid_epoch, + XidFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid)); if (set_xid != 0) { - ControlFile.checkPointCopy.nextXid = set_xid; + ControlFile.checkPointCopy.nextFullXid = + MakeFullTransactionId(EpochFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid), + set_xid); /* * For the moment, just set oldestXid to a value that will force @@ -705,8 +709,7 @@ GuessControlValues(void) ControlFile.checkPointCopy.ThisTimeLineID = 1; ControlFile.checkPointCopy.PrevTimeLineID = 1; ControlFile.checkPointCopy.fullPageWrites = false; - ControlFile.checkPointCopy.nextXidEpoch = 0; - ControlFile.checkPointCopy.nextXid = FirstNormalTransactionId; + ControlFile.checkPointCopy.nextFullXid = MakeFullTransactionId(0, FirstNormalTransactionId); ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId; ControlFile.checkPointCopy.nextMulti = FirstMultiXactId; ControlFile.checkPointCopy.nextMultiOffset = 0; @@ -785,9 +788,8 @@ PrintControlValues(bool guessed) ControlFile.checkPointCopy.ThisTimeLineID); printf(_("Latest checkpoint's full_page_writes: %s\n"), ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off")); - printf(_("Latest checkpoint's NextXID: %u:%u\n"), - ControlFile.checkPointCopy.nextXidEpoch, - ControlFile.checkPointCopy.nextXid); + printf(_("Latest checkpoint's NextXID: " UINT64_FORMAT "\n"), + U64FromFullTransactionId(ControlFile.checkPointCopy.nextFullXid)); printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile.checkPointCopy.nextOid); printf(_("Latest checkpoint's NextMultiXactId: %u\n"), @@ -879,7 +881,7 @@ PrintNewControlValues(void) if (set_xid != 0) { printf(_("NextXID: %u\n"), - ControlFile.checkPointCopy.nextXid); + XidFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid)); printf(_("OldestXID: %u\n"), ControlFile.checkPointCopy.oldestXid); printf(_("OldestXID's DB: %u\n"), @@ -889,7 +891,7 @@ PrintNewControlValues(void) if (set_xid_epoch != -1) { printf(_("NextXID epoch: %u\n"), - ControlFile.checkPointCopy.nextXidEpoch); + EpochFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid)); } if (set_oldest_commit_ts_xid != 0) diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 83ec3f19797..070f3bfdc74 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -44,6 +44,32 @@ #define TransactionIdStore(xid, dest) (*(dest) = (xid)) #define StoreInvalidTransactionId(dest) (*(dest) = InvalidTransactionId) +#define EpochFromFullTransactionId(x) ((uint32) ((x).value >> 32)) +#define XidFromFullTransactionId(x) ((uint32) (x).value) +#define U64FromFullTransactionId(x) ((x).value) +#define FullTransactionIdPrecedes(a, b) ((a).value < (b).value) +#define FullTransactionIdPrecedesOrEquals(a, b) ((a).value <= (b).value) + +/* + * A 64 bit value that contains an epoch and a TransactionId. This is + * wrapped in a struct to prevent implicit conversion to/from TransactionId. + * Allowing such conversions seems likely to be error-prone. + */ +typedef struct FullTransactionId +{ + uint64 value; +} FullTransactionId; + +static inline FullTransactionId +MakeFullTransactionId(uint32 epoch, TransactionId xid) +{ + FullTransactionId result; + + result.value = ((uint64) epoch) << 32 | xid; + + return result; +} + /* advance a transaction ID variable, handling wraparound correctly */ #define TransactionIdAdvance(dest) \ do { \ @@ -52,6 +78,16 @@ (dest) = FirstNormalTransactionId; \ } while(0) +/* advance a FullTransactionId lvalue, handling wraparound correctly */ +static inline void +FullTransactionIdAdvance(FullTransactionId *dest) +{ + dest->value++; + if (XidFromFullTransactionId(*dest) < FirstNormalTransactionId) + *dest = MakeFullTransactionId(EpochFromFullTransactionId(*dest), + FirstNormalTransactionId); +} + /* back up a transaction ID variable, handling wraparound correctly */ #define TransactionIdRetreat(dest) \ do { \ @@ -114,12 +150,12 @@ typedef struct VariableCacheData /* * These fields are protected by XidGenLock. */ - TransactionId nextXid; /* next XID to assign */ + FullTransactionId nextFullXid; /* next XID to assign */ TransactionId oldestXid; /* cluster-wide minimum datfrozenxid */ TransactionId xidVacLimit; /* start forcing autovacuums here */ TransactionId xidWarnLimit; /* start complaining here */ - TransactionId xidStopLimit; /* refuse to advance nextXid beyond here */ + TransactionId xidStopLimit; /* refuse to advance nextFullXid beyond here */ TransactionId xidWrapLimit; /* where the world ends */ Oid oldestXidDB; /* database with minimum datfrozenxid */ @@ -176,7 +212,8 @@ extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid); /* in transam/varsup.c */ extern TransactionId GetNewTransactionId(bool isSubXact); -extern TransactionId ReadNewTransactionId(void); +extern void AdvanceNextFullTransactionIdPast(TransactionId xid, bool lock_free_check); +extern FullTransactionId ReadNextFullTransactionId(void); extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid); extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid); diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 421ba6d7755..3c9d3401df5 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -276,7 +276,6 @@ extern XLogRecPtr GetRedoRecPtr(void); extern XLogRecPtr GetInsertRecPtr(void); extern XLogRecPtr GetFlushRecPtr(void); extern XLogRecPtr GetLastImportantRecPtr(void); -extern void GetNextXidAndEpoch(TransactionId *xid, uint32 *epoch); extern void RemovePromoteSignalFiles(void); extern bool CheckPromoteSignal(void); diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index 773d9e6ebae..fa7ff049403 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -15,6 +15,7 @@ #ifndef PG_CONTROL_H #define PG_CONTROL_H +#include "access/transam.h" #include "access/xlogdefs.h" #include "pgtime.h" /* for pg_time_t */ #include "port/pg_crc32c.h" @@ -39,8 +40,7 @@ typedef struct CheckPoint TimeLineID PrevTimeLineID; /* previous TLI, if this record begins a new * timeline (equals ThisTimeLineID otherwise) */ bool fullPageWrites; /* current full_page_writes */ - uint32 nextXidEpoch; /* higher-order bits of nextXid */ - TransactionId nextXid; /* next free XID */ + FullTransactionId nextFullXid; /* next free XID */ Oid nextOid; /* next free OID */ MultiXactId nextMulti; /* next free MultiXactId */ MultiXactOffset nextMultiOffset; /* next free MultiXact offset */ diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h index 1fcd8cf1b59..d1116454095 100644 --- a/src/include/storage/standby.h +++ b/src/include/storage/standby.h @@ -72,7 +72,7 @@ typedef struct RunningTransactionsData int xcnt; /* # of xact ids in xids[] */ int subxcnt; /* # of subxact ids in xids[] */ bool subxid_overflow; /* snapshot overflowed, subxids missing */ - TransactionId nextXid; /* copy of ShmemVariableCache->nextXid */ + TransactionId nextXid; /* xid from ShmemVariableCache->nextFullXid */ TransactionId oldestRunningXid; /* *not* oldestXmin */ TransactionId latestCompletedXid; /* so we can set xmax */ -- 2.17.0