From 34a19eb83c57ee5e2ab50ee7ab6dce6f754fefef Mon Sep 17 00:00:00 2001 From: Andrey Borodin 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)