From 4bda654ac7e2fa42d5e764ab44dc97f05de8725e Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
Date: Sat, 1 Aug 2026 11:30:22 +0200
Subject: [PATCH v1 1/2] Draft: Unlocked path for GetSnapshotDataReuse

This applies in the cases where no transaction has committed since
the last snapshot was taken, and the backend already has an xmin
installed.
---
 contrib/amcheck/verify_heapam.c               |  1 +
 contrib/pg_visibility/pg_visibility.c         |  1 +
 src/backend/access/transam/clog.c             |  1 +
 src/backend/access/transam/commit_ts.c        |  1 +
 src/backend/access/transam/subtrans.c         |  1 +
 src/backend/access/transam/twophase.c         |  1 +
 src/backend/access/transam/varsup.c           |  1 +
 src/backend/access/transam/xlog.c             |  1 +
 src/backend/access/transam/xlogreader.c       |  5 ++
 src/backend/postmaster/datachecksum_state.c   |  1 +
 src/backend/storage/ipc/procarray.c           | 68 +++++++++++++---
 src/backend/storage/lmgr/predicate.c          |  1 +
 src/backend/utils/adt/xid8funcs.c             |  1 +
 src/include/access/transam.h                  | 61 --------------
 src/include/access/varsup.h                   | 80 +++++++++++++++++++
 .../modules/xid_wraparound/xid_wraparound.c   |  1 +
 16 files changed, 154 insertions(+), 72 deletions(-)
 create mode 100644 src/include/access/varsup.h

diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 20ff58aa782..e2ed055959a 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -17,6 +17,7 @@
 #include "access/relation.h"
 #include "access/table.h"
 #include "access/toast_internals.h"
+#include "access/varsup.h"
 #include "access/visibilitymap.h"
 #include "access/xact.h"
 #include "catalog/pg_am.h"
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index dfab0b64cf5..aea7919620b 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -12,6 +12,7 @@
 
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/varsup.h"
 #include "access/visibilitymap.h"
 #include "access/xloginsert.h"
 #include "catalog/pg_type.h"
diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 6f7f6b86eb6..6875d9b8255 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -36,6 +36,7 @@
 #include "access/clog.h"
 #include "access/slru.h"
 #include "access/transam.h"
+#include "access/varsup.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index 9e6fd5d4657..4d598431938 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -25,6 +25,7 @@
 #include "access/htup_details.h"
 #include "access/slru.h"
 #include "access/transam.h"
+#include "access/varsup.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "funcapi.h"
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index b79e648b899..6e2b8673217 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -31,6 +31,7 @@
 #include "access/slru.h"
 #include "access/subtrans.h"
 #include "access/transam.h"
+#include "access/varsup.h"
 #include "miscadmin.h"
 #include "pg_trace.h"
 #include "storage/subsystems.h"
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index fa3bc50ec48..58a5c0abb46 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -82,6 +82,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/twophase_rmgr.h"
+#include "access/varsup.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index dc5e32d86f3..8e32668654a 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -17,6 +17,7 @@
 #include "access/commit_ts.h"
 #include "access/subtrans.h"
 #include "access/transam.h"
+#include "access/varsup.h"
 #include "access/xact.h"
 #include "access/xlogutils.h"
 #include "miscadmin.h"
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f8b939853e9..99720998df6 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -55,6 +55,7 @@
 #include "access/timeline.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/varsup.h"
 #include "access/xact.h"
 #include "access/xlog_internal.h"
 #include "access/xlogarchive.h"
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 946907a2507..cf324e46399 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -26,6 +26,11 @@
 #endif
 
 #include "access/transam.h"
+
+#ifndef FRONTEND
+#include "access/varsup.h"
+#endif
+
 #include "access/xlog_internal.h"
 #include "access/xlogreader.h"
 #include "access/xlogrecord.h"
diff --git a/src/backend/postmaster/datachecksum_state.c b/src/backend/postmaster/datachecksum_state.c
index fc082ac37b9..8651e88dd20 100644
--- a/src/backend/postmaster/datachecksum_state.c
+++ b/src/backend/postmaster/datachecksum_state.c
@@ -189,6 +189,7 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/htup_details.h"
+#include "access/varsup.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 60336b31803..5025776a8c0 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -50,6 +50,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/varsup.h"
 #include "access/xact.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
