]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix off-by-one error in calculating subtrans/multixact truncation point.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 22 Jul 2015 22:30:17 +0000 (01:30 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 22 Jul 2015 22:30:17 +0000 (01:30 +0300)
If there were no subtransactions (or multixacts) active, we would calculate
the oldestxid == next xid. That's correct, but if next XID happens to be
on the next pg_subtrans (pg_multixact) page, the page does not exist yet,
and SimpleLruTruncate will produce an "apparent wraparound" warning. The
warning is harmless in this case, but looks very alarming to users.

Backpatch to all supported versions. Patch and analysis by Thomas Munro.

src/backend/access/transam/multixact.c
src/backend/access/transam/subtrans.c
src/include/access/multixact.h

index b90c110dcad7d5a04fff30377d981f89639365ec..5182b4acb533dd20379ff5fe0a4588478efe4f0d 100644 (file)
@@ -90,6 +90,8 @@
 #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
@@ -1902,17 +1904,21 @@ TruncateMultiXact(void)
        }
 
        /*
-        * 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);
 
index c6fa2419c633055cb2c88d94469e234e5a44f966..39e0f10866f37932eddf2ad62f2d3f67af2fd843 100644 (file)
@@ -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);
index c86aeee44ef3e27a9a57b28fe6cc5fa561784211..a96e6531ef8ff6e28997384d5eecc20e5221e6cd 100644 (file)
@@ -15,6 +15,7 @@
 
 #define InvalidMultiXactId     ((MultiXactId) 0)
 #define FirstMultiXactId       ((MultiXactId) 1)
+#define MaxMultiXactId         ((MultiXactId) 0xFFFFFFFF)
 
 #define MultiXactIdIsValid(multi) ((multi) != InvalidMultiXactId)