From b217f466ea820c78a6eb27dc5048ce5af40eccdb Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Mon, 11 Oct 2021 18:49:43 +0000
Subject: [PATCH v1] Accommodate startup process in a separate ProcState array
 slot instead of in MaxBackends slots.

Currently the ProcState array doesn't have entries for auxiliary
processes, it does have entries for MaxBackends. But the startup
process is eating up one slot from MaxBackends. We need to
increase the size of the ProcState array by 1 at least for the
startup process. The startup process uses ProcState via
InitRecoveryTransactionEnvironment()->SharedInvalBackendInit().
The procState array size is initialized to MaxBackends in SInvalShmemSize.

The consequence of not fixing this issue is clear: the database hits
the error "sorry, too many clients already" soon. For instance,
if MaxBackends is 100, then the error gets emitted on 100th backend
connection attempt as opposed to the error on 101th connection attempt.
---
 src/backend/storage/ipc/sinvaladt.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index 946bd8e3cb..f49fdd78f6 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -183,7 +183,8 @@ typedef struct SISeg
 	SharedInvalidationMessage buffer[MAXNUMMESSAGES];
 
 	/*
-	 * Per-backend invalidation state info (has MaxBackends entries).
+	 * Invalidation state info for backends and startup process
+	 * (MaxBackends + 1 entries).
 	 */
 	ProcState	procState[FLEXIBLE_ARRAY_MEMBER];
 } SISeg;
@@ -205,7 +206,8 @@ SInvalShmemSize(void)
 	Size		size;
 
 	size = offsetof(SISeg, procState);
-	size = add_size(size, mul_size(sizeof(ProcState), MaxBackends));
+	/* The startup process also requires a slot, along with backends */
+	size = add_size(size, mul_size(sizeof(ProcState), MaxBackends + 1));
 
 	return size;
 }
@@ -231,7 +233,8 @@ CreateSharedInvalidationState(void)
 	shmInvalBuffer->maxMsgNum = 0;
 	shmInvalBuffer->nextThreshold = CLEANUP_MIN;
 	shmInvalBuffer->lastBackend = 0;
-	shmInvalBuffer->maxBackends = MaxBackends;
+	/* The startup process also requires a slot, along with backends */
+	shmInvalBuffer->maxBackends = MaxBackends + 1;
 	SpinLockInit(&shmInvalBuffer->msgnumLock);
 
 	/* The buffer[] array is initially all unused, so we need not fill it */
@@ -288,7 +291,7 @@ SharedInvalBackendInit(bool sendOnly)
 		else
 		{
 			/*
-			 * out of procState slots: MaxBackends exceeded -- report normally
+			 * out of procState slots: maxBackends exceeded -- report normally
 			 */
 			MyBackendId = InvalidBackendId;
 			LWLockRelease(SInvalWriteLock);
-- 
2.25.1

