Good day.

Sami Imseih, I've introduced new precalculated variables:

    MaxChildren = MaxBackends + NUM_AUXILIARY_PROCS;
    TotalProcs = MaxChildren + max_prepared_xacts;
    TotalXactProcs = MaxBackends + max_prepared_xacts;

and spread their usage through the sources.
I don't know how committers will accept this change. But they will
certainly make their own version of patch, so I don't bother much.

But I still use inline procs for access to the arrays. Asserts cost nothing
in release build. And new version of functions doesn't branch.

And you're right: looks like there were mistake in previous version so
GetOldestVisibleMXactId became unused. New version has no this mistake.

Chao Li, new access functions doen't branch because I've made separate
functions to store at slots for prepared transactions.

--

regards
Yura Sokolov aka funny-falcon
From a9db95d6f97300d49c5c3f8ac1ecb87c9d7cf429 Mon Sep 17 00:00:00 2001
From: Yura Sokolov <[email protected]>
Date: Thu, 26 Feb 2026 17:41:40 +0300
Subject: [PATCH v4 1/2] Add precalculated proc counts to reduce repetition.

Lets name common sums:
MaxBackends + NUM_AUXILIARY_PROCS => MaxChildren
MaxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts => TotalProcs
MaxBackends + max_prepared_xacts => TotalXactProcs

It also may simplify api for extension.
---
 src/backend/access/transam/multixact.c      |  2 +-
 src/backend/access/transam/xlogwait.c       |  7 +++----
 src/backend/postmaster/launch_backend.c     |  4 ++++
 src/backend/storage/aio/aio_init.c          |  4 ++--
 src/backend/storage/aio/method_io_uring.c   |  2 +-
 src/backend/storage/buffer/bufmgr.c         |  2 +-
 src/backend/storage/ipc/procarray.c         |  2 +-
 src/backend/storage/ipc/procsignal.c        |  2 +-
 src/backend/storage/ipc/sinvaladt.c         |  2 +-
 src/backend/storage/lmgr/lock.c             |  9 ++++-----
 src/backend/storage/lmgr/predicate.c        | 13 +++++--------
 src/backend/storage/lmgr/proc.c             | 13 ++++---------
 src/backend/utils/activity/backend_status.c |  2 +-
 src/backend/utils/init/globals.c            |  7 +++++--
 src/backend/utils/init/postinit.c           |  9 +++++++--
 src/include/miscadmin.h                     |  3 +++
 16 files changed, 44 insertions(+), 39 deletions(-)

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 90ec87d9dd6..a35671ea346 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -210,7 +210,7 @@ typedef struct MultiXactStateData
 /*
  * Size of OldestMemberMXactId and OldestVisibleMXactId arrays.
  */
-#define MaxOldestSlot	(MaxBackends + max_prepared_xacts)
+#define MaxOldestSlot	TotalXactProcs
 
 /* Pointers to the state data in shared memory */
 static MultiXactStateData *MultiXactState;
diff --git a/src/backend/access/transam/xlogwait.c b/src/backend/access/transam/xlogwait.c
index d286ff63123..4c2f3085e34 100644
--- a/src/backend/access/transam/xlogwait.c
+++ b/src/backend/access/transam/xlogwait.c
@@ -115,7 +115,7 @@ WaitLSNShmemSize(void)
 	Size		size;
 
 	size = offsetof(WaitLSNState, procInfos);
-	size = add_size(size, mul_size(MaxBackends + NUM_AUXILIARY_PROCS, sizeof(WaitLSNProcInfo)));
+	size = add_size(size, mul_size(MaxChildren, sizeof(WaitLSNProcInfo)));
 	return size;
 }
 
@@ -140,8 +140,7 @@ WaitLSNShmemInit(void)
 		}
 
 		/* Initialize process info array */
-		memset(&waitLSNState->procInfos, 0,
-			   (MaxBackends + NUM_AUXILIARY_PROCS) * sizeof(WaitLSNProcInfo));
+		memset(&waitLSNState->procInfos, 0, MaxChildren * sizeof(WaitLSNProcInfo));
 	}
 }
 
