@@ -175,6 +175,11 @@ static void KnownAssignedXidsReset(void);
175
175
static inline void ProcArrayEndTransactionInternal (PGPROC * proc ,
176
176
PGXACT * pgxact , TransactionId latestXid );
177
177
static 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 );
178
183
179
184
/*
180
185
* Report shared-memory space needed by CreateSharedProcArray.
@@ -349,9 +354,7 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
349
354
Assert (TransactionIdIsValid (allPgXact [proc -> pgprocno ].xid ));
350
355
351
356
/* Advance global latestCompletedXid while holding the lock */
352
- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
353
- latestXid ))
354
- ShmemVariableCache -> latestCompletedXid = latestXid ;
357
+ MaintainLatestCompletedXid (latestXid );
355
358
}
356
359
else
357
360
{
@@ -464,9 +467,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, PGXACT *pgxact,
464
467
pgxact -> overflowed = false;
465
468
466
469
/* Also advance global latestCompletedXid while holding the lock */
467
- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
468
- latestXid ))
469
- ShmemVariableCache -> latestCompletedXid = latestXid ;
470
+ MaintainLatestCompletedXid (latestXid );
470
471
}
471
472
472
473
/*
@@ -621,6 +622,59 @@ ProcArrayClearTransaction(PGPROC *proc)
621
622
pgxact -> overflowed = false;
622
623
}
623
624
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
+
624
678
/*
625
679
* ProcArrayInitRecovery -- initialize recovery xid mgmt environment
626
680
*
@@ -869,12 +923,9 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
869
923
* If a transaction wrote a commit record in the gap between taking and
870
924
* logging the snapshot then latestCompletedXid may already be higher than
871
925
* the value from the snapshot, so check before we use the incoming value.
926
+ * It also might not yet be set at all.
872
927
*/
873
- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
874
- running -> latestCompletedXid ))
875
- ShmemVariableCache -> latestCompletedXid = running -> latestCompletedXid ;
876
-
877
- Assert (TransactionIdIsNormal (ShmemVariableCache -> latestCompletedXid ));
928
+ MaintainLatestCompletedXidRecovery (running -> latestCompletedXid );
878
929
879
930
LWLockRelease (ProcArrayLock );
880
931
@@ -989,6 +1040,7 @@ TransactionIdIsInProgress(TransactionId xid)
989
1040
int nxids = 0 ;
990
1041
ProcArrayStruct * arrayP = procArray ;
991
1042
TransactionId topxid ;
1043
+ TransactionId latestCompletedXid ;
992
1044
int i ,
993
1045
j ;
994
1046
@@ -1051,7 +1103,9 @@ TransactionIdIsInProgress(TransactionId xid)
1051
1103
* Now that we have the lock, we can check latestCompletedXid; if the
1052
1104
* target Xid is after that, it's surely still running.
1053
1105
*/
1054
- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid , xid ))
1106
+ latestCompletedXid =
1107
+ XidFromFullTransactionId (ShmemVariableCache -> latestCompletedXid );
1108
+ if (TransactionIdPrecedes (latestCompletedXid , xid ))
1055
1109
{
1056
1110
LWLockRelease (ProcArrayLock );
1057
1111
xc_by_latest_xid_inc ();
@@ -1330,9 +1384,9 @@ GetOldestXmin(Relation rel, int flags)
1330
1384
* and so protects us against overestimating the result due to future
1331
1385
* additions.
1332
1386
*/
1333
- result = ShmemVariableCache -> latestCompletedXid ;
1334
- Assert (TransactionIdIsNormal (result ));
1387
+ result = XidFromFullTransactionId (ShmemVariableCache -> latestCompletedXid );
1335
1388
TransactionIdAdvance (result );
1389
+ Assert (TransactionIdIsNormal (result ));
1336
1390
1337
1391
for (index = 0 ; index < arrayP -> numProcs ; index ++ )
1338
1392
{
@@ -1511,6 +1565,7 @@ GetSnapshotData(Snapshot snapshot)
1511
1565
int count = 0 ;
1512
1566
int subcount = 0 ;
1513
1567
bool suboverflowed = false;
1568
+ FullTransactionId latest_completed ;
1514
1569
TransactionId replication_slot_xmin = InvalidTransactionId ;
1515
1570
TransactionId replication_slot_catalog_xmin = InvalidTransactionId ;
1516
1571
@@ -1554,10 +1609,11 @@ GetSnapshotData(Snapshot snapshot)
1554
1609
*/
1555
1610
LWLockAcquire (ProcArrayLock , LW_SHARED );
1556
1611
1612
+ latest_completed = ShmemVariableCache -> latestCompletedXid ;
1557
1613
/* xmax is always latestCompletedXid + 1 */
1558
- xmax = ShmemVariableCache -> latestCompletedXid ;
1559
- Assert (TransactionIdIsNormal (xmax ));
1614
+ xmax = XidFromFullTransactionId (latest_completed );
1560
1615
TransactionIdAdvance (xmax );
1616
+ Assert (TransactionIdIsNormal (xmax ));
1561
1617
1562
1618
/* initialize xmin calculation with xmax */
1563
1619
globalxmin = xmin = xmax ;
@@ -1984,9 +2040,10 @@ GetRunningTransactionData(void)
1984
2040
LWLockAcquire (ProcArrayLock , LW_SHARED );
1985
2041
LWLockAcquire (XidGenLock , LW_SHARED );
1986
2042
1987
- latestCompletedXid = ShmemVariableCache -> latestCompletedXid ;
1988
-
1989
- oldestRunningXid = XidFromFullTransactionId (ShmemVariableCache -> nextXid );
2043
+ latestCompletedXid =
2044
+ XidFromFullTransactionId (ShmemVariableCache -> latestCompletedXid );
2045
+ oldestRunningXid =
2046
+ XidFromFullTransactionId (ShmemVariableCache -> nextXid );
1990
2047
1991
2048
/*
1992
2049
* Spin over procArray collecting all xids
@@ -3207,9 +3264,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
3207
3264
elog (WARNING , "did not find subXID %u in MyProc" , xid );
3208
3265
3209
3266
/* Also advance global latestCompletedXid while holding the lock */
3210
- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
3211
- latestXid ))
3212
- ShmemVariableCache -> latestCompletedXid = latestXid ;
3267
+ MaintainLatestCompletedXid (latestXid );
3213
3268
3214
3269
LWLockRelease (ProcArrayLock );
3215
3270
}
@@ -3236,6 +3291,32 @@ DisplayXidCache(void)
3236
3291
}
3237
3292
#endif /* XIDCACHE_DEBUG */
3238
3293
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
+
3239
3320
3240
3321
/* ----------------------------------------------
3241
3322
* KnownAssignedTransactionIds sub-module
@@ -3388,9 +3469,7 @@ ExpireTreeKnownAssignedTransactionIds(TransactionId xid, int nsubxids,
3388
3469
KnownAssignedXidsRemoveTree (xid , nsubxids , subxids );
3389
3470
3390
3471
/* As in ProcArrayEndTransaction, advance latestCompletedXid */
3391
- if (TransactionIdPrecedes (ShmemVariableCache -> latestCompletedXid ,
3392
- max_xid ))
3393
- ShmemVariableCache -> latestCompletedXid = max_xid ;
3472
+ MaintainLatestCompletedXidRecovery (max_xid );
3394
3473
3395
3474
LWLockRelease (ProcArrayLock );
3396
3475
}
0 commit comments