From 586abbc3656bebd3074aaefc615a2a0ff6d6e4ae Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Fri, 11 Sep 2026 15:55:34 -0400 Subject: [PATCH v2] Avoid setting pd_prune_xid when inserting frozen tuples 378a216187a set pd_prune_xid on inserts so that on-access pruning could later set the pages all-visible. heap_multi_insert() skipped this only when setting the page all-frozen, but it can insert frozen tuples even when not setting the page all-frozen. Inserting frozen tuples should not set pd_prune_xid as they introduce no work for on-access pruning. Fix by only setting pd_prune_xid when inserting non-frozen tuples. --- src/backend/access/heap/heapam.c | 6 ++--- src/backend/access/heap/heapam_xlog.c | 38 +++++++++++++++++---------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 10766d330a9..65ac2dc06f8 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -2493,10 +2493,10 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, /* * Set pd_prune_xid. See heap_insert() for more on why we do this when - * inserting tuples. This only makes sense if we aren't already - * setting the page frozen in the VM and we're not in bootstrap mode. + * inserting tuples. This only makes sense if the tuples aren't frozen + * and we're not in bootstrap mode. */ - if (!all_frozen_set && TransactionIdIsNormal(xid)) + if (TransactionIdIsNormal(xid) && !(options & HEAP_INSERT_FROZEN)) PageSetPrunable(page, xid); MarkBufferDirty(buffer); diff --git a/src/backend/access/heap/heapam_xlog.c b/src/backend/access/heap/heapam_xlog.c index 7a7bc7ea740..4630e32f26d 100644 --- a/src/backend/access/heap/heapam_xlog.c +++ b/src/backend/access/heap/heapam_xlog.c @@ -529,15 +529,7 @@ heap_xlog_multi_insert(XLogReaderState *record) BlockNumber blkno; Buffer buffer; Page page; - union - { - HeapTupleHeaderData hdr; - char data[MaxHeapTupleSize]; - } tbuf; - HeapTupleHeader htup; - uint32 newlen; Size freespace = 0; - int i; bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0; XLogRedoAction action; Buffer vmbuffer = InvalidBuffer; @@ -583,6 +575,7 @@ heap_xlog_multi_insert(XLogReaderState *record) char *tupdata; char *endptr; Size len; + bool inserted_tuples_frozen = false; /* Tuples are stored as block data */ tupdata = XLogRecGetBlockData(record, HEAP_MULTI_INSERT_BLKREF_HEAP, @@ -591,8 +584,15 @@ heap_xlog_multi_insert(XLogReaderState *record) page = BufferGetPage(buffer); - for (i = 0; i < xlrec->ntuples; i++) + for (int i = 0; i < xlrec->ntuples; i++) { + union + { + HeapTupleHeaderData hdr; + char data[MaxHeapTupleSize]; + } tbuf; + HeapTupleHeader htup; + uint32 newlen; OffsetNumber offnum; xl_multi_insert_tuple *xlhdr; @@ -630,6 +630,10 @@ heap_xlog_multi_insert(XLogReaderState *record) ItemPointerSetBlockNumber(&htup->t_ctid, blkno); ItemPointerSetOffsetNumber(&htup->t_ctid, offnum); + /* If one inserted tuple was frozen, they all were */ + if (i == 0) + inserted_tuples_frozen = HeapTupleHeaderXminFrozen(htup); + offnum = PageAddItem(page, htup, newlen, offnum, true, true); if (offnum == InvalidOffsetNumber) elog(PANIC, "failed to add tuple"); @@ -645,17 +649,23 @@ heap_xlog_multi_insert(XLogReaderState *record) PageClearAllVisible(page); /* - * XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible. If - * we are not setting the page frozen, then set the page's prunable - * hint so that we trigger on-access pruning later which may set the - * page all-visible in the VM. + * XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible, so + * set PD_ALL_VISIBLE and clear pd_prune_xid. + * + * If the page isn't being set all-frozen and we aren't inserting + * frozen tuples, set pd_prune_xid so that the page gets on-access + * pruned. + * + * Frozen tuples may be added to an already all-frozen page or to a + * page containing non-frozen tuples, but they introduce nothing new + * for on-access pruning, so preserve the existing hint. */ if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET) { PageSetAllVisible(page); PageClearPrunable(page); } - else + else if (!inserted_tuples_frozen) PageSetPrunable(page, XLogRecGetXid(record)); MarkBufferDirty(buffer); -- 2.50.1 (Apple Git-155)