@@ -382,7 +381,7 @@ WaitForLSN(WaitLSNType lsnType, XLogRecPtr targetLSN, int64 timeout)
 	Assert(waitLSNState);
 
 	/* Should have a valid proc number */
-	Assert(MyProcNumber >= 0 && MyProcNumber < MaxBackends + NUM_AUXILIARY_PROCS);
+	Assert(MyProcNumber >= 0 && MyProcNumber < MaxChildren);
 
 	if (timeout > 0)
 	{
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index e9134b9751b..1b5c5b7f550 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -62,6 +62,7 @@
 
 #ifdef EXEC_BACKEND
 
+#include "access/twophase.h"
 #include "common/file_utils.h"
 #include "storage/fd.h"
 #include "storage/lwlock.h"
@@ -1010,6 +1011,9 @@ restore_backend_variables(BackendParameters *param)
 	max_safe_fds = param->max_safe_fds;
 
 	MaxBackends = param->MaxBackends;
+	MaxChildren = MaxBackends + NUM_AUXILIARY_PROCS;
+	TotalProcs = MaxChildren + max_prepared_xacts;
+	TotalXactProcs = MaxBackends + max_prepared_xacts;
 	num_pmchild_slots = param->num_pmchild_slots;
 
 #ifdef WIN32
diff --git a/src/backend/storage/aio/aio_init.c b/src/backend/storage/aio/aio_init.c
index d3c68d8b04c..df1e750e802 100644
--- a/src/backend/storage/aio/aio_init.c
+++ b/src/backend/storage/aio/aio_init.c
@@ -42,7 +42,7 @@ AioProcs(void)
 	 * guarantee that nothing gets assigned to an IO worker's ProcNumber if we
 	 * just subtracted MAX_IO_WORKERS.
 	 */
-	return MaxBackends + NUM_AUXILIARY_PROCS;
+	return MaxChildren;
 }
 
 static Size
@@ -100,7 +100,7 @@ AioChooseMaxConcurrency(void)
 	int			max_proportional_pins;
 
 	/* Similar logic to LimitAdditionalPins() */
-	max_backends = MaxBackends + NUM_AUXILIARY_PROCS;
+	max_backends = MaxChildren;
 	max_proportional_pins = NBuffers / max_backends;
 
 	max_proportional_pins = Max(max_proportional_pins, 1);
diff --git a/src/backend/storage/aio/method_io_uring.c b/src/backend/storage/aio/method_io_uring.c
index ed6e71bcd46..904deeb724f 100644
--- a/src/backend/storage/aio/method_io_uring.c
+++ b/src/backend/storage/aio/method_io_uring.c
@@ -131,7 +131,7 @@ pgaio_uring_procs(void)
 	 * We can subtract MAX_IO_WORKERS here as io workers are never used at the
 	 * same time as io_method=io_uring.
 	 */
-	return MaxBackends + NUM_AUXILIARY_PROCS - MAX_IO_WORKERS;
+	return MaxChildren - MAX_IO_WORKERS;
 }
 
 /*
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index d1babaff023..c1d6306a073 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -4129,7 +4129,7 @@ InitBufferManagerAccess(void)
 	 * allow plenty of pins.  LimitAdditionalPins() and
 	 * GetAdditionalPinLimit() can be used to check the remaining balance.
 	 */
-	MaxProportionalPins = NBuffers / (MaxBackends + NUM_AUXILIARY_PROCS);
+	MaxProportionalPins = NBuffers / MaxChildren;
 
 	memset(&PrivateRefCountArray, 0, sizeof(PrivateRefCountArray));
 	memset(&PrivateRefCountArrayKeys, 0, sizeof(PrivateRefCountArrayKeys));
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 40312df2cac..807c292f481 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -381,7 +381,7 @@ ProcArrayShmemSize(void)
 	Size		size;
 
 	/* Size of the ProcArray structure itself */
