@@ -175,6 +175,11 @@ static void KnownAssignedXidsReset(void);
175175static inline void ProcArrayEndTransactionInternal (PGPROC * proc ,
176176 PGXACT * pgxact , TransactionId latestXid );
177177static void ProcArrayGroupClearXid (PGPROC * proc , TransactionId latestXid );
178+ static void MaintainLatestCompletedXid (TransactionId latestXid );
179+ static void MaintainLatestCompletedXidRecovery (TransactionId latestXid );
180+
181+ static inline FullTransactionId FullXidRelativeTo (FullTransactionId rel ,
182+ TransactionId xid );
178183
179184/*
180185 * Report shared-memory space needed by CreateSharedProcArray.
@@ -349,9 +354,7 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
349354 Assert (TransactionIdIsValid (allPgXact [proc -> pgprocno ].xid ));
350355
351356 /* Advance global latestCompletedXid while holding the lock */
352- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
353- latestXid ))
354- ShmemVariableCache -> latestCompletedXid = latestXid ;
357+ MaintainLatestCompletedXid (latestXid );
355358 }
356359 else
357360 {
@@ -464,9 +467,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, PGXACT *pgxact,
464467 pgxact -> overflowed = false;
465468
466469 /* Also advance global latestCompletedXid while holding the lock */
467- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
468- latestXid ))
469- ShmemVariableCache -> latestCompletedXid = latestXid ;
470+ MaintainLatestCompletedXid (latestXid );
470471}
471472
472473/*
@@ -621,6 +622,59 @@ ProcArrayClearTransaction(PGPROC *proc)
621622 pgxact -> overflowed = false;
622623}
623624
625+ /*
626+ * Update ShmemVariableCache->latestCompletedXid to point to latestXid if
627+ * currently older.
628+ */
629+ static void
630+ MaintainLatestCompletedXid (TransactionId latestXid )
631+ {
632+ FullTransactionId cur_latest = ShmemVariableCache -> latestCompletedXid ;
633+
634+ Assert (FullTransactionIdIsValid (cur_latest ));
635+ Assert (!RecoveryInProgress ());
636+ Assert (LWLockHeldByMe (ProcArrayLock ));
637+
638+ if (TransactionIdPrecedes (XidFromFullTransactionId (cur_latest ), latestXid ))
639+ {
640+ ShmemVariableCache -> latestCompletedXid =
641+ FullXidRelativeTo (cur_latest , latestXid );
642+ }
643+
644+ Assert (IsBootstrapProcessingMode () ||
645+ FullTransactionIdIsNormal (ShmemVariableCache -> latestCompletedXid ));
646+ }
647+
648+ /*
649+ * Same as MaintainLatestCompletedXid, except for use during WAL replay.
650+ */
651+ static void
652+ MaintainLatestCompletedXidRecovery (TransactionId latestXid )
653+ {
654+ FullTransactionId cur_latest = ShmemVariableCache -> latestCompletedXid ;
655+ FullTransactionId rel ;
656+
657+ Assert (AmStartupProcess () || !IsUnderPostmaster );
658+ Assert (LWLockHeldByMe (ProcArrayLock ));
659+
660+ /*
661+ * Need a FullTransactionId to compare latestXid with. Can't rely on
662+ * latestCompletedXid to be initialized in recovery. But in recovery it's
663+ * safe to access nextXid without a lock for the startup process.
664+ */
665+ rel = ShmemVariableCache -> nextXid ;
666+ Assert (FullTransactionIdIsValid (ShmemVariableCache -> nextXid ));
667+
668+ if (!FullTransactionIdIsValid (cur_latest ) ||
669+ TransactionIdPrecedes (XidFromFullTransactionId (cur_latest ), latestXid ))
670+ {
671+ ShmemVariableCache -> latestCompletedXid =
672+ FullXidRelativeTo (rel , latestXid );
673+ }
674+
675+ Assert (FullTransactionIdIsNormal (ShmemVariableCache -> latestCompletedXid ));
676+ }
677+
624678/*
625679 * ProcArrayInitRecovery -- initialize recovery xid mgmt environment
626680 *
@@ -869,12 +923,9 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
869923 * If a transaction wrote a commit record in the gap between taking and
870924 * logging the snapshot then latestCompletedXid may already be higher than
871925 * the value from the snapshot, so check before we use the incoming value.
926+ * It also might not yet be set at all.
872927 */
873- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
874- running -> latestCompletedXid ))
875- ShmemVariableCache -> latestCompletedXid = running -> latestCompletedXid ;
876-
877- Assert (TransactionIdIsNormal (ShmemVariableCache -> latestCompletedXid ));
928+ MaintainLatestCompletedXidRecovery (running -> latestCompletedXid );
878929
879930 LWLockRelease (ProcArrayLock );
880931
@@ -989,6 +1040,7 @@ TransactionIdIsInProgress(TransactionId xid)
9891040 int nxids = 0 ;
9901041 ProcArrayStruct * arrayP = procArray ;
9911042 TransactionId topxid ;
1043+ TransactionId latestCompletedXid ;
9921044 int i ,
9931045 j ;
9941046
@@ -1051,7 +1103,9 @@ TransactionIdIsInProgress(TransactionId xid)
10511103 * Now that we have the lock, we can check latestCompletedXid; if the
10521104 * target Xid is after that, it's surely still running.
10531105 */
1054- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid , xid ))
1106+ latestCompletedXid =
1107+ XidFromFullTransactionId (ShmemVariableCache -> latestCompletedXid );
1108+ if (TransactionIdPrecedes (latestCompletedXid , xid ))
10551109 {
10561110 LWLockRelease (ProcArrayLock );
10571111 xc_by_latest_xid_inc ();
@@ -1330,9 +1384,9 @@ GetOldestXmin(Relation rel, int flags)
13301384 * and so protects us against overestimating the result due to future
13311385 * additions.
13321386 */
1333- result = ShmemVariableCache -> latestCompletedXid ;
1334- Assert (TransactionIdIsNormal (result ));
1387+ result = XidFromFullTransactionId (ShmemVariableCache -> latestCompletedXid );
13351388 TransactionIdAdvance (result );
1389+ Assert (TransactionIdIsNormal (result ));
13361390
13371391 for (index = 0 ; index < arrayP -> numProcs ; index ++ )
13381392 {
@@ -1511,6 +1565,7 @@ GetSnapshotData(Snapshot snapshot)
15111565 int count = 0 ;
15121566 int subcount = 0 ;
15131567 bool suboverflowed = false;
1568+ FullTransactionId latest_completed ;
15141569 TransactionId replication_slot_xmin = InvalidTransactionId ;
15151570 TransactionId replication_slot_catalog_xmin = InvalidTransactionId ;
15161571
@@ -1554,10 +1609,11 @@ GetSnapshotData(Snapshot snapshot)
15541609 */
15551610 LWLockAcquire (ProcArrayLock , LW_SHARED );
15561611
1612+ latest_completed = ShmemVariableCache -> latestCompletedXid ;
15571613 /* xmax is always latestCompletedXid + 1 */
1558- xmax = ShmemVariableCache -> latestCompletedXid ;
1559- Assert (TransactionIdIsNormal (xmax ));
1614+ xmax = XidFromFullTransactionId (latest_completed );
15601615 TransactionIdAdvance (xmax );
1616+ Assert (TransactionIdIsNormal (xmax ));
15611617
15621618 /* initialize xmin calculation with xmax */
15631619 globalxmin = xmin = xmax ;
@@ -1984,9 +2040,10 @@ GetRunningTransactionData(void)
19842040 LWLockAcquire (ProcArrayLock , LW_SHARED );
19852041 LWLockAcquire (XidGenLock , LW_SHARED );
19862042
1987- latestCompletedXid = ShmemVariableCache -> latestCompletedXid ;
1988-
1989- oldestRunningXid = XidFromFullTransactionId (ShmemVariableCache -> nextXid );
2043+ latestCompletedXid =
2044+ XidFromFullTransactionId (ShmemVariableCache -> latestCompletedXid );
2045+ oldestRunningXid =
2046+ XidFromFullTransactionId (ShmemVariableCache -> nextXid );
19902047
19912048 /*
19922049 * Spin over procArray collecting all xids
@@ -3207,9 +3264,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
32073264 elog (WARNING , "did not find subXID %u in MyProc" , xid );
32083265
32093266 /* Also advance global latestCompletedXid while holding the lock */
3210- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
3211- latestXid ))
3212- ShmemVariableCache -> latestCompletedXid = latestXid ;
3267+ MaintainLatestCompletedXid (latestXid );
32133268
32143269 LWLockRelease (ProcArrayLock );
32153270}
@@ -3236,6 +3291,32 @@ DisplayXidCache(void)
32363291}
32373292#endif /* XIDCACHE_DEBUG */
32383293
3294+ /*
3295+ * Convert a 32 bit transaction id into 64 bit transaction id, by assuming it
3296+ * is within MaxTransactionId / 2 of XidFromFullTransactionId(rel).
3297+ *
3298+ * Be very careful about when to use this function. It can only safely be used
3299+ * when there is a guarantee that xid is within MaxTransactionId / 2 xids of
3300+ * rel. That e.g. can be guaranteed if the the caller assures a snapshot is
3301+ * held by the backend and xid is from a table (where vacuum/freezing ensures
3302+ * the xid has to be within that range), or if xid is from the procarray and
3303+ * prevents xid wraparound that way.
3304+ */
3305+ static inline FullTransactionId
3306+ FullXidRelativeTo (FullTransactionId rel , TransactionId xid )
3307+ {
3308+ TransactionId rel_xid = XidFromFullTransactionId (rel );
3309+
3310+ Assert (TransactionIdIsValid (xid ));
3311+ Assert (TransactionIdIsValid (rel_xid ));
3312+
3313+ /* not guaranteed to find issues, but likely to catch mistakes */
3314+ AssertTransactionIdInAllowableRange (xid );
3315+
3316+ return FullTransactionIdFromU64 (U64FromFullTransactionId (rel )
3317+ + (int32 ) (xid - rel_xid ));
3318+ }
3319+
32393320
32403321/* ----------------------------------------------
32413322 * KnownAssignedTransactionIds sub-module
@@ -3388,9 +3469,7 @@ ExpireTreeKnownAssignedTransactionIds(TransactionId xid, int nsubxids,
33883469 KnownAssignedXidsRemoveTree (xid , nsubxids , subxids );
33893470
33903471 /* As in ProcArrayEndTransaction, advance latestCompletedXid */
3391- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
3392- max_xid ))
3393- ShmemVariableCache -> latestCompletedXid = max_xid ;
3472+ MaintainLatestCompletedXidRecovery (max_xid );
33943473
33953474 LWLockRelease (ProcArrayLock );
33963475}
0 commit comments