v7-0002-Fill-next-multitransaction-in-REDO-to-avoid-corne.patch
application/octet-stream
Filename: v7-0002-Fill-next-multitransaction-in-REDO-to-avoid-corne.patch
Type: application/octet-stream
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v7-0002
Subject: Fill next multitransaction in REDO to avoid corner case 2
| File | + | − |
|---|---|---|
| src/backend/access/transam/multixact.c | 64 | 4 |
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)