-#define PROCARRAY_MAXPROCS	(MaxBackends + max_prepared_xacts)
+#define PROCARRAY_MAXPROCS	TotalXactProcs
 
 	size = offsetof(ProcArrayStruct, pgprocnos);
 	size = add_size(size, mul_size(sizeof(int), PROCARRAY_MAXPROCS));
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 7505c9d3a37..4694f645805 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -93,7 +93,7 @@ struct ProcSignalHeader
  * more than one of any auxiliary process type at a time, except for
  * IO workers.)
  */
-#define NumProcSignalSlots	(MaxBackends + NUM_AUXILIARY_PROCS)
+#define NumProcSignalSlots	MaxChildren
 
 /* Check whether the relevant type bit is set in the flags. */
 #define BARRIER_SHOULD_CHECK(flags, type) \
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index a7a7cc4f0a9..ec030b9466b 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -201,7 +201,7 @@ typedef struct SISeg
  * more than one of any auxiliary process type at a time, except for
  * IO workers.)
  */
-#define NumProcStateSlots	(MaxBackends + NUM_AUXILIARY_PROCS)
+#define NumProcStateSlots	MaxChildren
 
 static SISeg *shmInvalBuffer;	/* pointer to the shared inval buffer */
 
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index d930c66cdbd..d3294200c2c 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -53,8 +53,7 @@
 int			max_locks_per_xact; /* used to set the lock table size */
 bool		log_lock_failures = false;
 
-#define NLOCKENTS() \
-	mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
+#define NLOCKENTS() mul_size(max_locks_per_xact, TotalXactProcs)
 
 
 /*
@@ -3097,10 +3096,10 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
 			vxids = (VirtualTransactionId *)
 				MemoryContextAlloc(TopMemoryContext,
 								   sizeof(VirtualTransactionId) *
-								   (MaxBackends + max_prepared_xacts + 1));
+								   (TotalXactProcs + 1));
 	}
 	else
-		vxids = palloc0_array(VirtualTransactionId, (MaxBackends + max_prepared_xacts + 1));
+		vxids = palloc0_array(VirtualTransactionId, (TotalXactProcs + 1));
 
 	/* Compute hash code and partition lock, and look up conflicting modes. */
 	hashcode = LockTagHashCode(locktag);
@@ -3259,7 +3258,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
 
 	LWLockRelease(partitionLock);
 
-	if (count > MaxBackends + max_prepared_xacts)	/* should never happen */
+	if (count > TotalXactProcs)	/* should never happen */
 		elog(PANIC, "too many conflicting locks found");
 
 	vxids[count].procNumber = INVALID_PROC_NUMBER;
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index fe75ead3501..5a14422ad4c 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -261,8 +261,7 @@
 #define PredicateLockHashPartitionLockByIndex(i) \
 	(&MainLWLockArray[PREDICATELOCK_MANAGER_LWLOCK_OFFSET + (i)].lock)
 
