1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-19 13:42:17 +03:00

Track latest completed xid as a FullTransactionId.

The reason for doing so is that a subsequent commit will need that to
avoid wraparound issues. As the subsequent change is large this was
split out for easier review.

The reason this is not a perfect straight-forward change is that we do
not want track 64bit xids in the procarray or the WAL. Therefore we
need to advance lastestCompletedXid in relation to 32 bit xids. The
code for that is now centralized in MaintainLatestCompletedXid*.

Author: Andres Freund
Reviewed-By: Thomas Munro, Robert Haas, David Rowley
Discussion: https://postgr.es/m/20200301083601.ews6hz5dduc3w2se@alap3.anarazel.de
This commit is contained in:
Andres Freund
2020-08-11 17:41:18 -07:00
parent fea10a6434
commit 3bd7f9969a
4 changed files with 191 additions and 29 deletions

View File

@@ -175,6 +175,11 @@ static void KnownAssignedXidsReset(void);
static inline void ProcArrayEndTransactionInternal(PGPROC *proc,
PGXACT *pgxact, TransactionId latestXid);
static void ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid);
static void MaintainLatestCompletedXid(TransactionId latestXid);
static void MaintainLatestCompletedXidRecovery(TransactionId latestXid);
static inline FullTransactionId FullXidRelativeTo(FullTransactionId rel,
TransactionId xid);
/*
* Report shared-memory space needed by CreateSharedProcArray.
@@ -349,9 +354,7 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
Assert(TransactionIdIsValid(allPgXact[proc->pgprocno].xid));
/* Advance global latestCompletedXid while holding the lock */
if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
latestXid))
ShmemVariableCache->latestCompletedXid = latestXid;
MaintainLatestCompletedXid(latestXid);
}
else
{
@@ -464,9 +467,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, PGXACT *pgxact,
pgxact->overflowed = false;
/* Also advance global latestCompletedXid while holding the lock */
if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
latestXid))
ShmemVariableCache->latestCompletedXid = latestXid;
MaintainLatestCompletedXid(latestXid);
}
/*
@@ -621,6 +622,59 @@ ProcArrayClearTransaction(PGPROC *proc)
pgxact->overflowed = false;
}
/*
* Update ShmemVariableCache->latestCompletedXid to point to latestXid if
* currently older.
*/
static void
MaintainLatestCompletedXid(TransactionId latestXid)
{
FullTransactionId cur_latest = ShmemVariableCache->latestCompletedXid;
Assert(FullTransactionIdIsValid(cur_latest));
Assert(!RecoveryInProgress());
Assert(LWLockHeldByMe(ProcArrayLock));
if (TransactionIdPrecedes(XidFromFullTransactionId(cur_latest), latestXid))
{
ShmemVariableCache->latestCompletedXid =
FullXidRelativeTo(cur_latest, latestXid);
}
Assert(IsBootstrapProcessingMode() ||
FullTransactionIdIsNormal(ShmemVariableCache->latestCompletedXid));
}
/*
* Same as MaintainLatestCompletedXid, except for use during WAL replay.
*/
static void
MaintainLatestCompletedXidRecovery(TransactionId latestXid)
{
FullTransactionId cur_latest = ShmemVariableCache->latestCompletedXid;
FullTransactionId rel;
Assert(AmStartupProcess() || !IsUnderPostmaster);
Assert(LWLockHeldByMe(ProcArrayLock));
/*
* Need a FullTransactionId to compare latestXid with. Can't rely on
* latestCompletedXid to be initialized in recovery. But in recovery it's
* safe to access nextXid without a lock for the startup process.
*/
rel = ShmemVariableCache->nextXid;
Assert(FullTransactionIdIsValid(ShmemVariableCache->nextXid));
if (!FullTransactionIdIsValid(cur_latest) ||
TransactionIdPrecedes(XidFromFullTransactionId(cur_latest), latestXid))
{
ShmemVariableCache->latestCompletedXid =
FullXidRelativeTo(rel, latestXid);
}
Assert(FullTransactionIdIsNormal(ShmemVariableCache->latestCompletedXid));
}
/*
* ProcArrayInitRecovery -- initialize recovery xid mgmt environment
*
@@ -869,12 +923,9 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
* If a transaction wrote a commit record in the gap between taking and
* logging the snapshot then latestCompletedXid may already be higher than
* the value from the snapshot, so check before we use the incoming value.
* It also might not yet be set at all.
*/
if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
running->latestCompletedXid))
ShmemVariableCache->latestCompletedXid = running->latestCompletedXid;
Assert(TransactionIdIsNormal(ShmemVariableCache->latestCompletedXid));
MaintainLatestCompletedXidRecovery(running->latestCompletedXid);
LWLockRelease(ProcArrayLock);
@@ -989,6 +1040,7 @@ TransactionIdIsInProgress(TransactionId xid)
int nxids = 0;
ProcArrayStruct *arrayP = procArray;
TransactionId topxid;
TransactionId latestCompletedXid;
int i,
j;
@@ -1051,7 +1103,9 @@ TransactionIdIsInProgress(TransactionId xid)
* Now that we have the lock, we can check latestCompletedXid; if the
* target Xid is after that, it's surely still running.
*/
if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid, xid))
latestCompletedXid =
XidFromFullTransactionId(ShmemVariableCache->latestCompletedXid);
if (TransactionIdPrecedes(latestCompletedXid, xid))
{
LWLockRelease(ProcArrayLock);
xc_by_latest_xid_inc();
@@ -1330,9 +1384,9 @@ GetOldestXmin(Relation rel, int flags)
* and so protects us against overestimating the result due to future
* additions.
*/
result = ShmemVariableCache->latestCompletedXid;
Assert(TransactionIdIsNormal(result));
result = XidFromFullTransactionId(ShmemVariableCache->latestCompletedXid);
TransactionIdAdvance(result);
Assert(TransactionIdIsNormal(result));
for (index = 0; index < arrayP->numProcs; index++)
{
@@ -1511,6 +1565,7 @@ GetSnapshotData(Snapshot snapshot)
int count = 0;
int subcount = 0;
bool suboverflowed = false;
FullTransactionId latest_completed;
TransactionId replication_slot_xmin = InvalidTransactionId;
TransactionId replication_slot_catalog_xmin = InvalidTransactionId;
@@ -1554,10 +1609,11 @@ GetSnapshotData(Snapshot snapshot)
*/
LWLockAcquire(ProcArrayLock, LW_SHARED);
latest_completed = ShmemVariableCache->latestCompletedXid;
/* xmax is always latestCompletedXid + 1 */
xmax = ShmemVariableCache->latestCompletedXid;
Assert(TransactionIdIsNormal(xmax));
xmax = XidFromFullTransactionId(latest_completed);
TransactionIdAdvance(xmax);
Assert(TransactionIdIsNormal(xmax));
/* initialize xmin calculation with xmax */
globalxmin = xmin = xmax;
@@ -1984,9 +2040,10 @@ GetRunningTransactionData(void)
LWLockAcquire(ProcArrayLock, LW_SHARED);
LWLockAcquire(XidGenLock, LW_SHARED);
latestCompletedXid = ShmemVariableCache->latestCompletedXid;
oldestRunningXid = XidFromFullTransactionId(ShmemVariableCache->nextXid);
latestCompletedXid =
XidFromFullTransactionId(ShmemVariableCache->latestCompletedXid);
oldestRunningXid =
XidFromFullTransactionId(ShmemVariableCache->nextXid);
/*
* Spin over procArray collecting all xids
@@ -3207,9 +3264,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
elog(WARNING, "did not find subXID %u in MyProc", xid);
/* Also advance global latestCompletedXid while holding the lock */
if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
latestXid))
ShmemVariableCache->latestCompletedXid = latestXid;
MaintainLatestCompletedXid(latestXid);
LWLockRelease(ProcArrayLock);
}
@@ -3236,6 +3291,32 @@ DisplayXidCache(void)
}
#endif /* XIDCACHE_DEBUG */
/*
* Convert a 32 bit transaction id into 64 bit transaction id, by assuming it
* is within MaxTransactionId / 2 of XidFromFullTransactionId(rel).
*
* Be very careful about when to use this function. It can only safely be used
* when there is a guarantee that xid is within MaxTransactionId / 2 xids of
* rel. That e.g. can be guaranteed if the the caller assures a snapshot is
* held by the backend and xid is from a table (where vacuum/freezing ensures
* the xid has to be within that range), or if xid is from the procarray and
* prevents xid wraparound that way.
*/
static inline FullTransactionId
FullXidRelativeTo(FullTransactionId rel, TransactionId xid)
{
TransactionId rel_xid = XidFromFullTransactionId(rel);
Assert(TransactionIdIsValid(xid));
Assert(TransactionIdIsValid(rel_xid));
/* not guaranteed to find issues, but likely to catch mistakes */
AssertTransactionIdInAllowableRange(xid);
return FullTransactionIdFromU64(U64FromFullTransactionId(rel)
+ (int32) (xid - rel_xid));
}
/* ----------------------------------------------
* KnownAssignedTransactionIds sub-module
@@ -3388,9 +3469,7 @@ ExpireTreeKnownAssignedTransactionIds(TransactionId xid, int nsubxids,
KnownAssignedXidsRemoveTree(xid, nsubxids, subxids);
/* As in ProcArrayEndTransaction, advance latestCompletedXid */
if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
max_xid))
ShmemVariableCache->latestCompletedXid = max_xid;
MaintainLatestCompletedXidRecovery(max_xid);
LWLockRelease(ProcArrayLock);
}