aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/vacuum.c
diff options
context:
space:
mode:
authorPeter Geoghegan <pg@bowt.ie>2022-04-03 09:57:21 -0700
committerPeter Geoghegan <pg@bowt.ie>2022-04-03 09:57:21 -0700
commit0b018fabaaba77e39539ce7eb71e34a90ceb0825 (patch)
treea4b8ae23a9e20cf23460d3ba811c67a039f605e4 /src/backend/commands/vacuum.c
parent05023a237c059c840380817abf079a9282a227b7 (diff)
downloadpostgresql-0b018fabaaba77e39539ce7eb71e34a90ceb0825.tar.gz
postgresql-0b018fabaaba77e39539ce7eb71e34a90ceb0825.zip
Set relfrozenxid to oldest extant XID seen by VACUUM.
When VACUUM set relfrozenxid before now, it set it to whatever value was used to determine which tuples to freeze -- the FreezeLimit cutoff. This approach was very naive. The relfrozenxid invariant only requires that new relfrozenxid values be <= the oldest extant XID remaining in the table (at the point that the VACUUM operation ends), which in general might be much more recent than FreezeLimit. VACUUM now carefully tracks the oldest remaining XID/MultiXactId as it goes (the oldest remaining values _after_ lazy_scan_prune processing). The final values are set as the table's new relfrozenxid and new relminmxid in pg_class at the end of each VACUUM. The oldest XID might come from a tuple's xmin, xmax, or xvac fields. It might even come from one of the table's remaining MultiXacts. Final relfrozenxid values must still be >= FreezeLimit in an aggressive VACUUM (FreezeLimit still acts as a lower bound on the final value that aggressive VACUUM can set relfrozenxid to). Since standard VACUUMs still make no guarantees about advancing relfrozenxid, they might as well set relfrozenxid to a value from well before FreezeLimit when the opportunity presents itself. In general standard VACUUMs may now set relfrozenxid to any value > the original relfrozenxid and <= OldestXmin. Credit for the general idea of using the oldest extant XID to set pg_class.relfrozenxid at the end of VACUUM goes to Andres Freund. Author: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Andres Freund <andres@anarazel.de> Reviewed-By: Robert Haas <robertmhaas@gmail.com> Discussion: https://postgr.es/m/CAH2-WzkymFbz6D_vL+jmqSn_5q1wsFvFrE+37yLgL_Rkfd6Gzg@mail.gmail.com
Diffstat (limited to 'src/backend/commands/vacuum.c')
-rw-r--r--src/backend/commands/vacuum.c39
1 files changed, 23 insertions, 16 deletions
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 50a4a612e58..deec4887bec 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -945,14 +945,22 @@ get_all_vacuum_rels(int options)
* The output parameters are:
* - oldestXmin is the Xid below which tuples deleted by any xact (that
* committed) should be considered DEAD, not just RECENTLY_DEAD.
- * - freezeLimit is the Xid below which all Xids are replaced by
- * FrozenTransactionId during vacuum.
- * - multiXactCutoff is the value below which all MultiXactIds are removed
- * from Xmax.
+ * - oldestMxact is the Mxid below which MultiXacts are definitely not
+ * seen as visible by any running transaction.
+ * - freezeLimit is the Xid below which all Xids are definitely replaced by
+ * FrozenTransactionId during aggressive vacuums.
+ * - multiXactCutoff is the value below which all MultiXactIds are definitely
+ * removed from Xmax during aggressive vacuums.
*
* Return value indicates if vacuumlazy.c caller should make its VACUUM
* operation aggressive. An aggressive VACUUM must advance relfrozenxid up to
- * FreezeLimit, and relminmxid up to multiXactCutoff.
+ * FreezeLimit (at a minimum), and relminmxid up to multiXactCutoff (at a
+ * minimum).
+ *
+ * oldestXmin and oldestMxact are the most recent values that can ever be
+ * passed to vac_update_relstats() as frozenxid and minmulti arguments by our
+ * vacuumlazy.c caller later on. These values should be passed when it turns
+ * out that VACUUM will leave no unfrozen XIDs/XMIDs behind in the table.
*/
bool
vacuum_set_xid_limits(Relation rel,
@@ -961,6 +969,7 @@ vacuum_set_xid_limits(Relation rel,
int multixact_freeze_min_age,
int multixact_freeze_table_age,
TransactionId *oldestXmin,
+ MultiXactId *oldestMxact,
TransactionId *freezeLimit,
MultiXactId *multiXactCutoff)
{
@@ -969,7 +978,6 @@ vacuum_set_xid_limits(Relation rel,
int effective_multixact_freeze_max_age;
TransactionId limit;
TransactionId safeLimit;
- MultiXactId oldestMxact;
MultiXactId mxactLimit;
MultiXactId safeMxactLimit;
int freezetable;
@@ -1065,9 +1073,11 @@ vacuum_set_xid_limits(Relation rel,
effective_multixact_freeze_max_age / 2);
Assert(mxid_freezemin >= 0);
+ /* Remember for caller */
+ *oldestMxact = GetOldestMultiXactId();
+
/* compute the cutoff multi, being careful to generate a valid value */
- oldestMxact = GetOldestMultiXactId();
- mxactLimit = oldestMxact - mxid_freezemin;
+ mxactLimit = *oldestMxact - mxid_freezemin;
if (mxactLimit < FirstMultiXactId)
mxactLimit = FirstMultiXactId;
@@ -1082,8 +1092,8 @@ vacuum_set_xid_limits(Relation rel,
(errmsg("oldest multixact is far in the past"),
errhint("Close open transactions with multixacts soon to avoid wraparound problems.")));
/* Use the safe limit, unless an older mxact is still running */
- if (MultiXactIdPrecedes(oldestMxact, safeMxactLimit))
- mxactLimit = oldestMxact;
+ if (MultiXactIdPrecedes(*oldestMxact, safeMxactLimit))
+ mxactLimit = *oldestMxact;
else
mxactLimit = safeMxactLimit;
}
@@ -1390,12 +1400,9 @@ vac_update_relstats(Relation relation,
* Update relfrozenxid, unless caller passed InvalidTransactionId
* indicating it has no new data.
*
- * Ordinarily, we don't let relfrozenxid go backwards: if things are
- * working correctly, the only way the new frozenxid could be older would
- * be if a previous VACUUM was done with a tighter freeze_min_age, in
- * which case we don't want to forget the work it already did. However,
- * if the stored relfrozenxid is "in the future", then it must be corrupt
- * and it seems best to overwrite it with the cutoff we used this time.
+ * Ordinarily, we don't let relfrozenxid go backwards. However, if the
+ * stored relfrozenxid is "in the future" then it seems best to assume
+ * it's corrupt, and overwrite with the oldest remaining XID in the table.
* This should match vac_update_datfrozenxid() concerning what we consider
* to be "in the future".
*/