From aee92ee8a07beade81a82200fbbfe605d499ac4c Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Tue, 16 Sep 2025 14:22:10 -0400 Subject: [PATCH v14 06/24] Add helper for freeze determination to heap_page_prune_and_freeze After scanning through the line pointers on the heap page during vacuum's first phase, we use several statuses and information we collected to determine whether or not we will use the freeze plans we assembled. Do this in a helper for better readability. --- src/backend/access/heap/pruneheap.c | 199 +++++++++++++++++----------- 1 file changed, 119 insertions(+), 80 deletions(-) diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 740aa07cd83..4ed74de6f27 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -289,6 +289,120 @@ heap_page_prune_opt(Relation relation, Buffer buffer) } } +/* + * Decide if we want to go ahead with freezing according to the freeze plans + * we prepared for the given heap buffer or not. If the caller specified we + * should not freeze tuples, it exits early. Otherwise, it does a few + * pre-freeze checks. + * + * do_prune, do_hint_full_or_prunable, and did_tuple_hint_fpi must all have + * been decided before calling this function. + * + * prstate is an input/output parameter. + * + * Returns true if we should use our freeze plans and freeze tuples on the page + * and false otherwise. + */ +static bool +heap_page_will_freeze(Relation relation, Buffer buffer, + bool did_tuple_hint_fpi, + bool do_prune, + bool do_hint_prune, + PruneState *prstate) +{ + bool do_freeze = false; + + /* + * If the caller specified we should not attempt to freeze any tuples, + * validate that everything is in the right state and exit. + */ + if (!prstate->attempt_freeze) + { + Assert(!prstate->all_frozen && prstate->nfrozen == 0); + Assert(prstate->lpdead_items == 0 || !prstate->all_visible); + return false; + } + + if (prstate->pagefrz.freeze_required) + { + /* + * heap_prepare_freeze_tuple indicated that at least one XID/MXID from + * before FreezeLimit/MultiXactCutoff is present. Must freeze to + * advance relfrozenxid/relminmxid. + */ + do_freeze = true; + } + else + { + /* + * Opportunistically freeze the page if we are generating an FPI + * anyway and if doing so means that we can set the page all-frozen + * afterwards (might not happen until VACUUM's final heap pass). + * + * XXX: Previously, we knew if pruning emitted an FPI by checking + * pgWalUsage.wal_fpi before and after pruning. Once the freeze and + * prune records were combined, this heuristic couldn't be used + * anymore. The opportunistic freeze heuristic must be improved; + * however, for now, try to approximate the old logic. + */ + if (prstate->all_frozen && prstate->nfrozen > 0) + { + Assert(prstate->all_visible); + + /* + * Freezing would make the page all-frozen. Have already emitted + * an FPI or will do so anyway? + */ + if (RelationNeedsWAL(relation)) + { + if (did_tuple_hint_fpi) + do_freeze = true; + else if (do_prune) + { + if (XLogCheckBufferNeedsBackup(buffer)) + do_freeze = true; + } + else if (do_hint_prune) + { + if (XLogHintBitIsNeeded() && XLogCheckBufferNeedsBackup(buffer)) + do_freeze = true; + } + } + } + } + + if (do_freeze) + { + /* + * Validate the tuples we will be freezing before entering the + * critical section. + */ + heap_pre_freeze_checks(buffer, prstate->frozen, prstate->nfrozen); + } + else if (prstate->nfrozen > 0) + { + /* + * The page contained some tuples that were not already frozen, and we + * chose not to freeze them now. The page won't be all-frozen then. + */ + Assert(!prstate->pagefrz.freeze_required); + + prstate->all_frozen = false; + prstate->nfrozen = 0; /* avoid miscounts in instrumentation */ + } + else + { + /* + * We have no freeze plans to execute. The page might already be + * all-frozen (perhaps only following pruning), though. Such pages + * can be marked all-frozen in the VM by our caller, even though none + * of its tuples were newly frozen here. + */ + } + + return do_freeze; +} + /* * Prune and repair fragmentation and potentially freeze tuples on the @@ -666,87 +780,12 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer, * Decide if we want to go ahead with freezing according to the freeze * plans we prepared, or not. */ - do_freeze = false; - if (prstate.attempt_freeze) - { - if (prstate.pagefrz.freeze_required) - { - /* - * heap_prepare_freeze_tuple indicated that at least one XID/MXID - * from before FreezeLimit/MultiXactCutoff is present. Must - * freeze to advance relfrozenxid/relminmxid. - */ - do_freeze = true; - } - else - { - /* - * Opportunistically freeze the page if we are generating an FPI - * anyway and if doing so means that we can set the page - * all-frozen afterwards (might not happen until VACUUM's final - * heap pass). - * - * XXX: Previously, we knew if pruning emitted an FPI by checking - * pgWalUsage.wal_fpi before and after pruning. Once the freeze - * and prune records were combined, this heuristic couldn't be - * used anymore. The opportunistic freeze heuristic must be - * improved; however, for now, try to approximate the old logic. - */ - if (prstate.all_frozen && prstate.nfrozen > 0) - { - Assert(prstate.all_visible); + do_freeze = heap_page_will_freeze(relation, buffer, + did_tuple_hint_fpi, + do_prune, + do_hint_prune, + &prstate); - /* - * Freezing would make the page all-frozen. Have already - * emitted an FPI or will do so anyway? - */ - if (RelationNeedsWAL(relation)) - { - if (did_tuple_hint_fpi) - do_freeze = true; - else if (do_prune) - { - if (XLogCheckBufferNeedsBackup(buffer)) - do_freeze = true; - } - else if (do_hint_prune) - { - if (XLogHintBitIsNeeded() && XLogCheckBufferNeedsBackup(buffer)) - do_freeze = true; - } - } - } - } - } - - if (do_freeze) - { - /* - * Validate the tuples we will be freezing before entering the - * critical section. - */ - heap_pre_freeze_checks(buffer, prstate.frozen, prstate.nfrozen); - } - else if (prstate.nfrozen > 0) - { - /* - * The page contained some tuples that were not already frozen, and we - * chose not to freeze them now. The page won't be all-frozen then. - */ - Assert(!prstate.pagefrz.freeze_required); - - prstate.all_frozen = false; - prstate.nfrozen = 0; /* avoid miscounts in instrumentation */ - } - else - { - /* - * We have no freeze plans to execute. The page might already be - * all-frozen (perhaps only following pruning), though. Such pages - * can be marked all-frozen in the VM by our caller, even though none - * of its tuples were newly frozen here. - */ - } Assert(!prstate.all_frozen || prstate.all_visible); /* Any error while applying the changes is critical */ -- 2.43.0