From 9b89490054f2984d882c8ff6c3eaf6399167b41f Mon Sep 17 00:00:00 2001 From: Haibo Yan Date: Fri, 11 Sep 2026 11:25:14 -0700 Subject: [PATCH v6] Avoid unnecessary horizon refreshes during opportunistic pruning heap_page_prune_opt() currently checks whether the page's prune XID is globally removable before checking whether pruning would be useful based on the page's free space. Determining removability can refresh the global visibility horizons, which requires scanning the ProcArray. That work is unnecessary when the existing page-local heuristic would reject the pruning attempt anyway. Check the existing page-full/free-space heuristic first, and perform the visibility test only for pages for which opportunistic pruning is considered useful. The refresh-capable visibility test, conditional cleanup lock, and the usefulness recheck under the cleanup lock remain unchanged. As before, the unlocked usefulness check is only a heuristic and can miss an opportunistic pruning opportunity if page state changes concurrently. --- src/backend/access/heap/pruneheap.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 29f4722b02d..982462c4f55 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -295,15 +295,6 @@ heap_page_prune_opt(Relation relation, Buffer buffer, Buffer *vmbuffer, if (!TransactionIdIsValid(prune_xid)) return; - /* - * Check whether prune_xid indicates that there may be dead rows that can - * be cleaned up. - */ - vistest = GlobalVisTestFor(relation); - - if (!GlobalVisTestIsRemovableXid(vistest, prune_xid, true)) - return; - /* * We prune when a previous UPDATE failed to find enough space on the page * for a new tuple version, or when free space falls below the relation's @@ -325,6 +316,17 @@ heap_page_prune_opt(Relation relation, Buffer buffer, Buffer *vmbuffer, bool record_free_space = false; Size freespace = 0; + /* + * Check whether prune_xid indicates that there may be dead rows that + * can be cleaned up. Do this only after deciding that pruning would + * be useful, since refreshing the visibility horizon may require + * scanning the ProcArray. + */ + vistest = GlobalVisTestFor(relation); + + if (!GlobalVisTestIsRemovableXid(vistest, prune_xid, true)) + return; + /* OK, try to get exclusive buffer lock */ if (!ConditionalLockBufferForCleanup(buffer)) return; -- 2.43.0