diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index 7c457a6..c5b9959 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -2941,6 +2941,7 @@ TruncateMultiXact(void) mxtruncinfo trunc; MultiXactId earliest; MembersLiveRange range; + int offsetsCutoffPage; Assert(AmCheckpointerProcess() || AmStartupProcess() || !IsPostmasterEnvironment); @@ -3013,10 +3014,21 @@ TruncateMultiXact(void) SlruScanDirectory(MultiXactMemberCtl, SlruScanDirCbRemoveMembers, &range); - /* Now we can truncate MultiXactOffset */ - SimpleLruTruncate(MultiXactOffsetCtl, - MultiXactIdToOffsetPage(oldestMXact)); + /* + * Figure out the cutoff page for offsets. If there are no multixacts, then + * oldestMXact refers to a multixact that doesn't exist yet, so we will use + * the page for the preceding multixact (which is different only when + * oldestMXact falls in the first entry of a new page). If we didn't do this, + * we'd be passing SimpleSlruTruncate a cutoff page which doesn't exist yet, + * triggering its wraparound defenses. + */ + if (oldestMXact == nextMXact) + offsetsCutoffPage = MultiXactIdToOffsetPage(oldestMXact - 1); + else + offsetsCutoffPage = MultiXactIdToOffsetPage(oldestMXact); + /* Now we can truncate MultiXactOffset */ + SimpleLruTruncate(MultiXactOffsetCtl, offsetsCutoffPage); /* * Now, and only now, we can advance the stop point for multixact members.