From d8408e8d6823036bb88310b36eed8d9607220847 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Wed, 16 Sep 2026 14:52:12 +0300 Subject: Fix multixact members truncation at page boundary Like in PerformOffsetsTruncation() and e.g. in TruncateSUBTRANS(), need to step back to previous page at page boundary, to avoid passing a cutoff page to SimpleLruTruncate() that hasn't been created yet. Otherwise you can get a bogus 'LOG: could not truncate directory "pg_multixact/members": apparent wraparound' message in the log. Reported-by: Noah Misch Discussion: https://www.postgresql.org/message-id/20260827231757.78.noahmisch@microsoft.com Backpatch-through: 19 --- src/backend/access/transam/multixact.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index 424516dc106..0dbb072ce44 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -2642,8 +2642,16 @@ MultiXactMemberFreezeThreshold(void) static void PerformMembersTruncation(MultiXactOffset newOldestOffset) { + /* + * We step back one entry to avoid passing a cutoff page that hasn't been + * created yet in the rare case that oldestOffset would be the first item + * on a page and oldestOffset == nextOffset. In that case, if we didn't + * subtract one, we'd trigger SimpleLruTruncate's wraparound detection. + */ + if (newOldestOffset <= 1) + return; SimpleLruTruncate(MultiXactMemberCtl, - MXOffsetToMemberPage(newOldestOffset)); + MXOffsetToMemberPage(newOldestOffset - 1)); } /* @@ -2653,11 +2661,8 @@ static void PerformOffsetsTruncation(MultiXactId newOldestMulti) { /* - * We step back one multixact to avoid passing a cutoff page that hasn't - * been created yet in the rare case that oldestMulti would be the first - * item on a page and oldestMulti == nextMulti. In that case, if we - * didn't subtract one, we'd trigger SimpleLruTruncate's wraparound - * detection. + * Like in PerformMembersTruncation(), step back one entry to avoid + * passing a cutoff page that hasn't been created yet. */ SimpleLruTruncate(MultiXactOffsetCtl, MultiXactIdToOffsetPage(PreviousMultiXactId(newOldestMulti))); -- cgit v1.2.3