-#define NPREDICATELOCKTARGETENTS() \
-	mul_size(max_predicate_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
+#define NPREDICATELOCKTARGETENTS() mul_size(max_predicate_locks_per_xact, TotalXactProcs)
 
 #define SxactIsOnFinishedList(sxact) (!dlist_node_is_detached(&(sxact)->finishedLink))
 
@@ -1214,7 +1213,7 @@ PredicateLockShmemInit(void)
 	 * Compute size for serializable transaction hashtable. Note these
 	 * calculations must agree with PredicateLockShmemSize!
 	 */
-	max_table_size = (MaxBackends + max_prepared_xacts);
+	max_table_size = TotalXactProcs;
 
 	/*
 	 * Allocate a list to hold information on transactions participating in
@@ -1376,7 +1375,7 @@ PredicateLockShmemSize(void)
 	size = add_size(size, size / 10);
 
 	/* transaction list */
-	max_table_size = MaxBackends + max_prepared_xacts;
+	max_table_size = TotalXactProcs;
 	max_table_size *= 10;
 	size = add_size(size, PredXactListDataSize);
 	size = add_size(size, mul_size((Size) max_table_size,
@@ -1902,8 +1901,7 @@ GetSerializableTransactionSnapshotInt(Snapshot snapshot,
 	else
 	{
 		++(PredXact->WritableSxactCount);
-		Assert(PredXact->WritableSxactCount <=
-			   (MaxBackends + max_prepared_xacts));
+		Assert(PredXact->WritableSxactCount <= TotalXactProcs);
 	}
 
 	/* Maintain serializable global xmin info. */
@@ -4965,8 +4963,7 @@ predicatelock_twophase_recover(FullTransactionId fxid, uint16 info,
 		if (!SxactIsReadOnly(sxact))
 		{
 			++(PredXact->WritableSxactCount);
-			Assert(PredXact->WritableSxactCount <=
-				   (MaxBackends + max_prepared_xacts));
+			Assert(PredXact->WritableSxactCount <= TotalXactProcs);
 		}
 
 		/*
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 771b006b522..52cc3c7c702 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -87,8 +87,6 @@ static Size
 PGProcShmemSize(void)
 {
 	Size		size = 0;
-	Size		TotalProcs =
-		add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
 
 	size = add_size(size, mul_size(TotalProcs, sizeof(PGPROC)));
 	size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->xids)));
@@ -105,8 +103,6 @@ static Size
 FastPathLockShmemSize(void)
 {
 	Size		size = 0;
-	Size		TotalProcs =
-		add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
 	Size		fpLockBitsSize,
 				fpRelIdSize;
 
@@ -151,7 +147,7 @@ ProcGlobalSemas(void)
 	 * We need a sema per backend (including autovacuum), plus one for each
 	 * auxiliary process.
 	 */
-	return MaxBackends + NUM_AUXILIARY_PROCS;
+	return MaxChildren;
 }
 
 /*
@@ -186,7 +182,6 @@ InitProcGlobal(void)
 	int			i,
 				j;
 	bool		found;
-	uint32		TotalProcs = MaxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts;
 
 	/* Used for setup of per-backend fast-path slots. */
 	char	   *fpPtr,
@@ -238,7 +233,7 @@ InitProcGlobal(void)
 
 	ProcGlobal->allProcs = procs;
 	/* XXX allProcCount isn't really all of them; it excludes prepared xacts */
-	ProcGlobal->allProcCount = MaxBackends + NUM_AUXILIARY_PROCS;
+	ProcGlobal->allProcCount = MaxChildren;
 
 	/*
 	 * Allocate arrays mirroring PGPROC fields in a dense manner. See
@@ -304,7 +299,7 @@ InitProcGlobal(void)
 		 * dummy PGPROCs don't need these though - they're never associated
 		 * with a real process
 		 */
-		if (i < MaxBackends + NUM_AUXILIARY_PROCS)
+		if (i < MaxChildren)
 		{
 			proc->sem = PGSemaphoreCreate();
 			InitSharedLatch(&(proc->procLatch));
@@ -369,7 +364,7 @@ InitProcGlobal(void)
 	 * processes and prepared transactions.
 	 */
 	AuxiliaryProcs = &procs[MaxBackends];
-	PreparedXactProcs = &procs[MaxBackends + NUM_AUXILIARY_PROCS];
+	PreparedXactProcs = &procs[MaxChildren];
 }
 
 /*
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index cd087129469..ed797da6bee 100644
--- a/src/backend/utils/activity/backend_status.c
+++ b/src/backend/utils/activity/backend_status.c
@@ -32,7 +32,7 @@
  * real processes.)
  * ----------
  */
-#define NumBackendStatSlots (MaxBackends + NUM_AUXILIARY_PROCS)
+#define NumBackendStatSlots MaxChildren
 
 
 /* ----------
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 36ad708b360..1fbef826a8b 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -136,14 +136,17 @@ int			max_parallel_maintenance_workers = 2;
 /*
  * Primary determinants of sizes of shared-memory structures.
  *
- * MaxBackends is computed by PostmasterMain after modules have had a chance to
- * register background workers.
+ * MaxBackends, MaxChildren and TotalProcs are computed by PostmasterMain after
+ * modules have had a chance to register background workers.
  */
 int			NBuffers = 16384;
 int			MaxConnections = 100;
 int			max_worker_processes = 8;
 int			max_parallel_workers = 8;
 int			MaxBackends = 0;
+int			MaxChildren = 0;
+int			TotalProcs = 0;
+int			TotalXactProcs = 0;
 
 /* GUC parameters for vacuum */
 int			VacuumBufferUsageLimit = 2048;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index b59e08605cc..cc295878868 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -24,6 +24,7 @@
 #include "access/htup_details.h"
 #include "access/session.h"
 #include "access/tableam.h"
+#include "access/twophase.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -563,14 +564,18 @@ InitializeMaxBackends(void)
 	MaxBackends = MaxConnections + autovacuum_worker_slots +
 		max_worker_processes + max_wal_senders + NUM_SPECIAL_WORKER_PROCS;
 
-	if (MaxBackends > MAX_BACKENDS)
+	MaxChildren = MaxBackends + NUM_AUXILIARY_PROCS;
+	if (MaxChildren > MAX_BACKENDS)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("too many server processes configured"),
 				 errdetail("\"max_connections\" (%d) plus \"autovacuum_worker_slots\" (%d) plus \"max_worker_processes\" (%d) plus \"max_wal_senders\" (%d) must be less than %d.",
 						   MaxConnections, autovacuum_worker_slots,
 						   max_worker_processes, max_wal_senders,
-						   MAX_BACKENDS - (NUM_SPECIAL_WORKER_PROCS - 1))));
+						   MAX_BACKENDS - NUM_AUXILIARY_PROCS - (NUM_SPECIAL_WORKER_PROCS - 1))));
+
+	TotalProcs = MaxChildren + max_prepared_xacts;
+	TotalXactProcs = MaxBackends + max_prepared_xacts;
 }
 
 /*
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f16f35659b9..b2498455ea4 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -175,6 +175,9 @@ extern PGDLLIMPORT int data_directory_mode;
 
 extern PGDLLIMPORT int NBuffers;
 extern PGDLLIMPORT int MaxBackends;
+extern PGDLLIMPORT int MaxChildren;
+extern PGDLLIMPORT int TotalProcs;
+extern PGDLLIMPORT int TotalXactProcs;
 extern PGDLLIMPORT int MaxConnections;
 extern PGDLLIMPORT int max_worker_processes;
 extern PGDLLIMPORT int max_parallel_workers;
-- 
2.51.0

From d3c2bb72de0b4f5e3149cd8f01493c37315067ce Mon Sep 17 00:00:00 2001
From: Yura Sokolov <[email protected]>
Date: Thu, 26 Feb 2026 19:08:57 +0300
Subject: [PATCH v4 2/2] Fix multixacts OldestMemberMXactId and
 OldestVisibleMXactId usage

Due to [1], OldestMemberMXactId is no longer accessed by synthetic
dummyBackendId, but rather with pgprocno. Procs for prepared xacts are
placed after auxiliary procs, therefore calculation for MaxOldestSlot
became invalid.

On the other hand, OldestVisibleMXactId is used only for real backends,
and so never accessed at index greater than MaxBackends.

Lets separate size calculation for arrays and use converted index to
store oldest member mxact for prepared transactions.

[1] ab355e3a88de745 "Redefine backend ID to be an index into the proc array"
---
 src/backend/access/transam/multixact.c | 104 +++++++++++++++++--------
 1 file changed, 72 insertions(+), 32 deletions(-)

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index a35671ea346..42859828513 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -162,10 +162,6 @@ typedef struct MultiXactStateData
 	 * immediately following the MultiXactStateData struct. Each is indexed by
 	 * ProcNumber.
 	 *
-	 * In both arrays, there's a slot for all normal backends
-	 * (0..MaxBackends-1) followed by a slot for max_prepared_xacts prepared
-	 * transactions.
-	 *
 	 * OldestMemberMXactId[k] is the oldest MultiXactId each backend's current
 	 * transaction(s) could possibly be a member of, or InvalidMultiXactId
 	 * when the backend has no live transaction that could possibly be a
@@ -176,6 +172,10 @@ typedef struct MultiXactStateData
 	 * member of a MultiXact, and that MultiXact would have to be created
 	 * during or after the lock acquisition.)
 	 *
+	 * In OldestMemberMXactId array, there's a slot for all normal backends
+	 * (0..MaxBackends-1) followed by a slot for max_prepared_xacts prepared
+	 * transactions.
+	 *
 	 * OldestVisibleMXactId[k] is the oldest MultiXactId each backend's
 	 * current transaction(s) think is potentially live, or InvalidMultiXactId
 	 * when not in a transaction or not in a transaction that's paid any
@@ -187,6 +187,9 @@ typedef struct MultiXactStateData
 	 * than its own OldestVisibleMXactId[] setting; this is necessary because
 	 * the relevant SLRU data can be concurrently truncated away.
 	 *
+	 * In OldestVisibleMXactId array, there's a slot for all normal backends
+	 * (0..MaxBackends-1) only. No slots for prepared transactions.
+	 *
 	 * The oldest valid value among all of the OldestMemberMXactId[] and
 	 * OldestVisibleMXactId[] entries is considered by vacuum as the earliest
 	 * possible value still having any live member transaction -- OldestMxact.
@@ -210,13 +213,54 @@ typedef struct MultiXactStateData
 /*
  * Size of OldestMemberMXactId and OldestVisibleMXactId arrays.
  */
