On Mon, Mar 25, 2019 at 5:01 PM Thomas Munro <thomas.mu...@gmail.com> wrote: > New version attached. I'd like to commit this for PG12.
Here is a follow-up sketch patch that shows FullTransactionId being used in the transaction stack, so you can call eg GetCurrentFullTransactionId(). A table access method could use this to avoid the need to freeze stuff later (eg zheap). I suppose it's not strictly necessary, since you could use GetCurrentTransactionId() and infer the epoch by comparing with ReadNextFullTransactionId() (now that the epoch counting is reliable, due to patch 0001 which I'm repeating again here just for cfbot). But I suppose we want to get away from that sort of thing. Thoughts? -- Thomas Munro https://enterprisedb.com
From 1f513f48a603c663753ddb5fa1d8e62abf500db9 Mon Sep 17 00:00:00 2001 From: Thomas Munro <thomas.munro@gmail.com> Date: Mon, 25 Mar 2019 12:29:23 +1300 Subject: [PATCH 1/2] Track the next transaction ID using 64 bits. Instead of inferring epoch advancement from xids and checkpoints, introduce a 64 bit FullTransactionId type and use it to track xid generation. This fixes an unlikely bug where epoch information is corrupted if you somehow manage to make TransactionId wrap around more than once between checkpoints. This commit creates some very basic infrastructure allowing for later patches to adopt 64 bit transaction IDs in more places, potentially including table access methods that don't need to freeze tables. Author: Thomas Munro Reported-by: Amit Kapila Reviewed-by: Andres Freund, Tom Lane Discussion: https://postgr.es/m/CAA4eK1%2BMv%2Bmb0HFfWM9Srtc6MVe160WFurXV68iAFMcagRZ0dQ%40mail.gmail.com --- src/backend/access/rmgrdesc/xlogdesc.c | 4 +- src/backend/access/transam/clog.c | 8 +- src/backend/access/transam/commit_ts.c | 4 +- src/backend/access/transam/multixact.c | 20 +---- src/backend/access/transam/subtrans.c | 8 +- src/backend/access/transam/twophase.c | 40 +++------ src/backend/access/transam/varsup.c | 76 ++++++++++++---- src/backend/access/transam/xact.c | 35 +------- src/backend/access/transam/xlog.c | 113 ++++++------------------ src/backend/replication/walreceiver.c | 5 +- src/backend/replication/walsender.c | 5 +- src/backend/storage/ipc/procarray.c | 34 ++----- src/backend/storage/ipc/standby.c | 2 +- src/backend/storage/lmgr/predicate.c | 2 +- src/backend/utils/adt/txid.c | 13 ++- src/backend/utils/misc/pg_controldata.c | 5 +- src/bin/pg_controldata/pg_controldata.c | 5 +- src/bin/pg_resetwal/pg_resetwal.c | 20 +++-- src/include/access/transam.h | 52 ++++++++++- src/include/access/xlog.h | 1 - src/include/catalog/pg_control.h | 6 +- src/include/storage/standby.h | 2 +- src/include/storage/standbydefs.h | 2 +- src/tools/pgindent/typedefs.list | 1 + 24 files changed, 225 insertions(+), 238 deletions(-) diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c index bfad284be08..33060f30429 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" @@ -52,7 +53,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record) checkpoint->ThisTimeLineID, checkpoint->PrevTimeLineID, checkpoint->fullPageWrites ? "true" : "false", - checkpoint->nextXidEpoch, checkpoint->nextXid, + EpochFromFullTransactionId(checkpoint->nextFullXid), + XidFromFullTransactionId(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 aa089d83fa8..3bd55fbdd33 100644 --- a/src/backend/access/transam/clog.c +++ b/src/backend/access/transam/clog.c @@ -749,12 +749,12 @@ ZeroCLOGPage(int pageno, bool writeXlog) /* * This must be called ONCE during postmaster or standalone-backend startup, - * after StartupXLOG has initialized ShmemVariableCache->nextXid. + * after StartupXLOG has initialized ShmemVariableCache->nextFullXid. */ 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); @@ -792,7 +792,7 @@ TrimCLOG(void) * but makes no WAL entry). Let's just be safe. (We need not worry about * pages beyond the current one, since those will be zeroed when first * used. For the same reason, there is no need to do anything when - * nextXid is exactly at a page boundary; and it's likely that the + * nextFullXid is exactly at a page boundary; and it's likely that the * "current" page doesn't exist yet in that case.) */ if (TransactionIdToPgIndex(xid) != 0) diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c index 9d7f15935dc..8162f884bd1 100644 --- a/src/backend/access/transam/commit_ts.c +++ b/src/backend/access/transam/commit_ts.c @@ -553,7 +553,7 @@ ZeroCommitTsPage(int pageno, bool writeXlog) /* * This must be called ONCE during postmaster or standalone-backend startup, - * after StartupXLOG has initialized ShmemVariableCache->nextXid. + * after StartupXLOG has initialized ShmemVariableCache->nextFullXid. */ void StartupCommitTs(void) @@ -643,7 +643,7 @@ ActivateCommitTs(void) } LWLockRelease(CommitTsLock); - xid = ShmemVariableCache->nextXid; + xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); pageno = TransactionIdToCTsPage(xid); /* diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index c3998719405..763b9997071 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -3267,9 +3267,9 @@ multixact_redo(XLogReaderState *record) xlrec->moff + xlrec->nmembers); /* - * Make sure nextXid is beyond any XID mentioned in the record. This - * should be unnecessary, since any XID found here ought to have other - * evidence in the XLOG, but let's be safe. + * Make sure nextFullXid is beyond any XID mentioned in the record. + * This should be unnecessary, since any XID found here ought to have + * other evidence in the XLOG, but let's be safe. */ max_xid = XLogRecGetXid(record); for (i = 0; i < xlrec->nmembers; i++) @@ -3278,19 +3278,7 @@ multixact_redo(XLogReaderState *record) max_xid = xlrec->members[i].xid; } - /* - * We don't expect anyone else to modify nextXid, hence startup - * 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); - } + AdvanceNextFullTransactionIdPastXid(max_xid); } else if (info == XLOG_MULTIXACT_TRUNCATE_ID) { diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c index cbc61294eb9..e667fd02385 100644 --- a/src/backend/access/transam/subtrans.c +++ b/src/backend/access/transam/subtrans.c @@ -241,14 +241,15 @@ ZeroSUBTRANSPage(int pageno) /* * This must be called ONCE during postmaster or standalone-backend startup, - * after StartupXLOG has initialized ShmemVariableCache->nextXid. + * after StartupXLOG has initialized ShmemVariableCache->nextFullXid. * - * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid + * oldestActiveXID is the oldest XID of any prepared transaction, or nextFullXid * if there are none. */ 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 21986e48fe2..11992f7447d 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -1878,16 +1878,16 @@ restoreTwoPhaseData(void) * * Scan the shared memory entries of TwoPhaseState and determine the range * of valid XIDs present. This is run during database startup, after we - * have completed reading WAL. ShmemVariableCache->nextXid has been set to + * have completed reading WAL. ShmemVariableCache->nextFullXid has been set to * one more than the highest XID for which evidence exists in WAL. * - * We throw away any prepared xacts with main XID beyond nextXid --- if any + * We throw away any prepared xacts with main XID beyond nextFullXid --- if any * are present, it suggests that the DBA has done a PITR recovery to an * earlier point in time without cleaning out pg_twophase. We dare not * try to recover such prepared xacts since they likely depend on database * state that doesn't exist now. * - * However, we will advance nextXid beyond any subxact XIDs belonging to + * However, we will advance nextFullXid beyond any subxact XIDs belonging to * valid prepared xacts. We need to do this since subxact commit doesn't * write a WAL entry, and so there might be no evidence in WAL of those * subxact XIDs. @@ -1897,7 +1897,7 @@ restoreTwoPhaseData(void) * backup should be rolled in. * * Our other responsibility is to determine and return the oldest valid XID - * among the prepared xacts (if none, return ShmemVariableCache->nextXid). + * among the prepared xacts (if none, return ShmemVariableCache->nextFullXid). * This is needed to synchronize pg_subtrans startup properly. * * If xids_p and nxids_p are not NULL, pointer to a palloc'd array of all @@ -1907,7 +1907,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; @@ -2123,7 +2124,7 @@ RecoverPreparedTransactions(void) * * If setParent is true, set up subtransaction parent linkages. * - * If setNextXid is true, set ShmemVariableCache->nextXid to the newest + * If setNextXid is true, set ShmemVariableCache->nextFullXid to the newest * value scanned. */ static char * @@ -2132,7 +2133,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; @@ -2212,7 +2214,7 @@ ProcessTwoPhaseBuffer(TransactionId xid, /* * Examine subtransaction XIDs ... they should all follow main XID, and - * they may force us to advance nextXid. + * they may force us to advance nextFullXid. */ subxids = (TransactionId *) (buf + MAXALIGN(sizeof(TwoPhaseFileHeader)) + @@ -2223,25 +2225,9 @@ ProcessTwoPhaseBuffer(TransactionId xid, Assert(TransactionIdFollows(subxid, xid)); - /* update nextXid if needed */ - if (setNextXid && - TransactionIdFollowsOrEquals(subxid, - ShmemVariableCache->nextXid)) - { - /* - * 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); - } + /* update nextFullXid if needed */ + if (setNextXid) + AdvanceNextFullTransactionIdPastXid(subxid); if (setParent) SubTransSetParent(subxid, xid); diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c index fe94fdaf049..db141d286dc 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 - * 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. + * 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 @@ -236,18 +236,64 @@ 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. + * This must only be called during recovery or from two-phase start-up code. + */ +void +AdvanceNextFullTransactionIdPastXid(TransactionId xid) +{ + FullTransactionId newNextFullXid; + TransactionId next_xid; + uint32 epoch; + + /* + * It is safe to read nextFullXid without a lock, because this is only + * called from the startup process or single process mode, meaning that no + * other process can modify it. + */ + Assert(AmStartupProcess() || !IsUnderPostmaster); + + /* Fast return if this isn't an xid high enough to move the needle. */ + next_xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); + if (!TransactionIdFollowsOrEquals(xid, next_xid)) + return; + + /* + * Compute the FullTransactionId that comes after the given xid. To do + * this, we preserve the existing epoch, but detect when we've wrapped + * into a new epoch. This is necessary because WAL records and 2PC state + * currently contain 32 bit xids. The wrap logic is safe in those cases + * because the span of active xids cannot exceed one epoch at any given + * point in the WAL stream. + */ + TransactionIdAdvance(xid); + epoch = EpochFromFullTransactionId(ShmemVariableCache->nextFullXid); + if (unlikely(xid < next_xid)) + ++epoch; + newNextFullXid = FullTransactionIdFromEpochAndXid(epoch, xid); + + /* + * We still need to take a lock to modify the value against concurrent + * readers. + */ + LWLockAcquire(XidGenLock, LW_EXCLUSIVE); + ShmemVariableCache->nextFullXid = newNextFullXid; + LWLockRelease(XidGenLock); } /* @@ -351,7 +397,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 */ @@ -427,7 +473,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 c3214d4f4d8..9b100050597 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -5636,21 +5636,8 @@ xact_redo_commit(xl_xact_parsed_commit *parsed, max_xid = TransactionIdLatest(xid, parsed->nsubxacts, parsed->subxacts); - /* - * Make sure nextXid is beyond any XID mentioned in the record. - * - * We don't expect anyone else to modify nextXid, hence we don'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); - } + /* Make sure nextFullXid is beyond any XID mentioned in the record. */ + AdvanceNextFullTransactionIdPastXid(max_xid); Assert(((parsed->xinfo & XACT_XINFO_HAS_ORIGIN) == 0) == (origin_id == InvalidRepOriginId)); @@ -5792,25 +5779,11 @@ xact_redo_abort(xl_xact_parsed_abort *parsed, TransactionId xid) Assert(TransactionIdIsValid(xid)); - /* - * Make sure nextXid is beyond any XID mentioned in the record. - * - * We don't expect anyone else to modify nextXid, hence we don't need to - * hold a lock while checking this. We still acquire the lock to modify - * it, though. - */ + /* Make sure nextFullXid is beyond any XID mentioned in the record. */ 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); - } + AdvanceNextFullTransactionIdPastXid(max_xid); if (standbyState == STANDBY_DISABLED) { diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index ad12ebc4269..19d7911ec50 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -590,8 +590,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; /* nextFullXid of latest checkpoint */ XLogRecPtr asyncXactLSN; /* LSN of newest async commit/abort */ XLogRecPtr replicationSlotMinLSN; /* oldest LSN needed by any slot */ @@ -5115,8 +5114,8 @@ BootStrapXLOG(void) checkPoint.ThisTimeLineID = ThisTimeLineID; checkPoint.PrevTimeLineID = ThisTimeLineID; checkPoint.fullPageWrites = fullPageWrites; - checkPoint.nextXidEpoch = 0; - checkPoint.nextXid = FirstNormalTransactionId; + checkPoint.nextFullXid = + FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId); checkPoint.nextOid = FirstBootstrapObjectId; checkPoint.nextMulti = FirstMultiXactId; checkPoint.nextMultiOffset = 0; @@ -5129,7 +5128,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); @@ -6557,8 +6556,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", @@ -6573,12 +6572,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); @@ -6587,8 +6586,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 @@ -6859,7 +6857,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 @@ -6889,9 +6887,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; @@ -7061,20 +7059,10 @@ StartupXLOG(void) error_context_stack = &errcallback; /* - * ShmemVariableCache->nextXid must be beyond record's xid. - * - * 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. + * ShmemVariableCache->nextFullXid must be beyond record's + * xid. */ - if (TransactionIdFollowsOrEquals(record->xl_xid, - ShmemVariableCache->nextXid)) - { - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = record->xl_xid; - TransactionIdAdvance(ShmemVariableCache->nextXid); - LWLockRelease(XidGenLock); - } + AdvanceNextFullTransactionIdPastXid(record->xl_xid); /* * Before replaying this record, check if this record causes @@ -7654,7 +7642,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); @@ -8247,41 +8235,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 */ @@ -8701,7 +8654,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); @@ -8711,11 +8664,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) @@ -8859,8 +8807,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); /* @@ -9622,7 +9569,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; @@ -9676,9 +9623,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; @@ -9690,13 +9637,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); /* @@ -9717,9 +9662,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); /* @@ -9749,13 +9694,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/replication/walreceiver.c b/src/backend/replication/walreceiver.c index d9959e568a8..f32cf91ffb3 100644 --- a/src/backend/replication/walreceiver.c +++ b/src/backend/replication/walreceiver.c @@ -1160,6 +1160,7 @@ static void XLogWalRcvSendHSFeedback(bool immed) { TimestampTz now; + FullTransactionId nextFullXid; TransactionId nextXid; uint32 xmin_epoch, catalog_xmin_epoch; @@ -1238,7 +1239,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 4bb98ef352a..21f5c868f18 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -1912,10 +1912,13 @@ PhysicalReplicationSlotNewXmin(TransactionId feedbackXmin, TransactionId feedbac static bool TransactionIdInRecentPast(TransactionId xid, uint32 epoch) { + FullTransactionId nextFullXid; TransactionId nextXid; uint32 nextEpoch; - GetNextXidAndEpoch(&nextXid, &nextEpoch); + nextFullXid = ReadNextFullTransactionId(); + nextXid = XidFromFullTransactionId(nextFullXid); + nextEpoch = EpochFromFullTransactionId(nextFullXid); if (xid <= nextXid) { diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index cf93357997c..76d6833b017 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -664,7 +664,6 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running) { TransactionId *xids; int nxids; - TransactionId nextXid; int i; Assert(standbyState >= STANDBY_INITIALIZED); @@ -881,23 +880,10 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running) LWLockRelease(ProcArrayLock); - /* - * ShmemVariableCache->nextXid must be beyond any observed xid. - * - * 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. - */ - nextXid = latestObservedXid; - TransactionIdAdvance(nextXid); - if (TransactionIdFollows(nextXid, ShmemVariableCache->nextXid)) - { - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = nextXid; - LWLockRelease(XidGenLock); - } + /* ShmemVariableCache->nextFullXid must be beyond any observed xid. */ + AdvanceNextFullTransactionIdPastXid(latestObservedXid); - Assert(TransactionIdIsValid(ShmemVariableCache->nextXid)); + Assert(TransactionIdIsValid(XidFromFullTransactionId(ShmemVariableCache->nextFullXid))); KnownAssignedXidsDisplay(trace_recovery(DEBUG3)); if (standbyState == STANDBY_SNAPSHOT_READY) @@ -2001,7 +1987,7 @@ GetRunningTransactionData(void) latestCompletedXid = ShmemVariableCache->latestCompletedXid; - oldestRunningXid = ShmemVariableCache->nextXid; + oldestRunningXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); /* * Spin over procArray collecting all xids @@ -2093,7 +2079,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; @@ -2138,7 +2124,7 @@ GetOldestActiveTransactionId(void) * have already completed), when we spin over it. */ LWLockAcquire(XidGenLock, LW_SHARED); - oldestRunningXid = ShmemVariableCache->nextXid; + oldestRunningXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); LWLockRelease(XidGenLock); /* @@ -2206,7 +2192,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 @@ -3266,12 +3252,10 @@ RecordKnownAssignedTransactionIds(TransactionId xid) */ latestObservedXid = xid; - /* ShmemVariableCache->nextXid must be beyond any observed xid */ + /* ShmemVariableCache->nextFullXid must be beyond any observed xid */ + AdvanceNextFullTransactionIdPastXid(latestObservedXid); next_expected_xid = latestObservedXid; TransactionIdAdvance(next_expected_xid); - LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - ShmemVariableCache->nextXid = next_expected_xid; - LWLockRelease(XidGenLock); } } diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index 4d10e57a803..cd56dca3aef 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -867,7 +867,7 @@ standby_redo(XLogReaderState *record) * up from a checkpoint and are immediately at our starting point, we * unconditionally move to STANDBY_INITIALIZED. After this point we * must do 4 things: - * * move shared nextXid forwards as we see new xids + * * move shared nextFullXid forwards as we see new xids * * extend the clog and subtrans with each new xid * * keep track of uncommitted known assigned xids * * keep track of uncommitted AccessExclusiveLocks diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index 92beaab5663..4e4d04bae37 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -3410,7 +3410,7 @@ ReleasePredicateLocks(bool isCommit, bool isReadOnlySafe) * 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 either a rollback or a read-only transaction diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c index 791bbf84024..daea21e84dd 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 e6742dc24b8..e675c33c547 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" @@ -164,8 +165,8 @@ pg_control_checkpoint(PG_FUNCTION_ARGS) nulls[5] = false; values[6] = CStringGetTextDatum(psprintf("%u:%u", - ControlFile->checkPointCopy.nextXidEpoch, - ControlFile->checkPointCopy.nextXid)); + EpochFromFullTransactionId(ControlFile->checkPointCopy.nextFullXid), + XidFromFullTransactionId(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 1aa1db218ac..9a17d0f9c0f 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -20,6 +20,7 @@ #include <time.h> +#include "access/transam.h" #include "access/xlog.h" #include "access/xlog_internal.h" #include "catalog/pg_control.h" @@ -256,8 +257,8 @@ main(int argc, char *argv[]) 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); + EpochFromFullTransactionId(ControlFile->checkPointCopy.nextFullXid), + XidFromFullTransactionId(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 a7b25ffe1cd..67fc646befb 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -430,11 +430,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 = + FullTransactionIdFromEpochAndXid(set_xid_epoch, + XidFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid)); if (set_xid != 0) { - ControlFile.checkPointCopy.nextXid = set_xid; + ControlFile.checkPointCopy.nextFullXid = + FullTransactionIdFromEpochAndXid(EpochFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid), + set_xid); /* * For the moment, just set oldestXid to a value that will force @@ -704,8 +708,8 @@ 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 = + FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId); ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId; ControlFile.checkPointCopy.nextMulti = FirstMultiXactId; ControlFile.checkPointCopy.nextMultiOffset = 0; @@ -786,8 +790,8 @@ PrintControlValues(bool guessed) 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); + EpochFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid), + XidFromFullTransactionId(ControlFile.checkPointCopy.nextFullXid)); printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile.checkPointCopy.nextOid); printf(_("Latest checkpoint's NextMultiXactId: %u\n"), @@ -879,7 +883,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 +893,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 78997e533e7..5715f0c5217 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. + * Not all values represent valid normal XIDs. + */ +typedef struct FullTransactionId +{ + uint64 value; +} FullTransactionId; + +static inline FullTransactionId +FullTransactionIdFromEpochAndXid(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,15 @@ (dest) = FirstNormalTransactionId; \ } while(0) +/* advance a FullTransactionId variable, stepping over special XIDs */ +static inline void +FullTransactionIdAdvance(FullTransactionId *dest) +{ + dest->value++; + while (XidFromFullTransactionId(*dest) < FirstNormalTransactionId) + dest->value++; +} + /* back up a transaction ID variable, handling wraparound correctly */ #define TransactionIdRetreat(dest) \ do { \ @@ -125,12 +160,12 @@ typedef struct VariableCacheData /* * These fields are protected by XidGenLock. */ - TransactionId nextXid; /* next XID to assign */ + FullTransactionId nextFullXid; /* next full 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 */ @@ -156,6 +191,7 @@ typedef struct VariableCacheData typedef VariableCacheData *VariableCache; + /* ---------------- * extern declarations * ---------------- @@ -187,11 +223,21 @@ extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid); /* in transam/varsup.c */ extern TransactionId GetNewTransactionId(bool isSubXact); -extern TransactionId ReadNewTransactionId(void); +extern void AdvanceNextFullTransactionIdPastXid(TransactionId xid); +extern FullTransactionId ReadNextFullTransactionId(void); extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid); extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid); extern bool ForceTransactionIdLimitUpdate(void); extern Oid GetNewObjectId(void); +/* + * For callers that just need the XID part of the next transaction ID. + */ +static inline TransactionId +ReadNewTransactionId(void) +{ + return XidFromFullTransactionId(ReadNextFullTransactionId()); +} + #endif /* TRAMSAM_H */ diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index bd74e7aaa03..eb6c44649dc 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -310,7 +310,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 a3910a5f997..ff98d9e91a8 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -15,13 +15,14 @@ #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" /* Version identifier for this pg_control format */ -#define PG_CONTROL_VERSION 1200 +#define PG_CONTROL_VERSION 1201 /* Nonce key length, see below */ #define MOCK_AUTH_NONCE_LEN 32 @@ -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 full transaction ID */ 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 346a3108bc1..23612435148 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 */ diff --git a/src/include/storage/standbydefs.h b/src/include/storage/standbydefs.h index cc8ccd5d369..01d2db6ac6e 100644 --- a/src/include/storage/standbydefs.h +++ b/src/include/storage/standbydefs.h @@ -49,7 +49,7 @@ typedef struct xl_running_xacts 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 */ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 88fb396910c..d4022acd91e 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -796,6 +796,7 @@ FreePageManager FreePageSpanLeader FromCharDateMode FromExpr +FullTransactionId FuncCall FuncCallContext FuncCandidateList -- 2.21.0
From f9845bb8bf9a501c7c0d19e8faf394e8a42cc6d8 Mon Sep 17 00:00:00 2001 From: Thomas Munro <thomas.munro@gmail.com> Date: Mon, 25 Mar 2019 22:45:22 +1300 Subject: [PATCH 2/2] Use 64 bit transaction IDs for the transaction stack. Provide GetTopFullTransactionId() and GetCurrentFullTransactionId(). The intended users of these interfaces are table access methods that use xids for visibility checks but don't want to have to "freeze" tuples. Discussion: https://postgr.es/m/CAA4eK1%2BMv%2Bmb0HFfWM9Srtc6MVe160WFurXV68iAFMcagRZ0dQ%40mail.gmail.com --- src/backend/access/transam/varsup.c | 24 +++-- src/backend/access/transam/xact.c | 160 +++++++++++++++++++--------- src/include/access/transam.h | 4 + src/include/access/xact.h | 5 + 4 files changed, 136 insertions(+), 57 deletions(-) diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c index db141d286dc..0286eb99b60 100644 --- a/src/backend/access/transam/varsup.c +++ b/src/backend/access/transam/varsup.c @@ -35,7 +35,8 @@ VariableCache ShmemVariableCache = NULL; /* - * Allocate the next XID for a new transaction or subtransaction. + * Allocate the next FullTransactionId for a new transaction or + * subtransaction. * * The new XID is also stored into MyPgXact before returning. * @@ -44,9 +45,10 @@ VariableCache ShmemVariableCache = NULL; * does something. So it is safe to do a database lookup if we want to * issue a warning about XID wrap. */ -TransactionId -GetNewTransactionId(bool isSubXact) +FullTransactionId +GetNewFullTransactionId(bool isSubXact) { + FullTransactionId full_xid; TransactionId xid; /* @@ -64,7 +66,7 @@ GetNewTransactionId(bool isSubXact) { Assert(!isSubXact); MyPgXact->xid = BootstrapTransactionId; - return BootstrapTransactionId; + return FullTransactionIdFromEpochAndXid(0, BootstrapTransactionId); } /* safety check, we should never get this far in a HS standby */ @@ -73,7 +75,8 @@ GetNewTransactionId(bool isSubXact) LWLockAcquire(XidGenLock, LW_EXCLUSIVE); - xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid); + full_xid = ShmemVariableCache->nextFullXid; + xid = XidFromFullTransactionId(full_xid); /*---------- * Check to see if it's safe to assign another XID. This protects against @@ -232,7 +235,16 @@ GetNewTransactionId(bool isSubXact) LWLockRelease(XidGenLock); - return xid; + return full_xid; +} + +/* + * Allocate the next FullTransactionId, but return only the XID part. + */ +TransactionId +GetNewTransactionId(bool isSubXact) +{ + return XidFromFullTransactionId(GetNewFullTransactionId(isSubXact)); } /* diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 9b100050597..fa5787c67f0 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -105,7 +105,7 @@ int synchronous_commit = SYNCHRONOUS_COMMIT_ON; * The XIDs are stored sorted in numerical order (not logical order) to make * lookups as fast as possible. */ -TransactionId XactTopTransactionId = InvalidTransactionId; +FullTransactionId XactTopFullTransactionId = {InvalidTransactionId}; int nParallelCurrentXids = 0; TransactionId *ParallelCurrentXids; @@ -171,7 +171,7 @@ typedef enum TBlockState */ typedef struct TransactionStateData { - TransactionId transactionId; /* my XID, or Invalid if none */ + FullTransactionId fullTransactionId; /* my FullTransactionId */ SubTransactionId subTransactionId; /* my subxact ID */ char *name; /* savepoint name, if any */ int savepointLevel; /* savepoint level */ @@ -372,9 +372,9 @@ IsAbortedTransactionBlockState(void) TransactionId GetTopTransactionId(void) { - if (!TransactionIdIsValid(XactTopTransactionId)) + if (!FullTransactionIdIsValid(XactTopFullTransactionId)) AssignTransactionId(&TopTransactionStateData); - return XactTopTransactionId; + return XidFromFullTransactionId(XactTopFullTransactionId); } /* @@ -387,7 +387,7 @@ GetTopTransactionId(void) TransactionId GetTopTransactionIdIfAny(void) { - return XactTopTransactionId; + return XidFromFullTransactionId(XactTopFullTransactionId); } /* @@ -400,11 +400,7 @@ GetTopTransactionIdIfAny(void) TransactionId GetCurrentTransactionId(void) { - TransactionState s = CurrentTransactionState; - - if (!TransactionIdIsValid(s->transactionId)) - AssignTransactionId(s); - return s->transactionId; + return XidFromFullTransactionId(GetCurrentFullTransactionId()); } /* @@ -417,7 +413,66 @@ GetCurrentTransactionId(void) TransactionId GetCurrentTransactionIdIfAny(void) { - return CurrentTransactionState->transactionId; + return XidFromFullTransactionId(GetCurrentFullTransactionIdIfAny()); +} + +/* + * GetTopFullTransactionId + * + * This will return the XID of the main transaction, assigning one if + * it's not yet set. Be careful to call this only inside a valid xact. + */ +FullTransactionId +GetTopFullTransactionId(void) +{ + if (!FullTransactionIdIsValid(XactTopFullTransactionId)) + AssignTransactionId(&TopTransactionStateData); + return XactTopFullTransactionId; +} + +/* + * GetTopFullTransactionIdIfAny + * + * This will return the FullTransactionId of the main transaction, if one is + * assigned. It will return a value for which FullTransactionIdIsValid() + * returns false if we are not currently inside a transaction, or inside a + * transaction that hasn't yet been assigned an XID. + */ +FullTransactionId +GetTopFullTransactionIdIfAny(void) +{ + return XactTopFullTransactionId; +} + +/* + * GetCurrentFullTransactionId + * + * This will return the FullTransactionId of the current transaction (main or + * sub transaction), assigning one if it's not yet set. Be careful to call + * this only inside a valid xact. + */ +FullTransactionId +GetCurrentFullTransactionId(void) +{ + TransactionState s = CurrentTransactionState; + + if (!FullTransactionIdIsValid(s->fullTransactionId)) + AssignTransactionId(s); + return s->fullTransactionId; +} + +/* + * GetCurrentFullTransactionIdIfAny + * + * This will return the FullTransactionId of the current sub xact, if one is + * assigned. It will return a value for which FullTransactionIdIsValid() + * returns false if we are not currently inside a transaction, or inside a + * transaction that hasn't been assigned an XID yet. + */ +FullTransactionId +GetCurrentFullTransactionIdIfAny(void) +{ + return CurrentTransactionState->fullTransactionId; } /* @@ -428,7 +483,7 @@ GetCurrentTransactionIdIfAny(void) void MarkCurrentTransactionIdLoggedIfAny(void) { - if (TransactionIdIsValid(CurrentTransactionState->transactionId)) + if (FullTransactionIdIsValid(CurrentTransactionState->fullTransactionId)) CurrentTransactionState->didLogXid = true; } @@ -477,7 +532,7 @@ AssignTransactionId(TransactionState s) bool log_unknown_top = false; /* Assert that caller didn't screw up */ - Assert(!TransactionIdIsValid(s->transactionId)); + Assert(!FullTransactionIdIsValid(s->fullTransactionId)); Assert(s->state == TRANS_INPROGRESS); /* @@ -493,14 +548,14 @@ AssignTransactionId(TransactionState s) * if we're at the bottom of a huge stack of subtransactions none of which * have XIDs yet. */ - if (isSubXact && !TransactionIdIsValid(s->parent->transactionId)) + if (isSubXact && !FullTransactionIdIsValid(s->parent->fullTransactionId)) { TransactionState p = s->parent; TransactionState *parents; size_t parentOffset = 0; parents = palloc(sizeof(TransactionState) * s->nestingLevel); - while (p != NULL && !TransactionIdIsValid(p->transactionId)) + while (p != NULL && !FullTransactionIdIsValid(p->fullTransactionId)) { parents[parentOffset++] = p; p = p->parent; @@ -538,19 +593,20 @@ AssignTransactionId(TransactionState s) * PG_PROC, the subtrans entry is needed to ensure that other backends see * the Xid as "running". See GetNewTransactionId. */ - s->transactionId = GetNewTransactionId(isSubXact); + s->fullTransactionId = GetNewFullTransactionId(isSubXact); if (!isSubXact) - XactTopTransactionId = s->transactionId; + XactTopFullTransactionId = s->fullTransactionId; if (isSubXact) - SubTransSetParent(s->transactionId, s->parent->transactionId); + SubTransSetParent(XidFromFullTransactionId(s->fullTransactionId), + XidFromFullTransactionId(s->parent->fullTransactionId)); /* * If it's a top-level transaction, the predicate locking system needs to * be told about it too. */ if (!isSubXact) - RegisterPredicateLockingXid(s->transactionId); + RegisterPredicateLockingXid(XidFromFullTransactionId(s->fullTransactionId)); /* * Acquire lock on the transaction XID. (We assume this cannot block.) We @@ -560,7 +616,7 @@ AssignTransactionId(TransactionState s) currentOwner = CurrentResourceOwner; CurrentResourceOwner = s->curTransactionOwner; - XactLockTableInsert(s->transactionId); + XactLockTableInsert(XidFromFullTransactionId(s->fullTransactionId)); CurrentResourceOwner = currentOwner; @@ -584,7 +640,7 @@ AssignTransactionId(TransactionState s) */ if (isSubXact && XLogStandbyInfoActive()) { - unreportedXids[nUnreportedXids] = s->transactionId; + unreportedXids[nUnreportedXids] = XidFromFullTransactionId(s->fullTransactionId); nUnreportedXids++; /* @@ -832,9 +888,9 @@ TransactionIdIsCurrentTransactionId(TransactionId xid) if (s->state == TRANS_ABORT) continue; - if (!TransactionIdIsValid(s->transactionId)) + if (!FullTransactionIdIsValid(s->fullTransactionId)) continue; /* it can't have any child XIDs either */ - if (TransactionIdEquals(xid, s->transactionId)) + if (TransactionIdEquals(xid, XidFromFullTransactionId(s->fullTransactionId))) return true; /* As the childXids array is ordered, we can use binary search */ low = 0; @@ -1495,7 +1551,7 @@ AtSubCommit_childXids(void) * all XIDs already in the array belong to subtransactions started and * subcommitted before us, so their XIDs must precede ours. */ - s->parent->childXids[s->parent->nChildXids] = s->transactionId; + s->parent->childXids[s->parent->nChildXids] = XidFromFullTransactionId(s->fullTransactionId); if (s->nChildXids > 0) memcpy(&s->parent->childXids[s->parent->nChildXids + 1], @@ -1809,7 +1865,7 @@ StartTransaction(void) s = &TopTransactionStateData; CurrentTransactionState = s; - Assert(XactTopTransactionId == InvalidTransactionId); + Assert(XidFromFullTransactionId(XactTopFullTransactionId) == InvalidTransactionId); /* check the current transaction state */ Assert(s->state == TRANS_DEFAULT); @@ -1821,7 +1877,7 @@ StartTransaction(void) * flags are fetched below. */ s->state = TRANS_START; - s->transactionId = InvalidTransactionId; /* until assigned */ + s->fullTransactionId = InvalidFullTransactionId; /* until assigned */ /* * initialize current transaction state fields @@ -2165,7 +2221,7 @@ CommitTransaction(void) AtCommit_Memory(); - s->transactionId = InvalidTransactionId; + s->fullTransactionId = InvalidFullTransactionId; s->subTransactionId = InvalidSubTransactionId; s->nestingLevel = 0; s->gucNestLevel = 0; @@ -2173,7 +2229,7 @@ CommitTransaction(void) s->nChildXids = 0; s->maxChildXids = 0; - XactTopTransactionId = InvalidTransactionId; + XactTopFullTransactionId = InvalidFullTransactionId; nParallelCurrentXids = 0; /* @@ -2448,7 +2504,7 @@ PrepareTransaction(void) AtCommit_Memory(); - s->transactionId = InvalidTransactionId; + s->fullTransactionId = InvalidFullTransactionId; s->subTransactionId = InvalidSubTransactionId; s->nestingLevel = 0; s->gucNestLevel = 0; @@ -2456,7 +2512,7 @@ PrepareTransaction(void) s->nChildXids = 0; s->maxChildXids = 0; - XactTopTransactionId = InvalidTransactionId; + XactTopFullTransactionId = InvalidFullTransactionId; nParallelCurrentXids = 0; /* @@ -2686,7 +2742,7 @@ CleanupTransaction(void) AtCleanup_Memory(); /* and transaction memory */ - s->transactionId = InvalidTransactionId; + s->fullTransactionId = InvalidFullTransactionId; s->subTransactionId = InvalidSubTransactionId; s->nestingLevel = 0; s->gucNestLevel = 0; @@ -2695,7 +2751,7 @@ CleanupTransaction(void) s->maxChildXids = 0; s->parallelModeLevel = 0; - XactTopTransactionId = InvalidTransactionId; + XactTopFullTransactionId = InvalidFullTransactionId; nParallelCurrentXids = 0; /* @@ -4693,7 +4749,7 @@ CommitSubTransaction(void) */ /* Post-commit cleanup */ - if (TransactionIdIsValid(s->transactionId)) + if (FullTransactionIdIsValid(s->fullTransactionId)) AtSubCommit_childXids(); AfterTriggerEndSubXact(true); AtSubCommit_Portals(s->subTransactionId, @@ -4718,8 +4774,8 @@ CommitSubTransaction(void) * The only lock we actually release here is the subtransaction XID lock. */ CurrentResourceOwner = s->curTransactionOwner; - if (TransactionIdIsValid(s->transactionId)) - XactLockTableDelete(s->transactionId); + if (FullTransactionIdIsValid(s->fullTransactionId)) + XactLockTableDelete(XidFromFullTransactionId(s->fullTransactionId)); /* * Other locks should get transferred to their parent resource owner. @@ -4872,7 +4928,7 @@ AbortSubTransaction(void) (void) RecordTransactionAbort(true); /* Post-abort cleanup */ - if (TransactionIdIsValid(s->transactionId)) + if (FullTransactionIdIsValid(s->fullTransactionId)) AtSubAbort_childXids(); CallSubXactCallbacks(SUBXACT_EVENT_ABORT_SUB, s->subTransactionId, @@ -4985,7 +5041,7 @@ PushTransaction(void) * We can now stack a minimally valid subtransaction without fear of * failure. */ - s->transactionId = InvalidTransactionId; /* until assigned */ + s->fullTransactionId = InvalidFullTransactionId; /* until assigned */ s->subTransactionId = currentSubTransactionId; s->parent = p; s->nestingLevel = p->nestingLevel + 1; @@ -5052,12 +5108,12 @@ Size EstimateTransactionStateSpace(void) { TransactionState s; - Size nxids = 6; /* iso level, deferrable, top & current XID, + Size nxids = 8; /* iso level, deferrable, top & current XID, * command counter, XID count */ for (s = CurrentTransactionState; s != NULL; s = s->parent) { - if (TransactionIdIsValid(s->transactionId)) + if (FullTransactionIdIsValid(s->fullTransactionId)) nxids = add_size(nxids, 1); nxids = add_size(nxids, s->nChildXids); } @@ -5073,7 +5129,7 @@ EstimateTransactionStateSpace(void) * * We need to save and restore XactDeferrable, XactIsoLevel, and the XIDs * associated with this transaction. The first eight bytes of the result - * contain XactDeferrable and XactIsoLevel; the next twelve bytes contain the + * contain XactDeferrable and XactIsoLevel; the next 18 bytes contain the * XID of the top-level transaction, the XID of the current transaction * (or, in each case, InvalidTransactionId if none), and the current command * counter. After that, the next 4 bytes contain a count of how many @@ -5093,8 +5149,10 @@ SerializeTransactionState(Size maxsize, char *start_address) result[c++] = (TransactionId) XactIsoLevel; result[c++] = (TransactionId) XactDeferrable; - result[c++] = XactTopTransactionId; - result[c++] = CurrentTransactionState->transactionId; + result[c++] = EpochFromFullTransactionId(XactTopFullTransactionId); + result[c++] = XidFromFullTransactionId(XactTopFullTransactionId); + result[c++] = EpochFromFullTransactionId(CurrentTransactionState->fullTransactionId); + result[c++] = XidFromFullTransactionId(CurrentTransactionState->fullTransactionId); result[c++] = (TransactionId) currentCommandId; Assert(maxsize >= c * sizeof(TransactionId)); @@ -5118,7 +5176,7 @@ SerializeTransactionState(Size maxsize, char *start_address) */ for (s = CurrentTransactionState; s != NULL; s = s->parent) { - if (TransactionIdIsValid(s->transactionId)) + if (FullTransactionIdIsValid(s->fullTransactionId)) nxids = add_size(nxids, 1); nxids = add_size(nxids, s->nChildXids); } @@ -5128,8 +5186,8 @@ SerializeTransactionState(Size maxsize, char *start_address) workspace = palloc(nxids * sizeof(TransactionId)); for (s = CurrentTransactionState; s != NULL; s = s->parent) { - if (TransactionIdIsValid(s->transactionId)) - workspace[i++] = s->transactionId; + if (FullTransactionIdIsValid(s->fullTransactionId)) + workspace[i++] = XidFromFullTransactionId(s->fullTransactionId); memcpy(&workspace[i], s->childXids, s->nChildXids * sizeof(TransactionId)); i += s->nChildXids; @@ -5159,11 +5217,11 @@ StartParallelWorkerTransaction(char *tstatespace) XactIsoLevel = (int) tstate[0]; XactDeferrable = (bool) tstate[1]; - XactTopTransactionId = tstate[2]; - CurrentTransactionState->transactionId = tstate[3]; - currentCommandId = tstate[4]; - nParallelCurrentXids = (int) tstate[5]; - ParallelCurrentXids = &tstate[6]; + XactTopFullTransactionId = FullTransactionIdFromEpochAndXid(tstate[2], tstate[3]); + CurrentTransactionState->fullTransactionId = FullTransactionIdFromEpochAndXid(tstate[4], tstate[5]); + currentCommandId = tstate[6]; + nParallelCurrentXids = (int) tstate[7]; + ParallelCurrentXids = &tstate[8]; CurrentTransactionState->blockState = TBLOCK_PARALLEL_INPROGRESS; } @@ -5222,7 +5280,7 @@ ShowTransactionStateRec(const char *str, TransactionState s) PointerIsValid(s->name) ? s->name : "unnamed", BlockStateAsString(s->blockState), TransStateAsString(s->state), - (unsigned int) s->transactionId, + (unsigned int) XidFromFullTransactionId(s->fullTransactionId), (unsigned int) s->subTransactionId, (unsigned int) currentCommandId, currentCommandIdUsed ? " (used)" : "", diff --git a/src/include/access/transam.h b/src/include/access/transam.h index 5715f0c5217..614565a47b8 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -49,6 +49,9 @@ #define U64FromFullTransactionId(x) ((x).value) #define FullTransactionIdPrecedes(a, b) ((a).value < (b).value) #define FullTransactionIdPrecedesOrEquals(a, b) ((a).value <= (b).value) +#define FullTransactionIdIsValid(x) TransactionIdIsValid(XidFromFullTransactionId(x)) +#define InvalidFullTransactionId \ + FullTransactionIdFromEpochAndXid(0, InvalidTransactionId) /* * A 64 bit value that contains an epoch and a TransactionId. This is @@ -223,6 +226,7 @@ extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid); /* in transam/varsup.c */ extern TransactionId GetNewTransactionId(bool isSubXact); +extern FullTransactionId GetNewFullTransactionId(bool isSubXact); extern void AdvanceNextFullTransactionIdPastXid(TransactionId xid); extern FullTransactionId ReadNextFullTransactionId(void); extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid, diff --git a/src/include/access/xact.h b/src/include/access/xact.h index e8579dcd478..b550343c4db 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -14,6 +14,7 @@ #ifndef XACT_H #define XACT_H +#include "access/transam.h" #include "access/xlogreader.h" #include "lib/stringinfo.h" #include "nodes/pg_list.h" @@ -355,6 +356,10 @@ extern TransactionId GetCurrentTransactionId(void); extern TransactionId GetCurrentTransactionIdIfAny(void); extern TransactionId GetStableLatestTransactionId(void); extern SubTransactionId GetCurrentSubTransactionId(void); +extern FullTransactionId GetTopFullTransactionId(void); +extern FullTransactionId GetTopFullTransactionIdIfAny(void); +extern FullTransactionId GetCurrentFullTransactionId(void); +extern FullTransactionId GetCurrentFullTransactionIdIfAny(void); extern void MarkCurrentTransactionIdLoggedIfAny(void); extern bool SubTransactionIsActive(SubTransactionId subxid); extern CommandId GetCurrentCommandId(bool used); -- 2.21.0