From 89fde94dd74810d2bf349af33b7ca9585080c0f6 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Fri, 7 Nov 2025 23:49:47 +0200
Subject: [PATCH v1] Optimize shared memory usage for WaitLSNProcInfo

We need separate pairing heaps for different WaitLSNType's, because there
might be waiters for different LSN's at the same time.  However, one process
can wait only for one type of LSN at a time.  So, not need for inHeap
and heapNode fields to be arrays.
---
 src/backend/access/transam/xlogwait.c | 40 ++++++++++++---------------
 src/include/access/xlogwait.h         |  7 +++--
 2 files changed, 22 insertions(+), 25 deletions(-)

diff --git a/src/backend/access/transam/xlogwait.c b/src/backend/access/transam/xlogwait.c
index 34fa41ed9b2..e1eb21be125 100644
--- a/src/backend/access/transam/xlogwait.c
+++ b/src/backend/access/transam/xlogwait.c
@@ -90,7 +90,7 @@ WaitLSNShmemInit(void)
 		for (i = 0; i < WAIT_LSN_TYPE_COUNT; i++)
 		{
 			pg_atomic_init_u64(&waitLSNState->minWaitedLSN[i], PG_UINT64_MAX);
-			pairingheap_initialize(&waitLSNState->waitersHeap[i], waitlsn_cmp, (void *) (uintptr_t) i);
+			pairingheap_initialize(&waitLSNState->waitersHeap[i], waitlsn_cmp, NULL);
 		}
 
 		/* Initialize process info array */
@@ -106,9 +106,8 @@ WaitLSNShmemInit(void)
 static int
 waitlsn_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
 {
-	int			i = (uintptr_t) arg;
-	const WaitLSNProcInfo *aproc = pairingheap_const_container(WaitLSNProcInfo, heapNode[i], a);
-	const WaitLSNProcInfo *bproc = pairingheap_const_container(WaitLSNProcInfo, heapNode[i], b);
+	const WaitLSNProcInfo *aproc = pairingheap_const_container(WaitLSNProcInfo, heapNode, a);
+	const WaitLSNProcInfo *bproc = pairingheap_const_container(WaitLSNProcInfo, heapNode, b);
 
 	if (aproc->waitLSN < bproc->waitLSN)
 		return 1;
@@ -132,7 +131,7 @@ updateMinWaitedLSN(WaitLSNType lsnType)
 	if (!pairingheap_is_empty(&waitLSNState->waitersHeap[i]))
 	{
 		pairingheap_node *node = pairingheap_first(&waitLSNState->waitersHeap[i]);
-		WaitLSNProcInfo *procInfo = pairingheap_container(WaitLSNProcInfo, heapNode[i], node);
+		WaitLSNProcInfo *procInfo = pairingheap_container(WaitLSNProcInfo, heapNode, node);
 
 		minWaitedLSN = procInfo->waitLSN;
 	}
@@ -154,10 +153,11 @@ addLSNWaiter(XLogRecPtr lsn, WaitLSNType lsnType)
 
 	procInfo->procno = MyProcNumber;
 	procInfo->waitLSN = lsn;
+	procInfo->lsnType = lsnType;
 
-	Assert(!procInfo->inHeap[i]);
-	pairingheap_add(&waitLSNState->waitersHeap[i], &procInfo->heapNode[i]);
-	procInfo->inHeap[i] = true;
+	Assert(!procInfo->inHeap);
+	pairingheap_add(&waitLSNState->waitersHeap[i], &procInfo->heapNode);
+	procInfo->inHeap = true;
 	updateMinWaitedLSN(lsnType);
 
 	LWLockRelease(WaitLSNLock);
@@ -176,10 +176,10 @@ deleteLSNWaiter(WaitLSNType lsnType)
 
 	LWLockAcquire(WaitLSNLock, LW_EXCLUSIVE);
 
-	if (procInfo->inHeap[i])
+	if (procInfo->inHeap)
 	{
-		pairingheap_remove(&waitLSNState->waitersHeap[i], &procInfo->heapNode[i]);
-		procInfo->inHeap[i] = false;
+		pairingheap_remove(&waitLSNState->waitersHeap[i], &procInfo->heapNode);
+		procInfo->inHeap = false;
 		updateMinWaitedLSN(lsnType);
 	}
 
@@ -228,7 +228,7 @@ wakeupWaiters(WaitLSNType lsnType, XLogRecPtr currentLSN)
 			WaitLSNProcInfo *procInfo;
 
 			/* Get procInfo using appropriate heap node */
-			procInfo = pairingheap_container(WaitLSNProcInfo, heapNode[i], node);
+			procInfo = pairingheap_container(WaitLSNProcInfo, heapNode, node);
 
 			if (XLogRecPtrIsValid(currentLSN) && procInfo->waitLSN > currentLSN)
 				break;
@@ -238,7 +238,7 @@ wakeupWaiters(WaitLSNType lsnType, XLogRecPtr currentLSN)
 			(void) pairingheap_remove_first(&waitLSNState->waitersHeap[i]);
 
 			/* Update appropriate flag */
-			procInfo->inHeap[i] = false;
+			procInfo->inHeap = false;
 
 			if (numWakeUpProcs == WAKEUP_PROC_STATIC_ARRAY_SIZE)
 				break;
@@ -285,20 +285,14 @@ WaitLSNCleanup(void)
 {
 	if (waitLSNState)
 	{
-		int			i;
-
 		/*
-		 * We do a fast-path check of the heap flags without the lock.  These
-		 * flags are set to true only by the process itself.  So, it's only
+		 * We do a fast-path check of the inHeap flag without the lock.  This
+		 * flag is set to true only by the process itself.  So, it's only
 		 * possible to get a false positive.  But that will be eliminated by a
 		 * recheck inside deleteLSNWaiter().
 		 */
-
-		for (i = 0; i < (int) WAIT_LSN_TYPE_COUNT; i++)
-		{
-			if (waitLSNState->procInfos[MyProcNumber].inHeap[i])
-				deleteLSNWaiter((WaitLSNType) i);
-		}
+		if (waitLSNState->procInfos[MyProcNumber].inHeap)
+			deleteLSNWaiter(waitLSNState->procInfos[MyProcNumber].lsnType);
 	}
 }
 
diff --git a/src/include/access/xlogwait.h b/src/include/access/xlogwait.h
index 4dc328b1b07..46bac74988b 100644
--- a/src/include/access/xlogwait.h
+++ b/src/include/access/xlogwait.h
@@ -50,14 +50,17 @@ typedef struct WaitLSNProcInfo
 	/* LSN, which this process is waiting for */
 	XLogRecPtr	waitLSN;
 
+	/* The type of LSN to wait */
+	WaitLSNType lsnType;
+
 	/* Process to wake up once the waitLSN is reached */
 	ProcNumber	procno;
 
 	/* Heap membership flags for LSN types */
-	bool		inHeap[WAIT_LSN_TYPE_COUNT];
+	bool		inHeap;
 
 	/* Heap nodes for LSN types */
-	pairingheap_node heapNode[WAIT_LSN_TYPE_COUNT];
+	pairingheap_node heapNode;
 } WaitLSNProcInfo;
 
 /*
-- 
2.39.5 (Apple Git-154)

