aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/transam/multixact.c
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2021-01-16 12:21:35 -0800
committerNoah Misch <noah@leadboat.com>2021-01-16 12:21:35 -0800
commit6db992833c04c0322f7f34a486adece01651f929 (patch)
tree3ef1ce108c34f616ef2603b49e5c2cbd3ca232ba /src/backend/access/transam/multixact.c
parentc95765f47673b16ed36acbfe98e1242e3c3822a3 (diff)
downloadpostgresql-6db992833c04c0322f7f34a486adece01651f929.tar.gz
postgresql-6db992833c04c0322f7f34a486adece01651f929.zip
Prevent excess SimpleLruTruncate() deletion.
Every core SLRU wraps around. With the exception of pg_notify, the wrap point can fall in the middle of a page. Account for this in the PagePrecedes callback specification and in SimpleLruTruncate()'s use of said callback. Update each callback implementation to fit the new specification. This changes SerialPagePrecedesLogically() from the style of asyncQueuePagePrecedes() to the style of CLOGPagePrecedes(). (Whereas pg_clog and pg_serial share a key space, pg_serial is nothing like pg_notify.) The bug fixed here has the same symptoms and user followup steps as 592a589a04bd456410b853d86bd05faa9432cbbb. Back-patch to 9.5 (all supported versions). Reviewed by Andrey Borodin and (in earlier versions) by Tom Lane. Discussion: https://postgr.es/m/20190202083822.GC32531@gust.leadboat.com
Diffstat (limited to 'src/backend/access/transam/multixact.c')
-rw-r--r--src/backend/access/transam/multixact.c36
1 files changed, 23 insertions, 13 deletions
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 1233448481a..7dcfa023236 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -1852,11 +1852,13 @@ MultiXactShmemInit(void)
MultiXactOffsetSLRULock, "pg_multixact/offsets",
LWTRANCHE_MULTIXACTOFFSET_BUFFER,
SYNC_HANDLER_MULTIXACT_OFFSET);
+ SlruPagePrecedesUnitTests(MultiXactOffsetCtl, MULTIXACT_OFFSETS_PER_PAGE);
SimpleLruInit(MultiXactMemberCtl,
"MultiXactMember", NUM_MULTIXACTMEMBER_BUFFERS, 0,
MultiXactMemberSLRULock, "pg_multixact/members",
LWTRANCHE_MULTIXACTMEMBER_BUFFER,
SYNC_HANDLER_MULTIXACT_MEMBER);
+ /* doesn't call SimpleLruTruncate() or meet criteria for unit tests */
/* Initialize our shared state struct */
MultiXactState = ShmemInitStruct("Shared MultiXact State",
@@ -2982,6 +2984,14 @@ TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
* truncate the members SLRU. So we first scan the directory to determine
* the earliest offsets page number that we can read without error.
*
+ * When nextMXact is less than one segment away from multiWrapLimit,
+ * SlruScanDirCbFindEarliest can find some early segment other than the
+ * actual earliest. (MultiXactOffsetPagePrecedes(EARLIEST, LATEST)
+ * returns false, because not all pairs of entries have the same answer.)
+ * That can also arise when an earlier truncation attempt failed unlink()
+ * or returned early from this function. The only consequence is
+ * returning early, which wastes space that we could have liberated.
+ *
* NB: It's also possible that the page that oldestMulti is on has already
* been truncated away, and we crashed before updating oldestMulti.
*/
@@ -3096,15 +3106,11 @@ TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
}
/*
- * Decide which of two MultiXactOffset page numbers is "older" for truncation
- * purposes.
+ * Decide whether a MultiXactOffset page number is "older" for truncation
+ * purposes. Analogous to CLOGPagePrecedes().
*
- * We need to use comparison of MultiXactId here in order to do the right
- * thing with wraparound. However, if we are asked about page number zero, we
- * don't want to hand InvalidMultiXactId to MultiXactIdPrecedes: it'll get
- * weird. So, offset both multis by FirstMultiXactId to avoid that.
- * (Actually, the current implementation doesn't do anything weird with
- * InvalidMultiXactId, but there's no harm in leaving this code like this.)
+ * Offsetting the values is optional, because MultiXactIdPrecedes() has
+ * translational symmetry.
*/
static bool
MultiXactOffsetPagePrecedes(int page1, int page2)
@@ -3113,15 +3119,17 @@ MultiXactOffsetPagePrecedes(int page1, int page2)
MultiXactId multi2;
multi1 = ((MultiXactId) page1) * MULTIXACT_OFFSETS_PER_PAGE;
- multi1 += FirstMultiXactId;
+ multi1 += FirstMultiXactId + 1;
multi2 = ((MultiXactId) page2) * MULTIXACT_OFFSETS_PER_PAGE;
- multi2 += FirstMultiXactId;
+ multi2 += FirstMultiXactId + 1;
- return MultiXactIdPrecedes(multi1, multi2);
+ return (MultiXactIdPrecedes(multi1, multi2) &&
+ MultiXactIdPrecedes(multi1,
+ multi2 + MULTIXACT_OFFSETS_PER_PAGE - 1));
}
/*
- * Decide which of two MultiXactMember page numbers is "older" for truncation
+ * Decide whether a MultiXactMember page number is "older" for truncation
* purposes. There is no "invalid offset number" so use the numbers verbatim.
*/
static bool
@@ -3133,7 +3141,9 @@ MultiXactMemberPagePrecedes(int page1, int page2)
offset1 = ((MultiXactOffset) page1) * MULTIXACT_MEMBERS_PER_PAGE;
offset2 = ((MultiXactOffset) page2) * MULTIXACT_MEMBERS_PER_PAGE;
- return MultiXactOffsetPrecedes(offset1, offset2);
+ return (MultiXactOffsetPrecedes(offset1, offset2) &&
+ MultiXactOffsetPrecedes(offset1,
+ offset2 + MULTIXACT_MEMBERS_PER_PAGE - 1));
}
/*