From a9db95d6f97300d49c5c3f8ac1ecb87c9d7cf429 Mon Sep 17 00:00:00 2001
From: Yura Sokolov <y.sokolov@postgrespro.ru>
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