@@ -446,7 +447,7 @@ ProcArrayShmemInit(void *arg)
 	procArray->lastOverflowedXid = InvalidTransactionId;
 	procArray->replication_slot_xmin = InvalidTransactionId;
 	procArray->replication_slot_catalog_xmin = InvalidTransactionId;
-	TransamVariables->xactCompletionCount = 1;
+	pg_atomic_init_u64(&TransamVariables->xactCompletionCount, 1);
 
 	allProcs = ProcGlobal->allProcs;
 }
@@ -587,7 +588,7 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
 		MaintainLatestCompletedXid(latestXid);
 
 		/* Same with xactCompletionCount  */
-		TransamVariables->xactCompletionCount++;
+		pg_atomic_add_fetch_u64(&TransamVariables->xactCompletionCount, 1);
 
 		ProcGlobal->xids[myoff] = InvalidTransactionId;
 		ProcGlobal->subxidStates[myoff].overflowed = false;
@@ -765,7 +766,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
 	MaintainLatestCompletedXid(latestXid);
 
 	/* Same with xactCompletionCount  */
-	TransamVariables->xactCompletionCount++;
+	pg_atomic_add_fetch_u64(&TransamVariables->xactCompletionCount, 1);
 }
 
 /*
@@ -934,7 +935,7 @@ ProcArrayClearTransaction(PGPROC *proc)
 	 * otherwise could end up reusing the snapshot later. Which would be bad,
 	 * because it might not count the prepared transaction as running.
 	 */
-	TransamVariables->xactCompletionCount++;
+	pg_atomic_add_fetch_u64(&TransamVariables->xactCompletionCount, 1);
 
 	/* Clear the subtransaction-XID cache too */
 	Assert(ProcGlobal->subxidStates[pgxactoff].count == proc->subxidStatus.count &&
@@ -2021,6 +2022,46 @@ GetMaxSnapshotSubxidCount(void)
 	return TOTAL_MAX_CACHED_SUBXIDS;
 }
 
+static inline bool
+GetSnapshotReuseUnlocked(Snapshot snapshot, bool *try_reuse)
+{
+	uint64		completions;
+
+	if (!TransactionIdIsValid(MyProc->xmin))
+	{
+		*try_reuse = true;
+		return false;
+	}
+
+	if (unlikely(snapshot->snapXactCompletionCount == 0))
+	{
+		*try_reuse = false;
+		return false;
+	}
+
+	pg_memory_barrier();
+
+	completions = pg_atomic_read_u64(&TransamVariables->xactCompletionCount);
+
+	if (snapshot->snapXactCompletionCount != completions)
+	{
+		*try_reuse = false;
+		return false;
+	}
+
+	pg_memory_barrier();
+
+	RecentXmin = snapshot->xmin;
+	Assert(TransactionIdPrecedesOrEquals(TransactionXmin, RecentXmin));
+
+	snapshot->curcid = GetCurrentCommandId(false);
+	snapshot->active_count = 0;
+	snapshot->regd_count = 0;
+	snapshot->copied = false;
+
+	return true;
+}
+
 /*
  * Helper function for GetSnapshotData() that checks if the bulk of the
  * visibility information in the snapshot is still valid. If so, it updates
@@ -2040,7 +2081,8 @@ GetSnapshotDataReuse(Snapshot snapshot)
 	if (unlikely(snapshot->snapXactCompletionCount == 0))
 		return false;
 
-	curXactCompletionCount = TransamVariables->xactCompletionCount;
+	curXactCompletionCount = pg_atomic_read_u64(&TransamVariables->xactCompletionCount);
+
 	if (curXactCompletionCount != snapshot->snapXactCompletionCount)
 		return false;
 
@@ -2120,6 +2162,7 @@ GetSnapshotData(Snapshot snapshot)
 	int			count = 0;
 	int			subcount = 0;
 	bool		suboverflowed = false;
+	bool		try_reuse = true;
 	FullTransactionId latest_completed;
 	TransactionId oldestxid;
 	int			mypgxactoff;
@@ -2171,13 +2214,16 @@ GetSnapshotData(Snapshot snapshot)
 		}
 	}
 
+	if (GetSnapshotReuseUnlocked(snapshot, &try_reuse))
+		return snapshot;
+
 	/*
 	 * It is sufficient to get shared lock on ProcArrayLock, even if we are
 	 * going to set MyProc->xmin.
 	 */
 	LWLockAcquire(ProcArrayLock, LW_SHARED);
 
