mirror of
https://github.com/postgres/postgres.git
synced 2025-11-24 00:23:06 +03:00
Keep all_frozen updated in heap_page_prune_and_freeze
Previously, we relied on all_visible and all_frozen being used together to ensure that all_frozen was correct, but it is better to keep both fields updated. Future changes will separate their usage, so we should not depend on all_visible for the validity of all_frozen. Author: Melanie Plageman <melanieplageman@gmail.com> Reviewed-by: Kirill Reshke <reshkekirill@gmail.com> Discussion: https://postgr.es/m/flat/CAAKRu_ZMw6Npd_qm2KM%2BFwQ3cMOMx1Dh3VMhp8-V7SOLxdK9-g%40mail.gmail.com
This commit is contained in:
@@ -143,10 +143,6 @@ typedef struct
|
|||||||
* whether to freeze the page or not. The all_visible and all_frozen
|
* whether to freeze the page or not. The all_visible and all_frozen
|
||||||
* values returned to the caller are adjusted to include LP_DEAD items at
|
* values returned to the caller are adjusted to include LP_DEAD items at
|
||||||
* the end.
|
* the end.
|
||||||
*
|
|
||||||
* all_frozen should only be considered valid if all_visible is also set;
|
|
||||||
* we don't bother to clear the all_frozen flag every time we clear the
|
|
||||||
* all_visible flag.
|
|
||||||
*/
|
*/
|
||||||
bool all_visible;
|
bool all_visible;
|
||||||
bool all_frozen;
|
bool all_frozen;
|
||||||
@@ -359,8 +355,10 @@ heap_page_will_freeze(Relation relation, Buffer buffer,
|
|||||||
* anymore. The opportunistic freeze heuristic must be improved;
|
* anymore. The opportunistic freeze heuristic must be improved;
|
||||||
* however, for now, try to approximate the old logic.
|
* however, for now, try to approximate the old logic.
|
||||||
*/
|
*/
|
||||||
if (prstate->all_visible && prstate->all_frozen && prstate->nfrozen > 0)
|
if (prstate->all_frozen && prstate->nfrozen > 0)
|
||||||
{
|
{
|
||||||
|
Assert(prstate->all_visible);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Freezing would make the page all-frozen. Have already emitted
|
* Freezing would make the page all-frozen. Have already emitted
|
||||||
* an FPI or will do so anyway?
|
* an FPI or will do so anyway?
|
||||||
@@ -544,9 +542,9 @@ heap_page_prune_and_freeze(PruneFreezeParams *params,
|
|||||||
* dead tuples which are not yet removable. However, dead tuples which
|
* dead tuples which are not yet removable. However, dead tuples which
|
||||||
* will be removed by the end of vacuuming should not preclude us from
|
* will be removed by the end of vacuuming should not preclude us from
|
||||||
* opportunistically freezing. Because of that, we do not clear
|
* opportunistically freezing. Because of that, we do not clear
|
||||||
* all_visible when we see LP_DEAD items. We fix that at the end of the
|
* all_visible and all_frozen when we see LP_DEAD items. We fix that at
|
||||||
* function, when we return the value to the caller, so that the caller
|
* the end of the function, when we return the value to the caller, so
|
||||||
* doesn't set the VM bit incorrectly.
|
* that the caller doesn't set the VM bits incorrectly.
|
||||||
*/
|
*/
|
||||||
if (prstate.attempt_freeze)
|
if (prstate.attempt_freeze)
|
||||||
{
|
{
|
||||||
@@ -783,6 +781,8 @@ heap_page_prune_and_freeze(PruneFreezeParams *params,
|
|||||||
do_hint_prune,
|
do_hint_prune,
|
||||||
&prstate);
|
&prstate);
|
||||||
|
|
||||||
|
Assert(!prstate.all_frozen || prstate.all_visible);
|
||||||
|
|
||||||
/* Any error while applying the changes is critical */
|
/* Any error while applying the changes is critical */
|
||||||
START_CRIT_SECTION();
|
START_CRIT_SECTION();
|
||||||
|
|
||||||
@@ -852,7 +852,7 @@ heap_page_prune_and_freeze(PruneFreezeParams *params,
|
|||||||
*/
|
*/
|
||||||
if (do_freeze)
|
if (do_freeze)
|
||||||
{
|
{
|
||||||
if (prstate.all_visible && prstate.all_frozen)
|
if (prstate.all_frozen)
|
||||||
frz_conflict_horizon = prstate.visibility_cutoff_xid;
|
frz_conflict_horizon = prstate.visibility_cutoff_xid;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -889,16 +889,16 @@ heap_page_prune_and_freeze(PruneFreezeParams *params,
|
|||||||
presult->recently_dead_tuples = prstate.recently_dead_tuples;
|
presult->recently_dead_tuples = prstate.recently_dead_tuples;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It was convenient to ignore LP_DEAD items in all_visible earlier on to
|
* It was convenient to ignore LP_DEAD items in all_visible/all_frozen
|
||||||
* make the choice of whether or not to freeze the page unaffected by the
|
* earlier on to make the choice of whether or not to freeze the page
|
||||||
* short-term presence of LP_DEAD items. These LP_DEAD items were
|
* unaffected by the short-term presence of LP_DEAD items. These LP_DEAD
|
||||||
* effectively assumed to be LP_UNUSED items in the making. It doesn't
|
* items were effectively assumed to be LP_UNUSED items in the making. It
|
||||||
* matter which vacuum heap pass (initial pass or final pass) ends up
|
* doesn't matter which vacuum heap pass (initial pass or final pass) ends
|
||||||
* setting the page all-frozen, as long as the ongoing VACUUM does it.
|
* up setting the page all-frozen, as long as the ongoing VACUUM does it.
|
||||||
*
|
*
|
||||||
* Now that freezing has been finalized, unset all_visible if there are
|
* Now that freezing has been finalized, unset all_visible and all_frozen
|
||||||
* any LP_DEAD items on the page. It needs to reflect the present state
|
* if there are any LP_DEAD items on the page. It needs to reflect the
|
||||||
* of the page, as expected by our caller.
|
* present state of the page, as expected by our caller.
|
||||||
*/
|
*/
|
||||||
if (prstate.all_visible && prstate.lpdead_items == 0)
|
if (prstate.all_visible && prstate.lpdead_items == 0)
|
||||||
{
|
{
|
||||||
@@ -1289,8 +1289,9 @@ heap_prune_record_dead(PruneState *prstate, OffsetNumber offnum,
|
|||||||
prstate->ndead++;
|
prstate->ndead++;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deliberately delay unsetting all_visible until later during pruning.
|
* Deliberately delay unsetting all_visible and all_frozen until later
|
||||||
* Removable dead tuples shouldn't preclude freezing the page.
|
* during pruning. Removable dead tuples shouldn't preclude freezing the
|
||||||
|
* page.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Record the dead offset for vacuum */
|
/* Record the dead offset for vacuum */
|
||||||
@@ -1418,6 +1419,7 @@ heap_prune_record_unchanged_lp_normal(Page page, PruneState *prstate, OffsetNumb
|
|||||||
if (!HeapTupleHeaderXminCommitted(htup))
|
if (!HeapTupleHeaderXminCommitted(htup))
|
||||||
{
|
{
|
||||||
prstate->all_visible = false;
|
prstate->all_visible = false;
|
||||||
|
prstate->all_frozen = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1432,14 +1434,15 @@ heap_prune_record_unchanged_lp_normal(Page page, PruneState *prstate, OffsetNumb
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* For now always use prstate->cutoffs for this test, because
|
* For now always use prstate->cutoffs for this test, because
|
||||||
* we only update 'all_visible' when freezing is requested. We
|
* we only update 'all_visible' and 'all_frozen' when freezing
|
||||||
* could use GlobalVisTestIsRemovableXid instead, if a
|
* is requested. We could use GlobalVisTestIsRemovableXid
|
||||||
* non-freezing caller wanted to set the VM bit.
|
* instead, if a non-freezing caller wanted to set the VM bit.
|
||||||
*/
|
*/
|
||||||
Assert(prstate->cutoffs);
|
Assert(prstate->cutoffs);
|
||||||
if (!TransactionIdPrecedes(xmin, prstate->cutoffs->OldestXmin))
|
if (!TransactionIdPrecedes(xmin, prstate->cutoffs->OldestXmin))
|
||||||
{
|
{
|
||||||
prstate->all_visible = false;
|
prstate->all_visible = false;
|
||||||
|
prstate->all_frozen = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1453,6 +1456,7 @@ heap_prune_record_unchanged_lp_normal(Page page, PruneState *prstate, OffsetNumb
|
|||||||
case HEAPTUPLE_RECENTLY_DEAD:
|
case HEAPTUPLE_RECENTLY_DEAD:
|
||||||
prstate->recently_dead_tuples++;
|
prstate->recently_dead_tuples++;
|
||||||
prstate->all_visible = false;
|
prstate->all_visible = false;
|
||||||
|
prstate->all_frozen = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This tuple will soon become DEAD. Update the hint field so
|
* This tuple will soon become DEAD. Update the hint field so
|
||||||
@@ -1472,6 +1476,7 @@ heap_prune_record_unchanged_lp_normal(Page page, PruneState *prstate, OffsetNumb
|
|||||||
* does, so be consistent.
|
* does, so be consistent.
|
||||||
*/
|
*/
|
||||||
prstate->all_visible = false;
|
prstate->all_visible = false;
|
||||||
|
prstate->all_frozen = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we wanted to optimize for aborts, we might consider marking
|
* If we wanted to optimize for aborts, we might consider marking
|
||||||
@@ -1490,6 +1495,7 @@ heap_prune_record_unchanged_lp_normal(Page page, PruneState *prstate, OffsetNumb
|
|||||||
*/
|
*/
|
||||||
prstate->live_tuples++;
|
prstate->live_tuples++;
|
||||||
prstate->all_visible = false;
|
prstate->all_visible = false;
|
||||||
|
prstate->all_frozen = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This tuple may soon become DEAD. Update the hint field so that
|
* This tuple may soon become DEAD. Update the hint field so that
|
||||||
@@ -1554,10 +1560,10 @@ heap_prune_record_unchanged_lp_dead(Page page, PruneState *prstate, OffsetNumber
|
|||||||
* hastup/nonempty_pages as provisional no matter how LP_DEAD items are
|
* hastup/nonempty_pages as provisional no matter how LP_DEAD items are
|
||||||
* handled (handled here, or handled later on).
|
* handled (handled here, or handled later on).
|
||||||
*
|
*
|
||||||
* Similarly, don't unset all_visible until later, at the end of
|
* Similarly, don't unset all_visible and all_frozen until later, at the
|
||||||
* heap_page_prune_and_freeze(). This will allow us to attempt to freeze
|
* end of heap_page_prune_and_freeze(). This will allow us to attempt to
|
||||||
* the page after pruning. As long as we unset it before updating the
|
* freeze the page after pruning. As long as we unset it before updating
|
||||||
* visibility map, this will be correct.
|
* the visibility map, this will be correct.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Record the dead offset for vacuum */
|
/* Record the dead offset for vacuum */
|
||||||
|
|||||||
@@ -2017,7 +2017,6 @@ lazy_scan_prune(LVRelState *vacrel,
|
|||||||
* agreement with heap_page_is_all_visible() using an assertion.
|
* agreement with heap_page_is_all_visible() using an assertion.
|
||||||
*/
|
*/
|
||||||
#ifdef USE_ASSERT_CHECKING
|
#ifdef USE_ASSERT_CHECKING
|
||||||
/* Note that all_frozen value does not matter when !all_visible */
|
|
||||||
if (presult.all_visible)
|
if (presult.all_visible)
|
||||||
{
|
{
|
||||||
TransactionId debug_cutoff;
|
TransactionId debug_cutoff;
|
||||||
@@ -2071,6 +2070,7 @@ lazy_scan_prune(LVRelState *vacrel,
|
|||||||
*has_lpdead_items = (presult.lpdead_items > 0);
|
*has_lpdead_items = (presult.lpdead_items > 0);
|
||||||
|
|
||||||
Assert(!presult.all_visible || !(*has_lpdead_items));
|
Assert(!presult.all_visible || !(*has_lpdead_items));
|
||||||
|
Assert(!presult.all_frozen || presult.all_visible);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle setting visibility map bit based on information from the VM (as
|
* Handle setting visibility map bit based on information from the VM (as
|
||||||
@@ -2176,11 +2176,10 @@ lazy_scan_prune(LVRelState *vacrel,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the all-visible page is all-frozen but not marked as such yet, mark
|
* If the all-visible page is all-frozen but not marked as such yet, mark
|
||||||
* it as all-frozen. Note that all_frozen is only valid if all_visible is
|
* it as all-frozen.
|
||||||
* true, so we must check both all_visible and all_frozen.
|
|
||||||
*/
|
*/
|
||||||
else if (all_visible_according_to_vm && presult.all_visible &&
|
else if (all_visible_according_to_vm && presult.all_frozen &&
|
||||||
presult.all_frozen && !VM_ALL_FROZEN(vacrel->rel, blkno, &vmbuffer))
|
!VM_ALL_FROZEN(vacrel->rel, blkno, &vmbuffer))
|
||||||
{
|
{
|
||||||
uint8 old_vmbits;
|
uint8 old_vmbits;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user