mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Add helper for freeze determination to heap_page_prune_and_freeze
After scanning the line pointers on a heap page during the first phase of vacuum, we use the information collected to decide whether to use the assembled freeze plans. Move this decision logic into a helper function to improve readability. While here, rename a PruneState member and disambiguate some local variables in heap_page_prune_and_freeze(). Author: Melanie Plageman <melanieplageman@gmail.com> Reviewed-by: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/2wk7jo4m4qwh5sn33pfgerdjfujebbccsmmlownybddbh6nawl%40mdyyqpqzxjek
This commit is contained in:
@@ -43,7 +43,7 @@ typedef struct
|
|||||||
/* whether or not dead items can be set LP_UNUSED during pruning */
|
/* whether or not dead items can be set LP_UNUSED during pruning */
|
||||||
bool mark_unused_now;
|
bool mark_unused_now;
|
||||||
/* whether to attempt freezing tuples */
|
/* whether to attempt freezing tuples */
|
||||||
bool freeze;
|
bool attempt_freeze;
|
||||||
struct VacuumCutoffs *cutoffs;
|
struct VacuumCutoffs *cutoffs;
|
||||||
|
|
||||||
/*-------------------------------------------------------
|
/*-------------------------------------------------------
|
||||||
@@ -177,6 +177,10 @@ static void heap_prune_record_unchanged_lp_redirect(PruneState *prstate, OffsetN
|
|||||||
|
|
||||||
static void page_verify_redirects(Page page);
|
static void page_verify_redirects(Page page);
|
||||||
|
|
||||||
|
static bool heap_page_will_freeze(Relation relation, Buffer buffer,
|
||||||
|
bool did_tuple_hint_fpi, bool do_prune, bool do_hint_prune,
|
||||||
|
PruneState *prstate);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Optionally prune and repair fragmentation in the specified page.
|
* Optionally prune and repair fragmentation in the specified page.
|
||||||
@@ -294,6 +298,117 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decide whether to proceed with freezing according to the freeze plans
|
||||||
|
* prepared for the given heap buffer. If freezing is chosen, this function
|
||||||
|
* performs several pre-freeze checks.
|
||||||
|
*
|
||||||
|
* The values of do_prune, do_hint_prune, and did_tuple_hint_fpi must be
|
||||||
|
* determined before calling this function.
|
||||||
|
*
|
||||||
|
* prstate is both an input and output parameter.
|
||||||
|
*
|
||||||
|
* Returns true if we should apply the 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 return.
|
||||||
|
*/
|
||||||
|
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_visible && prstate->all_frozen && prstate->nfrozen > 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 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
|
* Prune and repair fragmentation and potentially freeze tuples on the
|
||||||
@@ -366,14 +481,14 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
|
|||||||
HeapTupleData tup;
|
HeapTupleData tup;
|
||||||
bool do_freeze;
|
bool do_freeze;
|
||||||
bool do_prune;
|
bool do_prune;
|
||||||
bool do_hint;
|
bool do_hint_prune;
|
||||||
bool hint_bit_fpi;
|
bool did_tuple_hint_fpi;
|
||||||
int64 fpi_before = pgWalUsage.wal_fpi;
|
int64 fpi_before = pgWalUsage.wal_fpi;
|
||||||
|
|
||||||
/* Copy parameters to prstate */
|
/* Copy parameters to prstate */
|
||||||
prstate.vistest = vistest;
|
prstate.vistest = vistest;
|
||||||
prstate.mark_unused_now = (options & HEAP_PAGE_PRUNE_MARK_UNUSED_NOW) != 0;
|
prstate.mark_unused_now = (options & HEAP_PAGE_PRUNE_MARK_UNUSED_NOW) != 0;
|
||||||
prstate.freeze = (options & HEAP_PAGE_PRUNE_FREEZE) != 0;
|
prstate.attempt_freeze = (options & HEAP_PAGE_PRUNE_FREEZE) != 0;
|
||||||
prstate.cutoffs = cutoffs;
|
prstate.cutoffs = cutoffs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -395,7 +510,7 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
|
|||||||
|
|
||||||
/* initialize page freezing working state */
|
/* initialize page freezing working state */
|
||||||
prstate.pagefrz.freeze_required = false;
|
prstate.pagefrz.freeze_required = false;
|
||||||
if (prstate.freeze)
|
if (prstate.attempt_freeze)
|
||||||
{
|
{
|
||||||
Assert(new_relfrozen_xid && new_relmin_mxid);
|
Assert(new_relfrozen_xid && new_relmin_mxid);
|
||||||
prstate.pagefrz.FreezePageRelfrozenXid = *new_relfrozen_xid;
|
prstate.pagefrz.FreezePageRelfrozenXid = *new_relfrozen_xid;
|
||||||
@@ -442,7 +557,7 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
|
|||||||
* function, when we return the value to the caller, so that the caller
|
* function, when we return the value to the caller, so that the caller
|
||||||
* doesn't set the VM bit incorrectly.
|
* doesn't set the VM bit incorrectly.
|
||||||
*/
|
*/
|
||||||
if (prstate.freeze)
|
if (prstate.attempt_freeze)
|
||||||
{
|
{
|
||||||
prstate.all_visible = true;
|
prstate.all_visible = true;
|
||||||
prstate.all_frozen = true;
|
prstate.all_frozen = true;
|
||||||
@@ -556,7 +671,7 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
|
|||||||
* If checksums are enabled, heap_prune_satisfies_vacuum() may have caused
|
* If checksums are enabled, heap_prune_satisfies_vacuum() may have caused
|
||||||
* an FPI to be emitted.
|
* an FPI to be emitted.
|
||||||
*/
|
*/
|
||||||
hint_bit_fpi = fpi_before != pgWalUsage.wal_fpi;
|
did_tuple_hint_fpi = fpi_before != pgWalUsage.wal_fpi;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process HOT chains.
|
* Process HOT chains.
|
||||||
@@ -664,97 +779,23 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
|
|||||||
* pd_prune_xid field or the page was marked full, we will update the hint
|
* pd_prune_xid field or the page was marked full, we will update the hint
|
||||||
* bit.
|
* bit.
|
||||||
*/
|
*/
|
||||||
do_hint = ((PageHeader) page)->pd_prune_xid != prstate.new_prune_xid ||
|
do_hint_prune = ((PageHeader) page)->pd_prune_xid != prstate.new_prune_xid ||
|
||||||
PageIsFull(page);
|
PageIsFull(page);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decide if we want to go ahead with freezing according to the freeze
|
* Decide if we want to go ahead with freezing according to the freeze
|
||||||
* plans we prepared, or not.
|
* plans we prepared, or not.
|
||||||
*/
|
*/
|
||||||
do_freeze = false;
|
do_freeze = heap_page_will_freeze(relation, buffer,
|
||||||
if (prstate.freeze)
|
did_tuple_hint_fpi,
|
||||||
{
|
do_prune,
|
||||||
if (prstate.pagefrz.freeze_required)
|
do_hint_prune,
|
||||||
{
|
&prstate);
|
||||||
/*
|
|
||||||
* 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_visible && prstate.all_frozen && prstate.nfrozen > 0)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Freezing would make the page all-frozen. Have already
|
|
||||||
* emitted an FPI or will do so anyway?
|
|
||||||
*/
|
|
||||||
if (RelationNeedsWAL(relation))
|
|
||||||
{
|
|
||||||
if (hint_bit_fpi)
|
|
||||||
do_freeze = true;
|
|
||||||
else if (do_prune)
|
|
||||||
{
|
|
||||||
if (XLogCheckBufferNeedsBackup(buffer))
|
|
||||||
do_freeze = true;
|
|
||||||
}
|
|
||||||
else if (do_hint)
|
|
||||||
{
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Any error while applying the changes is critical */
|
/* Any error while applying the changes is critical */
|
||||||
START_CRIT_SECTION();
|
START_CRIT_SECTION();
|
||||||
|
|
||||||
if (do_hint)
|
if (do_hint_prune)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Update the page's pd_prune_xid field to either zero, or the lowest
|
* Update the page's pd_prune_xid field to either zero, or the lowest
|
||||||
@@ -897,7 +938,7 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
|
|||||||
presult->lpdead_items = prstate.lpdead_items;
|
presult->lpdead_items = prstate.lpdead_items;
|
||||||
/* the presult->deadoffsets array was already filled in */
|
/* the presult->deadoffsets array was already filled in */
|
||||||
|
|
||||||
if (prstate.freeze)
|
if (prstate.attempt_freeze)
|
||||||
{
|
{
|
||||||
if (presult->nfrozen > 0)
|
if (presult->nfrozen > 0)
|
||||||
{
|
{
|
||||||
@@ -1479,7 +1520,7 @@ heap_prune_record_unchanged_lp_normal(Page page, PruneState *prstate, OffsetNumb
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Consider freezing any normal tuples which will not be removed */
|
/* Consider freezing any normal tuples which will not be removed */
|
||||||
if (prstate->freeze)
|
if (prstate->attempt_freeze)
|
||||||
{
|
{
|
||||||
bool totally_frozen;
|
bool totally_frozen;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user