From d2f59fcb90b206ba9b631bab4dcc1414f17a80af Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Mon, 18 Mar 2024 20:12:18 -0400 Subject: [PATCH v5 23/26] Set hastup in heap_page_prune lazy_scan_prune() loops through the line pointers and tuple visibility information for each tuple on a page, setting hastup to true if there are any LP_REDIRECT line pointers or tuples with storage which will not be removed. We want to remove this extra loop from lazy_scan_prune(), and we know about non-removable tuples during heap_page_prune() anyway. Set hastup when recording LP_REDIRECT line pointers in heap_prune_chain() and when LP_NORMAL line pointers refer to tuples whose visibility status is not HEAPTUPLE_DEAD. --- src/backend/access/heap/pruneheap.c | 64 ++++++++++++++++++---------- src/backend/access/heap/vacuumlazy.c | 24 +---------- src/include/access/heapam.h | 2 + 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index adf6406b880..b44dc149376 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -71,7 +71,8 @@ static int heap_prune_chain(Buffer buffer, static void heap_prune_record_prunable(PruneState *prstate, TransactionId xid); static void heap_prune_record_redirect(PruneState *prstate, - OffsetNumber offnum, OffsetNumber rdoffnum); + OffsetNumber offnum, OffsetNumber rdoffnum, + PruneFreezeResult *presult); static void heap_prune_record_dead(PruneState *prstate, OffsetNumber offnum, PruneFreezeResult *presult); static void heap_prune_record_dead_or_unused(PruneState *prstate, OffsetNumber offnum, @@ -274,6 +275,8 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer, presult->nnewlpdead = 0; presult->nfrozen = 0; + presult->hastup = false; + /* * Caller will update the VM after pruning, collecting LP_DEAD items, and * freezing tuples. Keep track of whether or not the page is all_visible @@ -416,30 +419,42 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer, break; } - /* - * Consider freezing any normal tuples which will not be removed - */ - if (presult->htsv[offnum] != HEAPTUPLE_DEAD && pagefrz) + if (presult->htsv[offnum] != HEAPTUPLE_DEAD) { - bool totally_frozen; + /* + * Deliberately don't set hastup for LP_DEAD items. We make the + * soft assumption that any LP_DEAD items encountered here will + * become LP_UNUSED later on, before count_nondeletable_pages is + * reached. If we don't make this assumption then rel truncation + * will only happen every other VACUUM, at most. Besides, VACUUM + * must treat hastup/nonempty_pages as provisional no matter how + * LP_DEAD items are handled (handled here, or handled later on). + */ + presult->hastup = true; - /* Tuple with storage -- consider need to freeze */ - if ((heap_prepare_freeze_tuple(htup, pagefrz, - &prstate.frozen[presult->nfrozen], - &totally_frozen))) + /* Consider freezing any normal tuples which will not be removed */ + if (pagefrz) { - /* Save prepared freeze plan for later */ - prstate.frozen[presult->nfrozen++].offset = offnum; - } + bool totally_frozen; - /* - * If any tuple isn't either totally frozen already or eligible to - * become totally frozen (according to its freeze plan), then the - * page definitely cannot be set all-frozen in the visibility map - * later on - */ - if (!totally_frozen) - presult->all_frozen = false; + /* Tuple with storage -- consider need to freeze */ + if ((heap_prepare_freeze_tuple(htup, pagefrz, + &prstate.frozen[presult->nfrozen], + &totally_frozen))) + { + /* Save prepared freeze plan for later */ + prstate.frozen[presult->nfrozen++].offset = offnum; + } + + /* + * If any tuple isn't either totally frozen already or + * eligible to become totally frozen (according to its freeze + * plan), then the page definitely cannot be set all-frozen in + * the visibility map later on + */ + if (!totally_frozen) + presult->all_frozen = false; + } } } @@ -993,7 +1008,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, if (i >= nchain) heap_prune_record_dead_or_unused(prstate, rootoffnum, presult); else - heap_prune_record_redirect(prstate, rootoffnum, chainitems[i]); + heap_prune_record_redirect(prstate, rootoffnum, chainitems[i], presult); } else if (nchain < 2 && ItemIdIsRedirected(rootlp)) { @@ -1027,7 +1042,8 @@ heap_prune_record_prunable(PruneState *prstate, TransactionId xid) /* Record line pointer to be redirected */ static void heap_prune_record_redirect(PruneState *prstate, - OffsetNumber offnum, OffsetNumber rdoffnum) + OffsetNumber offnum, OffsetNumber rdoffnum, + PruneFreezeResult *presult) { Assert(prstate->nredirected < MaxHeapTuplesPerPage); prstate->redirected[prstate->nredirected * 2] = offnum; @@ -1037,6 +1053,8 @@ heap_prune_record_redirect(PruneState *prstate, prstate->marked[offnum] = true; Assert(!prstate->marked[rdoffnum]); prstate->marked[rdoffnum] = true; + + presult->hastup = true; } /* Record line pointer to be marked dead */ diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index fe7751493e2..c87ab76c78e 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -1420,7 +1420,6 @@ lazy_scan_prune(LVRelState *vacrel, live_tuples, recently_dead_tuples; HeapPageFreeze pagefrz; - bool hastup = false; OffsetNumber deadoffsets[MaxHeapTuplesPerPage]; Assert(BufferGetBlockNumber(buf) == blkno); @@ -1477,28 +1476,12 @@ lazy_scan_prune(LVRelState *vacrel, vacrel->offnum = offnum; itemid = PageGetItemId(page, offnum); - if (!ItemIdIsUsed(itemid)) - continue; - /* Redirect items mustn't be touched */ - if (ItemIdIsRedirected(itemid)) - { - /* page makes rel truncation unsafe */ - hastup = true; + if (ItemIdIsRedirected(itemid) || !ItemIdIsUsed(itemid)) continue; - } if (ItemIdIsDead(itemid)) { - /* - * Deliberately don't set hastup for LP_DEAD items. We make the - * soft assumption that any LP_DEAD items encountered here will - * become LP_UNUSED later on, before count_nondeletable_pages is - * reached. If we don't make this assumption then rel truncation - * will only happen every other VACUUM, at most. Besides, VACUUM - * must treat hastup/nonempty_pages as provisional no matter how - * LP_DEAD items are handled (handled here, or handled later on). - */ deadoffsets[lpdead_items++] = offnum; continue; } @@ -1566,9 +1549,6 @@ lazy_scan_prune(LVRelState *vacrel, elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result"); break; } - - hastup = true; /* page makes rel truncation unsafe */ - } vacrel->offnum = InvalidOffsetNumber; @@ -1650,7 +1630,7 @@ lazy_scan_prune(LVRelState *vacrel, vacrel->recently_dead_tuples += recently_dead_tuples; /* Can't truncate this page */ - if (hastup) + if (presult.hastup) vacrel->nonempty_pages = blkno + 1; /* Did we find LP_DEAD items? */ diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 7a5bc018088..94573a59dd3 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -201,6 +201,8 @@ typedef struct PruneFreezeResult int ndeleted; /* Number of tuples deleted from the page */ int nnewlpdead; /* Number of newly LP_DEAD items */ bool all_visible; /* Whether or not the page is all visible */ + bool hastup; /* Does page make rel truncation unsafe */ + /* Whether or not the page can be set all frozen in the VM */ bool all_frozen; -- 2.39.2