From a5fe00defc0551f453d44ac50cf807dd667cbf7e Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Sun, 7 Jan 2024 16:53:45 -0500 Subject: [PATCH v5 18/26] Separate tuple pre freeze checks and invoke earlier When combining the prune and freeze records their critical sections will have to be combined. heap_freeze_execute_prepared() does a set of pre freeze validations before starting its critical section. Move these validations into a helper function, heap_pre_freeze_checks(), and invoke it in heap_page_prune() before the pruning critical section. Also move up the calculation of the freeze snapshot conflict horizon. --- src/backend/access/heap/heapam.c | 58 ++++++++++++++++------------- src/backend/access/heap/pruneheap.c | 31 ++++++++------- src/include/access/heapam.h | 3 ++ 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 8779fd04305..3c1be5e78c1 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -6657,35 +6657,19 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, HeapTupleFreeze *frz) } /* - * heap_freeze_execute_prepared - * - * Executes freezing of one or more heap tuples on a page on behalf of caller. - * Caller passes an array of tuple plans from heap_prepare_freeze_tuple. - * Caller must set 'offset' in each plan for us. Note that we destructively - * sort caller's tuples array in-place, so caller had better be done with it. - * - * WAL-logs the changes so that VACUUM can advance the rel's relfrozenxid - * later on without any risk of unsafe pg_xact lookups, even following a hard - * crash (or when querying from a standby). We represent freezing by setting - * infomask bits in tuple headers, but this shouldn't be thought of as a hint. - * See section on buffer access rules in src/backend/storage/buffer/README. - */ +* Perform xmin/xmax XID status sanity checks before calling +* heap_freeze_execute_prepared(). +* +* heap_prepare_freeze_tuple doesn't perform these checks directly because +* pg_xact lookups are relatively expensive. They shouldn't be repeated +* by successive VACUUMs that each decide against freezing the same page. +*/ void -heap_freeze_execute_prepared(Relation rel, Buffer buffer, - TransactionId snapshotConflictHorizon, - HeapTupleFreeze *tuples, int ntuples) +heap_pre_freeze_checks(Buffer buffer, + HeapTupleFreeze *tuples, int ntuples) { Page page = BufferGetPage(buffer); - Assert(ntuples > 0); - - /* - * Perform xmin/xmax XID status sanity checks before critical section. - * - * heap_prepare_freeze_tuple doesn't perform these checks directly because - * pg_xact lookups are relatively expensive. They shouldn't be repeated - * by successive VACUUMs that each decide against freezing the same page. - */ for (int i = 0; i < ntuples; i++) { HeapTupleFreeze *frz = tuples + i; @@ -6724,6 +6708,30 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer, xmax))); } } +} + +/* + * heap_freeze_execute_prepared + * + * Executes freezing of one or more heap tuples on a page on behalf of caller. + * Caller passes an array of tuple plans from heap_prepare_freeze_tuple. + * Caller must set 'offset' in each plan for us. Note that we destructively + * sort caller's tuples array in-place, so caller had better be done with it. + * + * WAL-logs the changes so that VACUUM can advance the rel's relfrozenxid + * later on without any risk of unsafe pg_xact lookups, even following a hard + * crash (or when querying from a standby). We represent freezing by setting + * infomask bits in tuple headers, but this shouldn't be thought of as a hint. + * See section on buffer access rules in src/backend/storage/buffer/README. + */ +void +heap_freeze_execute_prepared(Relation rel, Buffer buffer, + TransactionId snapshotConflictHorizon, + HeapTupleFreeze *tuples, int ntuples) +{ + Page page = BufferGetPage(buffer); + + Assert(ntuples > 0); START_CRIT_SECTION(); diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index d919f243c03..81c926cc6e1 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -523,6 +523,24 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer, (pagefrz->freeze_required || (whole_page_freezable && presult->nfrozen > 0 && (prune_fpi || hint_bit_fpi))); + if (do_freeze) + { + heap_pre_freeze_checks(buffer, prstate.frozen, presult->nfrozen); + + /* + * We can use frz_conflict_horizon as our cutoff for conflicts when + * the whole page is eligible to become all-frozen in the VM once + * we're done with it. Otherwise we generate a conservative cutoff by + * stepping back from OldestXmin. + */ + if (!(presult->all_visible_except_removable && presult->all_frozen)) + { + /* Avoids false conflicts when hot_standby_feedback in use */ + presult->frz_conflict_horizon = pagefrz->cutoffs->OldestXmin; + TransactionIdRetreat(presult->frz_conflict_horizon); + } + } + /* Any error while applying the changes is critical */ START_CRIT_SECTION(); @@ -594,19 +612,6 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer, if (do_freeze) { - /* - * We can use frz_conflict_horizon as our cutoff for conflicts when - * the whole page is eligible to become all-frozen in the VM once - * we're done with it. Otherwise we generate a conservative cutoff by - * stepping back from OldestXmin. - */ - if (!(presult->all_visible_except_removable && presult->all_frozen)) - { - /* Avoids false conflicts when hot_standby_feedback in use */ - presult->frz_conflict_horizon = pagefrz->cutoffs->OldestXmin; - TransactionIdRetreat(presult->frz_conflict_horizon); - } - /* Execute all freeze plans for page as a single atomic action */ heap_freeze_execute_prepared(relation, buffer, presult->frz_conflict_horizon, diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 9b24dae6a9e..340ac813a0f 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -312,6 +312,9 @@ extern void heap_inplace_update(Relation relation, HeapTuple tuple); extern bool heap_prepare_freeze_tuple(HeapTupleHeader tuple, HeapPageFreeze *pagefrz, HeapTupleFreeze *frz, bool *totally_frozen); + +extern void heap_pre_freeze_checks(Buffer buffer, + HeapTupleFreeze *tuples, int ntuples); extern void heap_freeze_execute_prepared(Relation rel, Buffer buffer, TransactionId snapshotConflictHorizon, HeapTupleFreeze *tuples, int ntuples); -- 2.39.2