diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index 377d0842bdd9d0f77588ba58b4fdeb4bc00f6922..1933a87d6568e68a6a7466ee442f95c16bb549b8 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -173,6 +173,8 @@ #define MULTIXACT_MEMBER_DANGER_THRESHOLD \ (MaxMultiXactOffset - MaxMultiXactOffset / 4) +#define PreviousMultiXactId(xid) \ + ((xid) == FirstMultiXactId ? MaxMultiXactId : (xid) - 1) /* * Links to shared-memory data structures for MultiXact control @@ -3057,10 +3059,15 @@ TruncateMultiXact(void) SlruScanDirectory(MultiXactMemberCtl, SlruScanDirCbRemoveMembers, &range); - /* Now we can truncate MultiXactOffset */ + /* + * Now we can truncate MultiXactOffset. We step back one multixact to + * avoid passing a cutoff page that hasn't been created yet in the rare + * case that oldestMXact would be the first item on a page and oldestMXact + * == nextMXact. In that case, if we didn't subtract one, we'd trigger + * SimpleLruTruncate's wraparound detection. + */ SimpleLruTruncate(MultiXactOffsetCtl, - MultiXactIdToOffsetPage(oldestMXact)); - + MultiXactIdToOffsetPage(PreviousMultiXactId(oldestMXact))); /* * Now, and only now, we can advance the stop point for multixact members. diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c index 4bc24d9bbcbc4fbdb9660f237d730d6f77432b27..6b709823227803b6975c5a962e2dced57a8e0f97 100644 --- a/src/backend/access/transam/subtrans.c +++ b/src/backend/access/transam/subtrans.c @@ -340,8 +340,13 @@ TruncateSUBTRANS(TransactionId oldestXact) /* * The cutoff point is the start of the segment containing oldestXact. We - * pass the *page* containing oldestXact to SimpleLruTruncate. + * pass the *page* containing oldestXact to SimpleLruTruncate. We step + * back one transaction to avoid passing a cutoff page that hasn't been + * created yet in the rare case that oldestXact would be the first item on + * a page and oldestXact == next XID. In that case, if we didn't subtract + * one, we'd trigger SimpleLruTruncate's wraparound detection. */ + TransactionIdRetreat(oldestXact); cutoffPage = TransactionIdToPage(oldestXact); SimpleLruTruncate(SubTransCtl, cutoffPage);