@@ -407,6 +407,7 @@ CreateSharedProcArray(void)
407
407
procArray -> lastOverflowedXid = InvalidTransactionId ;
408
408
procArray -> replication_slot_xmin = InvalidTransactionId ;
409
409
procArray -> replication_slot_catalog_xmin = InvalidTransactionId ;
410
+ ShmemVariableCache -> xactCompletionCount = 1 ;
410
411
}
411
412
412
413
allProcs = ProcGlobal -> allProcs ;
@@ -534,6 +535,9 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
534
535
/* Advance global latestCompletedXid while holding the lock */
535
536
MaintainLatestCompletedXid (latestXid );
536
537
538
+ /* Same with xactCompletionCount */
539
+ ShmemVariableCache -> xactCompletionCount ++ ;
540
+
537
541
ProcGlobal -> xids [proc -> pgxactoff ] = 0 ;
538
542
ProcGlobal -> subxidStates [proc -> pgxactoff ].overflowed = false;
539
543
ProcGlobal -> subxidStates [proc -> pgxactoff ].count = 0 ;
@@ -667,6 +671,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
667
671
{
668
672
size_t pgxactoff = proc -> pgxactoff ;
669
673
674
+ Assert (LWLockHeldByMe (ProcArrayLock ));
670
675
Assert (TransactionIdIsValid (ProcGlobal -> xids [pgxactoff ]));
671
676
Assert (ProcGlobal -> xids [pgxactoff ] == proc -> xid );
672
677
@@ -698,6 +703,9 @@ ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
698
703
699
704
/* Also advance global latestCompletedXid while holding the lock */
700
705
MaintainLatestCompletedXid (latestXid );
706
+
707
+ /* Same with xactCompletionCount */
708
+ ShmemVariableCache -> xactCompletionCount ++ ;
701
709
}
702
710
703
711
/*
@@ -1916,6 +1924,93 @@ GetMaxSnapshotSubxidCount(void)
1916
1924
return TOTAL_MAX_CACHED_SUBXIDS ;
1917
1925
}
1918
1926
1927
+ /*
1928
+ * Initialize old_snapshot_threshold specific parts of a newly build snapshot.
1929
+ */
1930
+ static void
1931
+ GetSnapshotDataInitOldSnapshot (Snapshot snapshot )
1932
+ {
1933
+ if (!OldSnapshotThresholdActive ())
1934
+ {
1935
+ /*
1936
+ * If not using "snapshot too old" feature, fill related fields with
1937
+ * dummy values that don't require any locking.
1938
+ */
1939
+ snapshot -> lsn = InvalidXLogRecPtr ;
1940
+ snapshot -> whenTaken = 0 ;
1941
+ }
1942
+ else
1943
+ {
1944
+ /*
1945
+ * Capture the current time and WAL stream location in case this
1946
+ * snapshot becomes old enough to need to fall back on the special
1947
+ * "old snapshot" logic.
1948
+ */
1949
+ snapshot -> lsn = GetXLogInsertRecPtr ();
1950
+ snapshot -> whenTaken = GetSnapshotCurrentTimestamp ();
1951
+ MaintainOldSnapshotTimeMapping (snapshot -> whenTaken , snapshot -> xmin );
1952
+ }
1953
+ }
1954
+
1955
+ /*
1956
+ * Helper function for GetSnapshotData() that checks if the bulk of the
1957
+ * visibility information in the snapshot is still valid. If so, it updates
1958
+ * the fields that need to change and returns true. Otherwise it returns
1959
+ * false.
1960
+ *
1961
+ * This very likely can be evolved to not need ProcArrayLock held (at very
1962
+ * least in the case we already hold a snapshot), but that's for another day.
1963
+ */
1964
+ static bool
1965
+ GetSnapshotDataReuse (Snapshot snapshot )
1966
+ {
1967
+ uint64 curXactCompletionCount ;
1968
+
1969
+ Assert (LWLockHeldByMe (ProcArrayLock ));
1970
+
1971
+ if (unlikely (snapshot -> snapXactCompletionCount == 0 ))
1972
+ return false;
1973
+
1974
+ curXactCompletionCount = ShmemVariableCache -> xactCompletionCount ;
1975
+ if (curXactCompletionCount != snapshot -> snapXactCompletionCount )
1976
+ return false;
1977
+
1978
+ /*
1979
+ * If the current xactCompletionCount is still the same as it was at the
1980
+ * time the snapshot was built, we can be sure that rebuilding the
1981
+ * contents of the snapshot the hard way would result in the same snapshot
1982
+ * contents:
1983
+ *
1984
+ * As explained in transam/README, the set of xids considered running by
1985
+ * GetSnapshotData() cannot change while ProcArrayLock is held. Snapshot
1986
+ * contents only depend on transactions with xids and xactCompletionCount
1987
+ * is incremented whenever a transaction with an xid finishes (while
1988
+ * holding ProcArrayLock) exclusively). Thus the xactCompletionCount check
1989
+ * ensures we would detect if the snapshot would have changed.
1990
+ *
1991
+ * As the snapshot contents are the same as it was before, it is is safe
1992
+ * to re-enter the snapshot's xmin into the PGPROC array. None of the rows
1993
+ * visible under the snapshot could already have been removed (that'd
1994
+ * require the set of running transactions to change) and it fulfills the
1995
+ * requirement that concurrent GetSnapshotData() calls yield the same
1996
+ * xmin.
1997
+ */
1998
+ if (!TransactionIdIsValid (MyProc -> xmin ))
1999
+ MyProc -> xmin = TransactionXmin = snapshot -> xmin ;
2000
+
2001
+ RecentXmin = snapshot -> xmin ;
2002
+ Assert (TransactionIdPrecedesOrEquals (TransactionXmin , RecentXmin ));
2003
+
2004
+ snapshot -> curcid = GetCurrentCommandId (false);
2005
+ snapshot -> active_count = 0 ;
2006
+ snapshot -> regd_count = 0 ;
2007
+ snapshot -> copied = false;
2008
+
2009
+ GetSnapshotDataInitOldSnapshot (snapshot );
2010
+
2011
+ return true;
2012
+ }
2013
+
1919
2014
/*
1920
2015
* GetSnapshotData -- returns information about running transactions.
1921
2016
*
@@ -1963,6 +2058,7 @@ GetSnapshotData(Snapshot snapshot)
1963
2058
TransactionId oldestxid ;
1964
2059
int mypgxactoff ;
1965
2060
TransactionId myxid ;
2061
+ uint64 curXactCompletionCount ;
1966
2062
1967
2063
TransactionId replication_slot_xmin = InvalidTransactionId ;
1968
2064
TransactionId replication_slot_catalog_xmin = InvalidTransactionId ;
@@ -2007,12 +2103,19 @@ GetSnapshotData(Snapshot snapshot)
2007
2103
*/
2008
2104
LWLockAcquire (ProcArrayLock , LW_SHARED );
2009
2105
2106
+ if (GetSnapshotDataReuse (snapshot ))
2107
+ {
2108
+ LWLockRelease (ProcArrayLock );
2109
+ return snapshot ;
2110
+ }
2111
+
2010
2112
latest_completed = ShmemVariableCache -> latestCompletedXid ;
2011
2113
mypgxactoff = MyProc -> pgxactoff ;
2012
2114
myxid = other_xids [mypgxactoff ];
2013
2115
Assert (myxid == MyProc -> xid );
2014
2116
2015
2117
oldestxid = ShmemVariableCache -> oldestXid ;
2118
+ curXactCompletionCount = ShmemVariableCache -> xactCompletionCount ;
2016
2119
2017
2120
/* xmax is always latestCompletedXid + 1 */
2018
2121
xmax = XidFromFullTransactionId (latest_completed );
@@ -2266,6 +2369,7 @@ GetSnapshotData(Snapshot snapshot)
2266
2369
snapshot -> xcnt = count ;
2267
2370
snapshot -> subxcnt = subcount ;
2268
2371
snapshot -> suboverflowed = suboverflowed ;
2372
+ snapshot -> snapXactCompletionCount = curXactCompletionCount ;
2269
2373
2270
2374
snapshot -> curcid = GetCurrentCommandId (false);
2271
2375
@@ -2277,26 +2381,7 @@ GetSnapshotData(Snapshot snapshot)
2277
2381
snapshot -> regd_count = 0 ;
2278
2382
snapshot -> copied = false;
2279
2383
2280
- if (old_snapshot_threshold < 0 )
2281
- {
2282
- /*
2283
- * If not using "snapshot too old" feature, fill related fields with
2284
- * dummy values that don't require any locking.
2285
- */
2286
- snapshot -> lsn = InvalidXLogRecPtr ;
2287
- snapshot -> whenTaken = 0 ;
2288
- }
2289
- else
2290
- {
2291
- /*
2292
- * Capture the current time and WAL stream location in case this
2293
- * snapshot becomes old enough to need to fall back on the special
2294
- * "old snapshot" logic.
2295
- */
2296
- snapshot -> lsn = GetXLogInsertRecPtr ();
2297
- snapshot -> whenTaken = GetSnapshotCurrentTimestamp ();
2298
- MaintainOldSnapshotTimeMapping (snapshot -> whenTaken , xmin );
2299
- }
2384
+ GetSnapshotDataInitOldSnapshot (snapshot );
2300
2385
2301
2386
return snapshot ;
2302
2387
}
0 commit comments