-	if (GetSnapshotDataReuse(snapshot))
+	if (try_reuse && GetSnapshotDataReuse(snapshot))
 	{
 		LWLockRelease(ProcArrayLock);
 		return snapshot;
@@ -2189,7 +2235,7 @@ GetSnapshotData(Snapshot snapshot)
 	Assert(myxid == MyProc->xid);
 
 	oldestxid = TransamVariables->oldestXid;
-	curXactCompletionCount = TransamVariables->xactCompletionCount;
+	curXactCompletionCount = pg_atomic_read_u64(&TransamVariables->xactCompletionCount);
 
 	/* xmax is always latestCompletedXid + 1 */
 	xmax = XidFromFullTransactionId(latest_completed);
@@ -4072,7 +4118,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
 	MaintainLatestCompletedXid(latestXid);
 
 	/* ... and xactCompletionCount */
-	TransamVariables->xactCompletionCount++;
+	pg_atomic_add_fetch_u64(&TransamVariables->xactCompletionCount, 1);
 
 	LWLockRelease(ProcArrayLock);
 }
@@ -4525,7 +4571,7 @@ ExpireTreeKnownAssignedTransactionIds(TransactionId xid, int nsubxids,
 	MaintainLatestCompletedXidRecovery(max_xid);
 
 	/* ... and xactCompletionCount */
-	TransamVariables->xactCompletionCount++;
+	pg_atomic_add_fetch_u64(&TransamVariables->xactCompletionCount, 1);
 
 	LWLockRelease(ProcArrayLock);
 }
@@ -4552,7 +4598,7 @@ ExpireAllKnownAssignedTransactionIds(void)
 	 * Any transactions that were in-progress were effectively aborted, so
 	 * advance xactCompletionCount.
 	 */
-	TransamVariables->xactCompletionCount++;
+	pg_atomic_add_fetch_u64(&TransamVariables->xactCompletionCount, 1);
 
 	/*
 	 * Reset lastOverflowedXid.  Currently, lastOverflowedXid has no use after
@@ -4581,7 +4627,7 @@ ExpireOldKnownAssignedTransactionIds(TransactionId xid)
 	MaintainLatestCompletedXidRecovery(latestXid);
 
 	/* ... and xactCompletionCount */
-	TransamVariables->xactCompletionCount++;
+	pg_atomic_add_fetch_u64(&TransamVariables->xactCompletionCount, 1);
 
 	/*
 	 * Reset lastOverflowedXid if we know all transactions that have been
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 0ae85b7d5b4..818ae298e04 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -198,6 +198,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/twophase_rmgr.h"
+#include "access/varsup.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "miscadmin.h"
diff --git a/src/backend/utils/adt/xid8funcs.c b/src/backend/utils/adt/xid8funcs.c
index c607e78d9ac..0c907a1a728 100644
--- a/src/backend/utils/adt/xid8funcs.c
+++ b/src/backend/utils/adt/xid8funcs.c
@@ -27,6 +27,7 @@
 #include "postgres.h"
 
 #include "access/transam.h"
+#include "access/varsup.h"
 #include "access/xact.h"
 #include "funcapi.h"
 #include "lib/qunique.h"
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 55a4ab26b34..d41d6fd32bb 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -196,64 +196,6 @@ FullTransactionIdAdvance(FullTransactionId *dest)
 #define FirstUnpinnedObjectId	12000
 #define FirstNormalObjectId		16384
 
-/*
- * TransamVariables is a data structure in shared memory that is used to track
- * OID and XID assignment state.  For largely historical reasons, there is
- * just one struct with different fields that are protected by different
- * LWLocks.
- *
- * Note: xidWrapLimit and oldestXidDB are not "active" values, but are
- * used just to generate useful messages when xidWarnLimit or xidStopLimit
- * are exceeded.
- */
-typedef struct TransamVariablesData
-{
-	/*
-	 * These fields are protected by OidGenLock.
-	 */
-	Oid			nextOid;		/* next OID to assign */
-	uint32		oidCount;		/* OIDs available before must do XLOG work */
-
-	/*
-	 * These fields are protected by XidGenLock.
-	 */
-	FullTransactionId nextXid;	/* next 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 xidWrapLimit; /* where the world ends */
-	Oid			oldestXidDB;	/* database with minimum datfrozenxid */
-
-	/*
-	 * These fields are protected by CommitTsLock
-	 */
-	TransactionId oldestCommitTsXid;
-	TransactionId newestCommitTsXid;
-
-	/*
-	 * These fields are protected by ProcArrayLock.
-	 */
-	FullTransactionId latestCompletedXid;	/* newest full XID that has
-											 * committed or aborted */
-
-	/*
-	 * Number of top-level transactions with xids (i.e. which may have
-	 * modified the database) that completed in some form since the start of
-	 * the server. This currently is solely used to check whether
-	 * GetSnapshotData() needs to recompute the contents of the snapshot, or
-	 * not. There are likely other users of this.  Always above 1.
-	 */
-	uint64		xactCompletionCount;
-
-	/*
-	 * These fields are protected by XactTruncationLock
-	 */
-	TransactionId oldestClogXid;	/* oldest it's safe to look up in clog */
-
-} TransamVariablesData;
-
 
 
 /*
@@ -329,9 +271,6 @@ TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
 /* in transam/xact.c */
 extern bool TransactionStartedDuringRecovery(void);
 
