v36-0004-Add-pruning-fast-path-for-all-visible-and-all-fr.patch
text/x-patch
Filename: v36-0004-Add-pruning-fast-path-for-all-visible-and-all-fr.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 v36-0004
Subject: Add pruning fast path for all-visible and all-frozen pages
| File | + | − |
|---|---|---|
| src/backend/access/heap/pruneheap.c | 75 | 0 |
From ed9509518d8b5a0772133d13e9714153dd526858 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Wed, 25 Feb 2026 16:48:19 -0500
Subject: [PATCH v36 04/16] Add pruning fast path for all-visible and
all-frozen pages
Because of the SKIP_PAGES_THRESHOLD optimization or a stale prune XID,
heap_page_prune_and_freeze() can be invoked for pages with no pruning or
freezing work. To avoid this, if a page is already all-frozen or it is
all-visible and no freezing will be attempted, we exit early.
Author: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
---
src/backend/access/heap/pruneheap.c | 75 +++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 6eca1474a2f..2cd684873c0 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -191,6 +191,7 @@ static void prune_freeze_setup(PruneFreezeParams *params,
PruneFreezeResult *presult,
PruneState *prstate);
static void heap_fix_vm_corruption(PruneState *prstate, OffsetNumber offnum);
+static void heap_page_bypass_prune_freeze(PruneState *prstate, PruneFreezeResult *presult);
static void prune_freeze_plan(PruneState *prstate,
OffsetNumber *off_loc);
static HTSV_Result heap_prune_satisfies_vacuum(PruneState *prstate,
@@ -889,6 +890,68 @@ heap_fix_vm_corruption(PruneState *prstate, OffsetNumber offnum)
prstate->vmbits = 0;
}
+/*
+ * If the page is already all-frozen, or already all-visible and freezing
+ * is not being attempted, there is no remaining work and we can bypass the
+ * expensive overhead of heap_page_prune_and_freeze().
+ *
+ * This can happen when the page has a stale prune hint, or if VACUUM is
+ * scanning an already all-frozen page due to SKIP_PAGES_THRESHOLD.
+ *
+ * The caller must already have examined the visibility map and saved the
+ * status for the page's VM bits in prstate->vmbits. Caller must hold a
+ * content lock on the heap page since it will examine line pointers.
+ *
+ * Before calling heap_page_bypass_prune_freeze(), the caller should first
+ * check for and fix any discrepancy between the page-level visibility hint
+ * and the visibility map. Otherwise, the fast path will always prevent us
+ * from getting them in sync. Note that if there are tuples on the page that
+ * are not visible to all but the VM is incorrectly marked
+ * all-visible/all-frozen, we will not get the chance to fix that corruption
+ * when using the fast path.
+ */
+static void
+heap_page_bypass_prune_freeze(PruneState *prstate, PruneFreezeResult *presult)
+{
+ OffsetNumber maxoff = PageGetMaxOffsetNumber(prstate->page);
+ Page page = prstate->page;
+
+ Assert(prstate->vmbits & VISIBILITYMAP_ALL_FROZEN ||
+ (prstate->vmbits & VISIBILITYMAP_ALL_VISIBLE &&
+ !prstate->attempt_freeze));
+
+ /* We'll fill in presult for the caller */
+ memset(presult, 0, sizeof(PruneFreezeResult));
+
+ presult->vmbits = prstate->vmbits;
+
+ /* Clear any stale prune hint */
+ if (TransactionIdIsValid(PageGetPruneXid(page)))
+ {
+ PageClearPrunable(page);
+ MarkBufferDirtyHint(prstate->buffer, true);
+ }
+
+ if (PageIsEmpty(page))
+ return;
+
+ presult->hastup = true;
+
+ /*
+ * Since the page is all-visible, a count of the normal ItemIds on the
+ * page should be sufficient for vacuum's live tuple count.
+ */
+ for (OffsetNumber off = FirstOffsetNumber;
+ off <= maxoff;
+ off = OffsetNumberNext(off))
+ {
+ if (ItemIdIsNormal(PageGetItemId(page, off)))
+ prstate->live_tuples++;
+ }
+
+ presult->live_tuples = prstate->live_tuples;
+}
+
/*
* Prune and repair fragmentation and potentially freeze tuples on the
* specified page.
@@ -952,6 +1015,18 @@ heap_page_prune_and_freeze(PruneFreezeParams *params,
!PageIsAllVisible(prstate.page))
heap_fix_vm_corruption(&prstate, InvalidOffsetNumber);
+ /*
+ * If the page is already all-frozen, or already all-visible when freezing
+ * is not being attempted, we can exit early. Do this after fixing any
+ * discrepancy between the page-level visibility hint and the VM.
+ */
+ if (prstate.vmbits & VISIBILITYMAP_ALL_FROZEN ||
+ (prstate.vmbits & VISIBILITYMAP_ALL_VISIBLE && !prstate.attempt_freeze))
+ {
+ heap_page_bypass_prune_freeze(&prstate, presult);
+ return;
+ }
+
/*
* Examine all line pointers and tuple visibility information to determine
* which line pointers should change state and which tuples may be frozen.
--
2.43.0