From 7e8ea684a4c6ee5d4b7169ec3195be75e76172e9 Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Wed, 25 Feb 2026 16:48:19 -0500 Subject: [PATCH v35 07/18] 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 can exit early. --- src/backend/access/heap/pruneheap.c | 73 +++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index fa470f663b7..73db45f8dfd 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -184,6 +184,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, @@ -880,6 +881,66 @@ 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)); + + /* + * 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; + + /* Clear any stale prune hint */ + if (TransactionIdIsValid(PageGetPruneXid(page))) + { + PageClearPrunable(page); + MarkBufferDirtyHint(prstate->buffer, true); + } + + presult->vmbits = prstate->vmbits; + + if (!PageIsEmpty(page)) + presult->hastup = true; +} + /* * Prune and repair fragmentation and potentially freeze tuples on the * specified page. @@ -943,6 +1004,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