-/* in transam/varsup.c */
-extern PGDLLIMPORT TransamVariablesData *TransamVariables;
-
 /*
  * prototypes for functions in transam/transam.c
  */
diff --git a/src/include/access/varsup.h b/src/include/access/varsup.h
new file mode 100644
index 00000000000..f1e12d90962
--- /dev/null
+++ b/src/include/access/varsup.h
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+*
+ * varsup.h
+ *	  postgres transaction shmem data
+ *
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/varsup.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef VARSUP_H
+#define VARSUP_H
+#include "transam.h"
+#include "port/atomics.h"
+
+/*
+ * TransamVariables is a data structure in shared memory that is used to track
+ * OID and XID assignment state.  For largely historical reasons, there is
+ * just one struct with different fields that are protected by different
+ * LWLocks.
+ *
+ * Note: xidWrapLimit and oldestXidDB are not "active" values, but are
+ * used just to generate useful messages when xidWarnLimit or xidStopLimit
+ * are exceeded.
+ */
+typedef struct TransamVariablesData
+{
+	/*
+	 * These fields are protected by OidGenLock.
+	 */
+	Oid			nextOid;		/* next OID to assign */
+	uint32		oidCount;		/* OIDs available before must do XLOG work */
+
+	/*
+	 * These fields are protected by XidGenLock.
+	 */
+	FullTransactionId nextXid;	/* next 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 xidWrapLimit; /* where the world ends */
+	Oid			oldestXidDB;	/* database with minimum datfrozenxid */
+
+	/*
+	 * These fields are protected by CommitTsLock
+	 */
+	TransactionId oldestCommitTsXid;
+	TransactionId newestCommitTsXid;
+
+	/*
+	 * These fields are protected by ProcArrayLock.
+	 */
+	FullTransactionId latestCompletedXid;	/* newest full XID that has
+											 * committed or aborted */
+
+	/*
+	 * Number of top-level transactions with xids (i.e. which may have
+	 * modified the database) that completed in some form since the start of
+	 * the server. This currently is solely used to check whether
+	 * GetSnapshotData() needs to recompute the contents of the snapshot, or
+	 * not. There are likely other users of this.  Always above 1.
+	 */
+	pg_atomic_uint64 xactCompletionCount;
+
+	/*
+	 * These fields are protected by XactTruncationLock
+	 */
+	TransactionId oldestClogXid;	/* oldest it's safe to look up in clog */
+
+} TransamVariablesData;
+
+/* in transam/varsup.c */
+extern PGDLLIMPORT TransamVariablesData *TransamVariables;
+
+#endif /* VARSUP_H */
diff --git a/src/test/modules/xid_wraparound/xid_wraparound.c b/src/test/modules/xid_wraparound/xid_wraparound.c
index ca25d7e0206..cf9fe0a355e 100644
--- a/src/test/modules/xid_wraparound/xid_wraparound.c
+++ b/src/test/modules/xid_wraparound/xid_wraparound.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/xact.h"
+#include "access/varsup.h"
 #include "miscadmin.h"
 #include "storage/proc.h"
 #include "utils/xid8.h"
-- 
2.50.1 (Apple Git-155)

