postgres.git / summary / log / commit / refs
commit fcc0e27f45e2d8dc6046435908a48626c91d3d84
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Wed Sep 16 11:52:12 2026 +0000
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 <noah@leadboat.com>
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 d688815083c..70a4ea69486 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)));