diff options
| author | Heikki Linnakangas | 2026-09-16 11:52:12 +0000 |
|---|---|---|
| committer | Heikki Linnakangas | 2026-09-16 11:53:50 +0000 |
| commit | d8408e8d6823036bb88310b36eed8d9607220847 (patch) | |
| tree | 8106361b78558aebfd62aedebc1fb45d3a77c2d9 | |
| parent | 0d5621890583bf64679ad4e4960c850732313472 (diff) | |
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
| -rw-r--r-- | src/backend/access/transam/multixact.c | 17 |
1 files 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))); |
