From 4bda654ac7e2fa42d5e764ab44dc97f05de8725e Mon Sep 17 00:00:00 2001 From: Matthias van de Meent 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)