From 34a19eb83c57ee5e2ab50ee7ab6dce6f754fefef Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Fri, 18 Jul 2025 18:48:54 +0500
Subject: [PATCH v7 2/2] Fill next multitransaction in REDO to avoid corner
 case 2

---
 src/backend/access/transam/multixact.c | 68 ++++++++++++++++++++++++--
 1 file changed, 64 insertions(+), 4 deletions(-)

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 6375d0b4762..ec15838c561 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -341,7 +341,7 @@ static MemoryContext MXactContext = NULL;
 /* internal MultiXactId management */
 static void MultiXactIdSetOldestVisible(void);
 static void RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
-							   int nmembers, MultiXactMember *members);
+							   int nmembers, MultiXactMember *members, bool redo);
 static MultiXactId GetNewMultiXactId(int nmembers, MultiXactOffset *offset);
 
 /* MultiXact cache management */
@@ -843,7 +843,7 @@ MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members)
 	(void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_CREATE_ID);
 
 	/* Now enter the information into the OFFSETs and MEMBERs logs */
-	RecordNewMultiXact(multi, offset, nmembers, members);
+	RecordNewMultiXact(multi, offset, nmembers, members, false);
 
 	/* Done with critical section */
 	END_CRIT_SECTION();
@@ -865,7 +865,7 @@ MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members)
  */
 static void
 RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
-				   int nmembers, MultiXactMember *members)
+				   int nmembers, MultiXactMember *members, bool redo)
 {
 	int			pageno;
 	int			prev_pageno;
@@ -890,8 +890,62 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 	offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 	offptr += entryno;
 
+	if (redo)
+	{
+		/*
+		 * We might have filled this offset previosuly.
+		 * Cross-check for correctness.
+		 */
+		Assert((*offptr == 0) || (*offptr == offset));
+	}
+
 	*offptr = offset;
 
+	if (redo)
+	{
+		/*
+		 * We want to avoid edge case 2 in redo, because we cannot wait for
+		 * startup process in GetMultiXactIdMembers() without risk of a deadlock.
+		 */
+		MultiXactId next = multi + 1;
+		int			next_pageno;
+		/* Handle wraparound as GetMultiXactIdMembers() does it. */
+		if (multi < FirstMultiXactId)
+			multi = FirstMultiXactId;
+		next_pageno = MultiXactIdToOffsetPage(next);
+		if (next_pageno == pageno)
+		{
+			offptr[1] = offset + nmembers;
+		}
+		else
+		{
+			int	next_slotno;
+			MultiXactOffset *next_offptr;
+			int	next_entryno = MultiXactIdToOffsetEntry(next);
+
+			if (SimpleLruDoesPhysicalPageExist(MultiXactOffsetCtl, next_pageno))
+			{
+				/* Just read a next page */
+				next_slotno = SimpleLruReadPage(MultiXactOffsetCtl, next_pageno, true, next);
+			}
+			else
+			{
+				/*
+				 * We have to create a new page.
+				 * SimpleLruWritePage is already prepared to deal
+				 * with creating a new segment file. We do not need to handle
+				 * race conditions, because this code is only executed in redo
+				 * and we hold MultiXactOffsetSLRULock.
+				 */
+				next_slotno = ZeroMultiXactOffsetPage(next_pageno, false);
+				SimpleLruWritePage(MultiXactOffsetCtl, next_slotno);
+			}
+			next_offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[next_slotno];
+			next_offptr[next_slotno] = offset + nmembers;
+			MultiXactMemberCtl->shared->page_dirty[next_slotno] = true;
+		}
+	}
+
 	MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
 
 	/* Exchange our lock */
@@ -1393,6 +1447,12 @@ retry:
 			/* Corner case 2: next multixact is still being filled in */
 			LWLockRelease(MultiXactOffsetSLRULock);
 			CHECK_FOR_INTERRUPTS();
+			/*
+			 * CHECK_FOR_INTERRUPTS above would be critical for avoiding
+			 * conflicts with recovery, yet caller might hold LWLock rendering
+			 * CHECK_FOR_INTERRUPTS disfunctional
+			 */
+			Assert(!RecoveryInProgress());
 			pg_usleep(1000L);
 			goto retry;
 		}
@@ -3286,7 +3346,7 @@ multixact_redo(XLogReaderState *record)
 
 		/* Store the data back into the SLRU files */
 		RecordNewMultiXact(xlrec->mid, xlrec->moff, xlrec->nmembers,
-						   xlrec->members);
+						   xlrec->members, true);
 
 		/* Make sure nextMXact/nextOffset are beyond what this record has */
 		MultiXactAdvanceNextMXact(xlrec->mid + 1,
-- 
2.39.5 (Apple Git-154)

