#define MXOffsetToMemberEntry(xid) \
((xid) % (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
+#define PreviousMultiXactId(xid) \
+ ((xid) == FirstMultiXactId ? MaxMultiXactId : (xid) - 1)
/*
* Links to shared-memory data structures for MultiXact control
}
/*
- * The cutoff point is the start of the segment containing oldestMXact. We
- * pass the *page* containing oldestMXact to SimpleLruTruncate.
+ * The cutoff point is the start of the segment containing oldestMXact.
+ * 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.
*/
- cutoffPage = MultiXactIdToOffsetPage(oldestMXact);
+ cutoffPage = MultiXactIdToOffsetPage(PreviousMultiXactId(oldestMXact));
SimpleLruTruncate(MultiXactOffsetCtl, cutoffPage);
/*
* Also truncate MultiXactMember at the previously determined offset.
*/
- cutoffPage = MXOffsetToMemberPage(oldestOffset);
+ cutoffPage = MXOffsetToMemberPage(oldestOffset - 1);
SimpleLruTruncate(MultiXactMemberCtl, cutoffPage);
/*
* 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);
#define InvalidMultiXactId ((MultiXactId) 0)
#define FirstMultiXactId ((MultiXactId) 1)
+#define MaxMultiXactId ((MultiXactId) 0xFFFFFFFF)
#define MultiXactIdIsValid(multi) ((multi) != InvalidMultiXactId)