From: Melanie Plageman Date: Mon, 2 Mar 2026 16:05:59 +0000 (-0500) Subject: Save prune cycles by consistently clearing prune hints on all-visible pages X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8b9d42bf6bd29b855a6bee21aa6010c070980086;p=thirdparty%2Fpostgresql.git Save prune cycles by consistently clearing prune hints on all-visible pages All-visible pages can't contain prunable tuples. We already clear the prune hint (pd_prune_xid) during pruning of all-visible pages, but we were not doing so in vacuum phase three, nor initializing it for all-frozen pages created by COPY FREEZE, and we were not clearing it on standbys. Because page hints are not WAL-logged, pages on a standby carry stale pd_prune_xid values. After promotion, that stale hint triggers unnecessary on-access pruning. Fix this by clearing the prune hint everywhere we currently mark a heap page all-visible. Clearing it when setting PD_ALL_VISIBLE ensures no extra overhead. Author: Melanie Plageman Reviewed-by: Andres Freund Discussion: https://postgr.es/m/flat/CAAKRu_b-BMOyu0X-0jc_8bWNSbQ5K6JTEueayEhcQuw-OkCSKg%40mail.gmail.com --- diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index d534258e547..a231563f0df 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -2578,6 +2578,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, else if (all_frozen_set) { PageSetAllVisible(page); + PageClearPrunable(page); visibilitymap_set_vmbits(BufferGetBlockNumber(buffer), vmbuffer, VISIBILITYMAP_ALL_VISIBLE | diff --git a/src/backend/access/heap/heapam_xlog.c b/src/backend/access/heap/heapam_xlog.c index f765345e9e4..6d39a5fff7c 100644 --- a/src/backend/access/heap/heapam_xlog.c +++ b/src/backend/access/heap/heapam_xlog.c @@ -164,7 +164,10 @@ heap_xlog_prune_freeze(XLogReaderState *record) * modification would fail to clear the visibility map bit. */ if (vmflags & VISIBILITYMAP_VALID_BITS) + { PageSetAllVisible(page); + PageClearPrunable(page); + } MarkBufferDirty(buffer); @@ -305,6 +308,7 @@ heap_xlog_visible(XLogReaderState *record) page = BufferGetPage(buffer); PageSetAllVisible(page); + PageClearPrunable(page); if (XLogHintBitIsNeeded()) PageSetLSN(page, lsn); @@ -734,7 +738,10 @@ heap_xlog_multi_insert(XLogReaderState *record) /* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */ if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET) + { PageSetAllVisible(page); + PageClearPrunable(page); + } MarkBufferDirty(buffer); } diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 5d271d80967..5b6f2441f6b 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -1933,6 +1933,7 @@ lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf, BlockNumber blkno, log_newpage_buffer(buf, true); PageSetAllVisible(page); + PageClearPrunable(page); visibilitymap_set(vacrel->rel, blkno, buf, InvalidXLogRecPtr, vmbuffer, InvalidTransactionId, @@ -2209,6 +2210,7 @@ lazy_scan_prune(LVRelState *vacrel, * the VM bits clear, so there is no point in optimizing it. */ PageSetAllVisible(page); + PageClearPrunable(page); MarkBufferDirty(buf); /* @@ -2944,6 +2946,7 @@ lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno, Buffer buffer, * set PD_ALL_VISIBLE. */ PageSetAllVisible(page); + PageClearPrunable(page); visibilitymap_set_vmbits(blkno, vmbuffer, vmflags, vacrel->rel->rd_locator);