-#define MaxOldestSlot	TotalXactProcs
+#define MaxMemberSlot	TotalXactProcs
+#define MaxVisibleSlot	MaxBackends
+/*
+ * Total size of perBackendXactIds.
+ */
+#define MaxOldestSlot	(MaxMemberSlot + MaxVisibleSlot)
 
 /* Pointers to the state data in shared memory */
 static MultiXactStateData *MultiXactState;
 static MultiXactId *OldestMemberMXactId;
 static MultiXactId *OldestVisibleMXactId;
 
+static inline MultiXactId
+GetOldestMemberMXactId(ProcNumber procno)
+{
+	Assert(procno >= 0 && procno < MaxBackends);
+	return OldestMemberMXactId[procno];
+}
+
+static inline void
+SetOldestMemberMXactId(ProcNumber procno, MultiXactId mxact)
+{
+	Assert(procno >= 0 && procno < MaxBackends);
+	OldestMemberMXactId[procno] = mxact;
+}
+
+static inline void
+SetOldestPreparedMXactId(ProcNumber procno, MultiXactId mxact)
+{
+	Assert(procno >= MaxChildren && procno < TotalProcs);
+	/* This check looks redundant, but it is future proof. */
+	Assert(procno - MaxChildren + MaxBackends < MaxMemberSlot);
+	OldestMemberMXactId[procno - MaxChildren + MaxBackends] = mxact;
+}
+
+static inline MultiXactId
+GetOldestVisibleMXactId(ProcNumber procno)
+{
+	Assert(procno >= 0 && procno < MaxVisibleSlot);
+	return OldestVisibleMXactId[procno];
+}
+
+static inline void
+SetOldestVisibleMXactId(ProcNumber procno, MultiXactId mxact)
+{
+	Assert(procno >= 0 && procno < MaxVisibleSlot);
+	OldestVisibleMXactId[procno] = mxact;
+}
 
 /*
  * Definitions for the backend-local MultiXactId cache.
@@ -308,7 +352,7 @@ MultiXactIdCreate(TransactionId xid1, MultiXactStatus status1,
 	Assert(!TransactionIdEquals(xid1, xid2) || (status1 != status2));
 
 	/* MultiXactIdSetOldestMember() must have been called already. */
