v44-0004-WAL-log-VM-setting-for-empty-pages-in-XLOG_HEAP2.patch
text/x-patch
Filename: v44-0004-WAL-log-VM-setting-for-empty-pages-in-XLOG_HEAP2.patch
Type: text/x-patch
Part: 3
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v44-0004
Subject: WAL log VM setting for empty pages in XLOG_HEAP2_PRUNE_VACUUM_SCAN
| File | + | − |
|---|---|---|
| src/backend/access/heap/pruneheap.c | 18 | 11 |
| src/backend/access/heap/vacuumlazy.c | 27 | 17 |
From 134e19504883dc3b07c506332dd23533e381b699 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Sat, 27 Sep 2025 11:55:21 -0400
Subject: [PATCH v44 04/10] WAL log VM setting for empty pages in
XLOG_HEAP2_PRUNE_VACUUM_SCAN
As part of removing XLOG_HEAP2_VISIBLE records, phase I of VACUUM now
marks empty pages all-visible in a XLOG_HEAP2_PRUNE_VACUUM_SCAN record.
This has no independent benefit, but empty pages were the last user of
XLOG_HEAP2_VISIBLE, so by making this change we can next remove all of
the XLOG_HEAP2_VISIBLE code.
Author: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Earlier version Reviewed-by: Robert Haas <robertmhaas@gmail.com>
---
src/backend/access/heap/pruneheap.c | 29 +++++++++++-------
src/backend/access/heap/vacuumlazy.c | 44 +++++++++++++++++-----------
2 files changed, 45 insertions(+), 28 deletions(-)
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 4d6d5e92773..fe9564b26c7 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -2541,6 +2541,8 @@ log_heap_prune_and_freeze(Relation relation, Buffer buffer,
uint8 info;
uint8 regbuf_flags_heap;
+ Page heap_page = BufferGetPage(buffer);
+
/* The following local variables hold data registered in the WAL record: */
xlhp_freeze_plan plans[MaxHeapTuplesPerPage];
xlhp_freeze_plans freeze_plans;
@@ -2559,14 +2561,18 @@ log_heap_prune_and_freeze(Relation relation, Buffer buffer,
/*
* We can avoid an FPI of the heap page if the only modification we are
* making to it is to set PD_ALL_VISIBLE and checksums/wal_log_hints are
- * disabled. Note that if we explicitly skip an FPI, we must not stamp the
- * heap page with this record's LSN. Recovery skips records <= the stamped
- * LSN, so this could lead to skipping an earlier FPI needed to repair a
- * torn page.
+ * disabled.
+ *
+ * However, if the page has never been WAL-logged (LSN is invalid), we
+ * must force an FPI regardless. This can happen when another backend
+ * extends the heap, initializes the page, and then fails before WAL-
+ * logging it. Since heap extension is not WAL-logged, recovery might try
+ * to replay our record and find that the page isn't initialized, which
+ * would cause a PANIC.
*/
- if (!do_prune &&
- nfrozen == 0 &&
- (!do_set_vm || !XLogHintBitIsNeeded()))
+ if (!XLogRecPtrIsValid(PageGetLSN(heap_page)))
+ regbuf_flags_heap |= REGBUF_FORCE_IMAGE;
+ else if (!do_prune && nfrozen == 0 && (!do_set_vm || !XLogHintBitIsNeeded()))
regbuf_flags_heap |= REGBUF_NO_IMAGE;
/*
@@ -2681,12 +2687,13 @@ log_heap_prune_and_freeze(Relation relation, Buffer buffer,
}
/*
- * See comment at the top of the function about regbuf_flags_heap for
- * details on when we can advance the page LSN.
+ * If we explicitly skip an FPI, we must not stamp the heap page with this
+ * record's LSN. Recovery skips records <= the stamped LSN, so this could
+ * lead to skipping an earlier FPI needed to repair a torn page.
*/
- if (do_prune || nfrozen > 0 || (do_set_vm && XLogHintBitIsNeeded()))
+ if (!(regbuf_flags_heap & REGBUF_NO_IMAGE))
{
Assert(BufferIsDirty(buffer));
- PageSetLSN(BufferGetPage(buffer), recptr);
+ PageSetLSN(heap_page, recptr);
}
}
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 23deabd8c01..63e6199241c 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -1929,33 +1929,43 @@ lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf, BlockNumber blkno,
*/
if (!PageIsAllVisible(page))
{
+ /* Lock vmbuffer before entering critical section */
+ LockBuffer(vmbuffer, BUFFER_LOCK_EXCLUSIVE);
+
START_CRIT_SECTION();
/* mark buffer dirty before writing a WAL record */
MarkBufferDirty(buf);
+ PageSetAllVisible(page);
+ PageClearPrunable(page);
+ visibilitymap_set_vmbits(blkno,
+ vmbuffer,
+ VISIBILITYMAP_ALL_VISIBLE |
+ VISIBILITYMAP_ALL_FROZEN,
+ vacrel->rel->rd_locator);
+
/*
- * It's possible that another backend has extended the heap,
- * initialized the page, and then failed to WAL-log the page due
- * to an ERROR. Since heap extension is not WAL-logged, recovery
- * might try to replay our record setting the page all-visible and
- * find that the page isn't initialized, which will cause a PANIC.
- * To prevent that, check whether the page has been previously
- * WAL-logged, and if not, do that now.
+ * Emit WAL for setting PD_ALL_VISIBLE on the heap page and
+ * setting the VM.
*/
- if (RelationNeedsWAL(vacrel->rel) &&
- !XLogRecPtrIsValid(PageGetLSN(page)))
- log_newpage_buffer(buf, true);
+ if (RelationNeedsWAL(vacrel->rel))
+ log_heap_prune_and_freeze(vacrel->rel, buf,
+ vmbuffer,
+ VISIBILITYMAP_ALL_VISIBLE |
+ VISIBILITYMAP_ALL_FROZEN,
+ InvalidTransactionId, /* conflict xid */
+ false, /* cleanup lock */
+ PRUNE_VACUUM_SCAN, /* reason */
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0);
- PageSetAllVisible(page);
- PageClearPrunable(page);
- visibilitymap_set(vacrel->rel, blkno, buf,
- InvalidXLogRecPtr,
- vmbuffer, InvalidTransactionId,
- VISIBILITYMAP_ALL_VISIBLE |
- VISIBILITYMAP_ALL_FROZEN);
END_CRIT_SECTION();
+ LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
+
/* Count the newly all-frozen pages for logging */
vacrel->new_all_visible_pages++;
vacrel->new_all_visible_all_frozen_pages++;
--
2.43.0