aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/heap/vacuumlazy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/heap/vacuumlazy.c')
-rw-r--r--src/backend/access/heap/vacuumlazy.c156
1 files changed, 93 insertions, 63 deletions
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 06b523a01fa..826982f70b7 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -144,7 +144,7 @@ typedef struct LVRelState
Relation *indrels;
int nindexes;
- /* Aggressive VACUUM (scan all unfrozen pages)? */
+ /* Aggressive VACUUM? (must set relfrozenxid >= FreezeLimit) */
bool aggressive;
/* Use visibility map to skip? (disabled by DISABLE_PAGE_SKIPPING) */
bool skipwithvm;
@@ -173,8 +173,9 @@ typedef struct LVRelState
/* VACUUM operation's target cutoffs for freezing XIDs and MultiXactIds */
TransactionId FreezeLimit;
MultiXactId MultiXactCutoff;
- /* Are FreezeLimit/MultiXactCutoff still valid? */
- bool freeze_cutoffs_valid;
+ /* Tracks oldest extant XID/MXID for setting relfrozenxid/relminmxid */
+ TransactionId NewRelfrozenXid;
+ MultiXactId NewRelminMxid;
/* Error reporting state */
char *relnamespace;
@@ -313,7 +314,8 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
minmulti_updated;
TransactionId OldestXmin,
FreezeLimit;
- MultiXactId MultiXactCutoff;
+ MultiXactId OldestMxact,
+ MultiXactCutoff;
BlockNumber orig_rel_pages,
new_rel_pages,
new_rel_allvisible;
@@ -345,20 +347,17 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
/*
* Get OldestXmin cutoff, which is used to determine which deleted tuples
* are considered DEAD, not just RECENTLY_DEAD. Also get related cutoffs
- * used to determine which XIDs/MultiXactIds will be frozen.
- *
- * If this is an aggressive VACUUM, then we're strictly required to freeze
- * any and all XIDs from before FreezeLimit, so that we will be able to
- * safely advance relfrozenxid up to FreezeLimit below (we must be able to
- * advance relminmxid up to MultiXactCutoff, too).
+ * used to determine which XIDs/MultiXactIds will be frozen. If this is
+ * an aggressive VACUUM then lazy_scan_heap cannot leave behind unfrozen
+ * XIDs < FreezeLimit (all MXIDs < MultiXactCutoff also need to go away).
*/
aggressive = vacuum_set_xid_limits(rel,
params->freeze_min_age,
params->freeze_table_age,
params->multixact_freeze_min_age,
params->multixact_freeze_table_age,
- &OldestXmin, &FreezeLimit,
- &MultiXactCutoff);
+ &OldestXmin, &OldestMxact,
+ &FreezeLimit, &MultiXactCutoff);
skipwithvm = true;
if (params->options & VACOPT_DISABLE_PAGE_SKIPPING)
@@ -505,10 +504,11 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
vacrel->vistest = GlobalVisTestFor(rel);
/* FreezeLimit controls XID freezing (always <= OldestXmin) */
vacrel->FreezeLimit = FreezeLimit;
- /* MultiXactCutoff controls MXID freezing */
+ /* MultiXactCutoff controls MXID freezing (always <= OldestMxact) */
vacrel->MultiXactCutoff = MultiXactCutoff;
- /* Track if cutoffs became invalid (possible in !aggressive case only) */
- vacrel->freeze_cutoffs_valid = true;
+ /* Initialize state used to track oldest extant XID/XMID */
+ vacrel->NewRelfrozenXid = OldestXmin;
+ vacrel->NewRelminMxid = OldestMxact;
/*
* Call lazy_scan_heap to perform all required heap pruning, index
@@ -542,13 +542,33 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
/*
* Prepare to update rel's pg_class entry.
*
- * In principle new_live_tuples could be -1 indicating that we (still)
- * don't know the tuple count. In practice that probably can't happen,
- * since we'd surely have scanned some pages if the table is new and
- * nonempty.
- *
+ * Aggressive VACUUMs must always be able to advance relfrozenxid to a
+ * value >= FreezeLimit, and relminmxid to a value >= MultiXactCutoff.
+ * Non-aggressive VACUUMs may advance them by any amount, or not at all.
+ */
+ Assert(vacrel->NewRelfrozenXid == OldestXmin ||
+ TransactionIdPrecedesOrEquals(aggressive ? FreezeLimit :
+ vacrel->relfrozenxid,
+ vacrel->NewRelfrozenXid));
+ Assert(vacrel->NewRelminMxid == OldestMxact ||
+ MultiXactIdPrecedesOrEquals(aggressive ? MultiXactCutoff :
+ vacrel->relminmxid,
+ vacrel->NewRelminMxid));
+ if (vacrel->scanned_pages + vacrel->frozenskipped_pages < orig_rel_pages)
+ {
+ /*
+ * Must keep original relfrozenxid in a non-aggressive VACUUM that
+ * had to skip an all-visible page. The state that tracks new
+ * values will have missed unfrozen XIDs from the pages we skipped.
+ */
+ Assert(!aggressive);
+ vacrel->NewRelfrozenXid = InvalidTransactionId;
+ vacrel->NewRelminMxid = InvalidMultiXactId;
+ }
+
+ /*
* For safety, clamp relallvisible to be not more than what we're setting
- * relpages to.
+ * pg_class.relpages to
*/
new_rel_pages = vacrel->rel_pages; /* After possible rel truncation */
visibilitymap_count(rel, &new_rel_allvisible, NULL);
@@ -558,33 +578,14 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
/*
* Now actually update rel's pg_class entry.
*
- * Aggressive VACUUM must reliably advance relfrozenxid (and relminmxid).
- * We are able to advance relfrozenxid in a non-aggressive VACUUM too,
- * provided we didn't skip any all-visible (not all-frozen) pages using
- * the visibility map, and assuming that we didn't fail to get a cleanup
- * lock that made it unsafe with respect to FreezeLimit (or perhaps our
- * MultiXactCutoff) established for VACUUM operation.
+ * In principle new_live_tuples could be -1 indicating that we (still)
+ * don't know the tuple count. In practice that can't happen, since we
+ * scan every page that isn't skipped using the visibility map.
*/
- if (vacrel->scanned_pages + vacrel->frozenskipped_pages < orig_rel_pages ||
- !vacrel->freeze_cutoffs_valid)
- {
- /* Cannot advance relfrozenxid/relminmxid */
- Assert(!aggressive);
- frozenxid_updated = minmulti_updated = false;
- vac_update_relstats(rel, new_rel_pages, vacrel->new_live_tuples,
- new_rel_allvisible, vacrel->nindexes > 0,
- InvalidTransactionId, InvalidMultiXactId,
- NULL, NULL, false);
- }
- else
- {
- Assert(vacrel->scanned_pages + vacrel->frozenskipped_pages ==
- orig_rel_pages);
- vac_update_relstats(rel, new_rel_pages, vacrel->new_live_tuples,
- new_rel_allvisible, vacrel->nindexes > 0,
- FreezeLimit, MultiXactCutoff,
- &frozenxid_updated, &minmulti_updated, false);
- }
+ vac_update_relstats(rel, new_rel_pages, vacrel->new_live_tuples,
+ new_rel_allvisible, vacrel->nindexes > 0,
+ vacrel->NewRelfrozenXid, vacrel->NewRelminMxid,
+ &frozenxid_updated, &minmulti_updated, false);
/*
* Report results to the stats collector, too.
@@ -692,17 +693,17 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
OldestXmin, diff);
if (frozenxid_updated)
{
- diff = (int32) (FreezeLimit - vacrel->relfrozenxid);
+ diff = (int32) (vacrel->NewRelfrozenXid - vacrel->relfrozenxid);
appendStringInfo(&buf,
_("new relfrozenxid: %u, which is %d xids ahead of previous value\n"),
- FreezeLimit, diff);
+ vacrel->NewRelfrozenXid, diff);
}
if (minmulti_updated)
{
- diff = (int32) (MultiXactCutoff - vacrel->relminmxid);
+ diff = (int32) (vacrel->NewRelminMxid - vacrel->relminmxid);
appendStringInfo(&buf,
_("new relminmxid: %u, which is %d mxids ahead of previous value\n"),
- MultiXactCutoff, diff);
+ vacrel->NewRelminMxid, diff);
}
if (orig_rel_pages > 0)
{
@@ -1582,6 +1583,8 @@ lazy_scan_prune(LVRelState *vacrel,
recently_dead_tuples;
int nnewlpdead;
int nfrozen;
+ TransactionId NewRelfrozenXid;
+ MultiXactId NewRelminMxid;
OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
xl_heap_freeze_tuple frozen[MaxHeapTuplesPerPage];
@@ -1591,7 +1594,9 @@ lazy_scan_prune(LVRelState *vacrel,
retry:
- /* Initialize (or reset) page-level counters */
+ /* Initialize (or reset) page-level state */
+ NewRelfrozenXid = vacrel->NewRelfrozenXid;
+ NewRelminMxid = vacrel->NewRelminMxid;
tuples_deleted = 0;
lpdead_items = 0;
live_tuples = 0;
@@ -1798,8 +1803,8 @@ retry:
vacrel->relminmxid,
vacrel->FreezeLimit,
vacrel->MultiXactCutoff,
- &frozen[nfrozen],
- &tuple_totally_frozen))
+ &frozen[nfrozen], &tuple_totally_frozen,
+ &NewRelfrozenXid, &NewRelminMxid))
{
/* Will execute freeze below */
frozen[nfrozen++].offset = offnum;
@@ -1813,13 +1818,16 @@ retry:
prunestate->all_frozen = false;
}
+ vacrel->offnum = InvalidOffsetNumber;
+
/*
* We have now divided every item on the page into either an LP_DEAD item
* that will need to be vacuumed in indexes later, or a LP_NORMAL tuple
* that remains and needs to be considered for freezing now (LP_UNUSED and
* LP_REDIRECT items also remain, but are of no further interest to us).
*/
- vacrel->offnum = InvalidOffsetNumber;
+ vacrel->NewRelfrozenXid = NewRelfrozenXid;
+ vacrel->NewRelminMxid = NewRelminMxid;
/*
* Consider the need to freeze any items with tuple storage from the page
@@ -1969,6 +1977,8 @@ lazy_scan_noprune(LVRelState *vacrel,
recently_dead_tuples,
missed_dead_tuples;
HeapTupleHeader tupleheader;
+ TransactionId NewRelfrozenXid = vacrel->NewRelfrozenXid;
+ MultiXactId NewRelminMxid = vacrel->NewRelminMxid;
OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
Assert(BufferGetBlockNumber(buf) == blkno);
@@ -2013,22 +2023,37 @@ lazy_scan_noprune(LVRelState *vacrel,
*hastup = true; /* page prevents rel truncation */
tupleheader = (HeapTupleHeader) PageGetItem(page, itemid);
- if (heap_tuple_needs_freeze(tupleheader,
+ if (heap_tuple_would_freeze(tupleheader,
vacrel->FreezeLimit,
- vacrel->MultiXactCutoff))
+ vacrel->MultiXactCutoff,
+ &NewRelfrozenXid, &NewRelminMxid))
{
+ /* Tuple with XID < FreezeLimit (or MXID < MultiXactCutoff) */
if (vacrel->aggressive)
{
- /* Going to have to get cleanup lock for lazy_scan_prune */
+ /*
+ * Aggressive VACUUMs must always be able to advance rel's
+ * relfrozenxid to a value >= FreezeLimit (and be able to
+ * advance rel's relminmxid to a value >= MultiXactCutoff).
+ * The ongoing aggressive VACUUM won't be able to do that
+ * unless it can freeze an XID (or XMID) from this tuple now.
+ *
+ * The only safe option is to have caller perform processing
+ * of this page using lazy_scan_prune. Caller might have to
+ * wait a while for a cleanup lock, but it can't be helped.
+ */
vacrel->offnum = InvalidOffsetNumber;
return false;
}
/*
- * Current non-aggressive VACUUM operation definitely won't be
- * able to advance relfrozenxid or relminmxid
+ * Non-aggressive VACUUMs are under no obligation to advance
+ * relfrozenxid (even by one XID). We can be much laxer here.
+ *
+ * Currently we always just accept an older final relfrozenxid
+ * and/or relminmxid value. We never make caller wait or work a
+ * little harder, even when it likely makes sense to do so.
*/
- vacrel->freeze_cutoffs_valid = false;
}
ItemPointerSet(&(tuple.t_self), blkno, offnum);
@@ -2078,9 +2103,14 @@ lazy_scan_noprune(LVRelState *vacrel,
vacrel->offnum = InvalidOffsetNumber;
/*
- * Now save details of the LP_DEAD items from the page in vacrel (though
- * only when VACUUM uses two-pass strategy)
+ * By here we know for sure that caller can put off freezing and pruning
+ * this particular page until the next VACUUM. Remember its details now.
+ * (lazy_scan_prune expects a clean slate, so we have to do this last.)
*/
+ vacrel->NewRelfrozenXid = NewRelfrozenXid;
+ vacrel->NewRelminMxid = NewRelminMxid;
+
+ /* Save any LP_DEAD items found on the page in dead_items array */
if (vacrel->nindexes == 0)
{
/* Using one-pass strategy (since table has no indexes) */