-	Assert(MultiXactIdIsValid(OldestMemberMXactId[MyProcNumber]));
+	Assert(MultiXactIdIsValid(GetOldestMemberMXactId(MyProcNumber)));
 
 	/*
 	 * Note: unlike MultiXactIdExpand, we don't bother to check that both XIDs
@@ -362,7 +406,7 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
 	Assert(TransactionIdIsValid(xid));
 
 	/* MultiXactIdSetOldestMember() must have been called already. */
-	Assert(MultiXactIdIsValid(OldestMemberMXactId[MyProcNumber]));
+	Assert(MultiXactIdIsValid(GetOldestMemberMXactId(MyProcNumber)));
 
 	debug_elog5(DEBUG2, "Expand: received multi %u, xid %u status %s",
 				multi, xid, mxstatus_to_string(status));
@@ -536,7 +580,7 @@ MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
 void
 MultiXactIdSetOldestMember(void)
 {
-	if (!MultiXactIdIsValid(OldestMemberMXactId[MyProcNumber]))
+	if (!MultiXactIdIsValid(GetOldestMemberMXactId(MyProcNumber)))
 	{
 		MultiXactId nextMXact;
 
@@ -558,7 +602,7 @@ MultiXactIdSetOldestMember(void)
 
 		nextMXact = MultiXactState->nextMXact;
 
-		OldestMemberMXactId[MyProcNumber] = nextMXact;
+		SetOldestMemberMXactId(MyProcNumber, nextMXact);
 
 		LWLockRelease(MultiXactGenLock);
 
@@ -586,7 +630,7 @@ MultiXactIdSetOldestMember(void)
 static void
 MultiXactIdSetOldestVisible(void)
 {
-	if (!MultiXactIdIsValid(OldestVisibleMXactId[MyProcNumber]))
+	if (!MultiXactIdIsValid(GetOldestVisibleMXactId(MyProcNumber)))
 	{
 		MultiXactId oldestMXact;
 		int			i;
@@ -594,7 +638,7 @@ MultiXactIdSetOldestVisible(void)
 		LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
 
 		oldestMXact = MultiXactState->nextMXact;
-		for (i = 0; i < MaxOldestSlot; i++)
+		for (i = 0; i < MaxMemberSlot; i++)
 		{
 			MultiXactId thisoldest = OldestMemberMXactId[i];
 
@@ -603,7 +647,7 @@ MultiXactIdSetOldestVisible(void)
 				oldestMXact = thisoldest;
 		}
 
-		OldestVisibleMXactId[MyProcNumber] = oldestMXact;
+		SetOldestVisibleMXactId(MyProcNumber, oldestMXact);
 
 		LWLockRelease(MultiXactGenLock);
 
@@ -1152,7 +1196,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
 	 * multi.  It cannot possibly still be running.
 	 */
 	if (isLockOnly &&
-		MultiXactIdPrecedes(multi, OldestVisibleMXactId[MyProcNumber]))
+		MultiXactIdPrecedes(multi, GetOldestVisibleMXactId(MyProcNumber)))
 	{
 		debug_elog2(DEBUG2, "GetMembers: a locker-only multi is too old");
 		*members = NULL;
@@ -1574,8 +1618,8 @@ AtEOXact_MultiXact(void)
 	 * We assume that storing a MultiXactId is atomic and so we need not take
 	 * MultiXactGenLock to do this.
 	 */
-	OldestMemberMXactId[MyProcNumber] = InvalidMultiXactId;
-	OldestVisibleMXactId[MyProcNumber] = InvalidMultiXactId;
+	SetOldestMemberMXactId(MyProcNumber, InvalidMultiXactId);
+	SetOldestVisibleMXactId(MyProcNumber, InvalidMultiXactId);
 
 	/*
 	 * Discard the local MultiXactId cache.  Since MXactContext was created as
@@ -1595,7 +1639,7 @@ AtEOXact_MultiXact(void)
 void
 AtPrepare_MultiXact(void)
 {
-	MultiXactId myOldestMember = OldestMemberMXactId[MyProcNumber];
+	MultiXactId myOldestMember = GetOldestMemberMXactId(MyProcNumber);
 
 	if (MultiXactIdIsValid(myOldestMember))
 		RegisterTwoPhaseRecord(TWOPHASE_RM_MULTIXACT_ID, 0,
@@ -1615,7 +1659,7 @@ PostPrepare_MultiXact(FullTransactionId fxid)
 	 * Transfer our OldestMemberMXactId value to the slot reserved for the
 	 * prepared transaction.
 	 */
-	myOldestMember = OldestMemberMXactId[MyProcNumber];
+	myOldestMember = GetOldestMemberMXactId(MyProcNumber);
 	if (MultiXactIdIsValid(myOldestMember))
 	{
 		ProcNumber	dummyProcNumber = TwoPhaseGetDummyProcNumber(fxid, false);
@@ -1628,8 +1672,8 @@ PostPrepare_MultiXact(FullTransactionId fxid)
 		 */
 		LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
 
-		OldestMemberMXactId[dummyProcNumber] = myOldestMember;
-		OldestMemberMXactId[MyProcNumber] = InvalidMultiXactId;
+		SetOldestPreparedMXactId(dummyProcNumber, myOldestMember);
+		SetOldestMemberMXactId(MyProcNumber, InvalidMultiXactId);
 
 		LWLockRelease(MultiXactGenLock);
 	}
@@ -1642,7 +1686,7 @@ PostPrepare_MultiXact(FullTransactionId fxid)
 	 * We assume that storing a MultiXactId is atomic and so we need not take
 	 * MultiXactGenLock to do this.
 	 */
-	OldestVisibleMXactId[MyProcNumber] = InvalidMultiXactId;
+	SetOldestVisibleMXactId(MyProcNumber, InvalidMultiXactId);
 
 	/*
 	 * Discard the local MultiXactId cache like in AtEOXact_MultiXact.
@@ -1669,7 +1713,7 @@ multixact_twophase_recover(FullTransactionId fxid, uint16 info,
 	Assert(len == sizeof(MultiXactId));
 	oldestMember = *((MultiXactId *) recdata);
 
-	OldestMemberMXactId[dummyProcNumber] = oldestMember;
+	SetOldestPreparedMXactId(dummyProcNumber, oldestMember);
 }
 
 /*
@@ -1684,7 +1728,7 @@ multixact_twophase_postcommit(FullTransactionId fxid, uint16 info,
 
 	Assert(len == sizeof(MultiXactId));
 
-	OldestMemberMXactId[dummyProcNumber] = InvalidMultiXactId;
+	SetOldestPreparedMXactId(dummyProcNumber, InvalidMultiXactId);
 }
 
 /*
@@ -1708,10 +1752,9 @@ MultiXactShmemSize(void)
 {
 	Size		size;
 
-	/* We need 2*MaxOldestSlot perBackendXactIds[] entries */
 #define SHARED_MULTIXACT_STATE_SIZE \
 	add_size(offsetof(MultiXactStateData, perBackendXactIds), \
-			 mul_size(sizeof(MultiXactId) * 2, MaxOldestSlot))
+			 mul_size(sizeof(MultiXactId), MaxOldestSlot))
 
 	size = SHARED_MULTIXACT_STATE_SIZE;
 	size = add_size(size, SimpleLruShmemSize(multixact_offset_buffers, 0));
@@ -1763,7 +1806,7 @@ MultiXactShmemInit(void)
 	 * Set up array pointers.
 	 */
 	OldestMemberMXactId = MultiXactState->perBackendXactIds;
-	OldestVisibleMXactId = OldestMemberMXactId + MaxOldestSlot;
+	OldestVisibleMXactId = OldestMemberMXactId + MaxMemberSlot;
 }
 
 /*
@@ -2303,6 +2346,8 @@ MultiXactId
 GetOldestMultiXactId(void)
 {
 	MultiXactId oldestMXact;
+	MultiXactId thisoldest;
+	MultiXactId	*allOldest;
 	int			i;
 
 	/*
@@ -2311,15 +2356,10 @@ GetOldestMultiXactId(void)
 	 */
 	LWLockAcquire(MultiXactGenLock, LW_SHARED);
 	oldestMXact = MultiXactState->nextMXact;
+	allOldest = MultiXactState->perBackendXactIds;
 	for (i = 0; i < MaxOldestSlot; i++)
 	{
-		MultiXactId thisoldest;
-
-		thisoldest = OldestMemberMXactId[i];
-		if (MultiXactIdIsValid(thisoldest) &&
-			MultiXactIdPrecedes(thisoldest, oldestMXact))
-			oldestMXact = thisoldest;
-		thisoldest = OldestVisibleMXactId[i];
+		thisoldest = allOldest[i];
 		if (MultiXactIdIsValid(thisoldest) &&
 			MultiXactIdPrecedes(thisoldest, oldestMXact))
 			oldestMXact = thisoldest;
-- 
2.51.0

Reply via email to