mirror of
https://github.com/postgres/postgres.git
synced 2025-08-30 06:01:21 +03:00
Separate multixact freezing parameters from xid's
Previously we were piggybacking on transaction ID parameters to freeze multixacts; but since there isn't necessarily any relationship between rates of Xid and multixact consumption, this turns out not to be a good idea. Therefore, we now have multixact-specific freezing parameters: vacuum_multixact_freeze_min_age: when to remove multis as we come across them in vacuum (default to 5 million, i.e. early in comparison to Xid's default of 50 million) vacuum_multixact_freeze_table_age: when to force whole-table scans instead of scanning only the pages marked as not all visible in visibility map (default to 150 million, same as for Xids). Whichever of both which reaches the 150 million mark earlier will cause a whole-table scan. autovacuum_multixact_freeze_max_age: when for cause emergency, uninterruptible whole-table scans (default to 400 million, double as that for Xids). This means there shouldn't be more frequent emergency vacuuming than previously, unless multixacts are being used very rapidly. Backpatch to 9.3 where multixacts were made to persist enough to require freezing. To avoid an ABI break in 9.3, VacuumStmt has a couple of fields in an unnatural place, and StdRdOptions is split in two so that the newly added fields can go at the end. Patch by me, reviewed by Robert Haas, with additional input from Andres Freund and Tom Lane.
This commit is contained in:
@@ -64,9 +64,13 @@ typedef struct
|
||||
|
||||
|
||||
static void rebuild_relation(Relation OldHeap, Oid indexOid,
|
||||
int freeze_min_age, int freeze_table_age, bool verbose);
|
||||
int freeze_min_age, int freeze_table_age,
|
||||
int multixact_freeze_min_age, int multixact_freeze_table_age,
|
||||
bool verbose);
|
||||
static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
|
||||
int freeze_min_age, int freeze_table_age, bool verbose,
|
||||
int freeze_min_age, int freeze_table_age,
|
||||
int multixact_freeze_min_age, int multixact_freeze_table_age,
|
||||
bool verbose,
|
||||
bool *pSwapToastByContent, TransactionId *pFreezeXid,
|
||||
MultiXactId *pCutoffMulti);
|
||||
static List *get_tables_to_cluster(MemoryContext cluster_context);
|
||||
@@ -179,7 +183,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
|
||||
* Do the job. We use a -1 freeze_min_age to avoid having CLUSTER
|
||||
* freeze tuples earlier than a plain VACUUM would.
|
||||
*/
|
||||
cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1);
|
||||
cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1, -1, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -230,7 +234,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
|
||||
PushActiveSnapshot(GetTransactionSnapshot());
|
||||
/* Do the job. As above, use a -1 freeze_min_age. */
|
||||
cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose,
|
||||
-1, -1);
|
||||
-1, -1, -1, -1);
|
||||
PopActiveSnapshot();
|
||||
CommitTransactionCommand();
|
||||
}
|
||||
@@ -262,7 +266,8 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
|
||||
*/
|
||||
void
|
||||
cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
|
||||
int freeze_min_age, int freeze_table_age)
|
||||
int freeze_min_age, int freeze_table_age,
|
||||
int multixact_freeze_min_age, int multixact_freeze_table_age)
|
||||
{
|
||||
Relation OldHeap;
|
||||
|
||||
@@ -407,6 +412,7 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
|
||||
|
||||
/* rebuild_relation does all the dirty work */
|
||||
rebuild_relation(OldHeap, indexOid, freeze_min_age, freeze_table_age,
|
||||
multixact_freeze_min_age, multixact_freeze_table_age,
|
||||
verbose);
|
||||
|
||||
/* NB: rebuild_relation does heap_close() on OldHeap */
|
||||
@@ -566,7 +572,9 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
|
||||
*/
|
||||
static void
|
||||
rebuild_relation(Relation OldHeap, Oid indexOid,
|
||||
int freeze_min_age, int freeze_table_age, bool verbose)
|
||||
int freeze_min_age, int freeze_table_age,
|
||||
int multixact_freeze_min_age, int multixact_freeze_table_age,
|
||||
bool verbose)
|
||||
{
|
||||
Oid tableOid = RelationGetRelid(OldHeap);
|
||||
Oid tableSpace = OldHeap->rd_rel->reltablespace;
|
||||
@@ -591,7 +599,9 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
|
||||
|
||||
/* Copy the heap data into the new table in the desired order */
|
||||
copy_heap_data(OIDNewHeap, tableOid, indexOid,
|
||||
freeze_min_age, freeze_table_age, verbose,
|
||||
freeze_min_age, freeze_table_age,
|
||||
multixact_freeze_min_age, multixact_freeze_table_age,
|
||||
verbose,
|
||||
&swap_toast_by_content, &frozenXid, &cutoffMulti);
|
||||
|
||||
/*
|
||||
@@ -733,7 +743,9 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
|
||||
*/
|
||||
static void
|
||||
copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
|
||||
int freeze_min_age, int freeze_table_age, bool verbose,
|
||||
int freeze_min_age, int freeze_table_age,
|
||||
int multixact_freeze_min_age, int multixact_freeze_table_age,
|
||||
bool verbose,
|
||||
bool *pSwapToastByContent, TransactionId *pFreezeXid,
|
||||
MultiXactId *pCutoffMulti)
|
||||
{
|
||||
@@ -849,6 +861,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
|
||||
* compute xids used to freeze and weed out dead tuples.
|
||||
*/
|
||||
vacuum_set_xid_limits(freeze_min_age, freeze_table_age,
|
||||
multixact_freeze_min_age, multixact_freeze_table_age,
|
||||
OldHeap->rd_rel->relisshared,
|
||||
&OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
|
||||
NULL);
|
||||
|
@@ -55,6 +55,8 @@
|
||||
*/
|
||||
int vacuum_freeze_min_age;
|
||||
int vacuum_freeze_table_age;
|
||||
int vacuum_multixact_freeze_min_age;
|
||||
int vacuum_multixact_freeze_table_age;
|
||||
|
||||
|
||||
/* A few variables that don't seem worth passing around as parameters */
|
||||
@@ -398,6 +400,8 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
|
||||
void
|
||||
vacuum_set_xid_limits(int freeze_min_age,
|
||||
int freeze_table_age,
|
||||
int multixact_freeze_min_age,
|
||||
int multixact_freeze_table_age,
|
||||
bool sharedRel,
|
||||
TransactionId *oldestXmin,
|
||||
TransactionId *freezeLimit,
|
||||
@@ -406,9 +410,11 @@ vacuum_set_xid_limits(int freeze_min_age,
|
||||
MultiXactId *mxactFullScanLimit)
|
||||
{
|
||||
int freezemin;
|
||||
int mxid_freezemin;
|
||||
TransactionId limit;
|
||||
TransactionId safeLimit;
|
||||
MultiXactId mxactLimit;
|
||||
MultiXactId mxactLimit;
|
||||
MultiXactId safeMxactLimit;
|
||||
|
||||
/*
|
||||
* We can always ignore processes running lazy vacuum. This is because we
|
||||
@@ -462,13 +468,36 @@ vacuum_set_xid_limits(int freeze_min_age,
|
||||
*freezeLimit = limit;
|
||||
|
||||
/*
|
||||
* simplistic MultiXactId removal limit: use the same policy as for
|
||||
* freezing Xids (except we use the oldest known mxact instead of the
|
||||
* current next value).
|
||||
* Determine the minimum multixact freeze age to use: as specified by
|
||||
* caller, or vacuum_multixact_freeze_min_age, but in any case not more
|
||||
* than half autovacuum_multixact_freeze_max_age, so that autovacuums to
|
||||
* prevent MultiXact wraparound won't occur too frequently.
|
||||
*/
|
||||
mxactLimit = GetOldestMultiXactId() - freezemin;
|
||||
mxid_freezemin = multixact_freeze_min_age;
|
||||
if (mxid_freezemin < 0)
|
||||
mxid_freezemin = vacuum_multixact_freeze_min_age;
|
||||
mxid_freezemin = Min(mxid_freezemin,
|
||||
autovacuum_multixact_freeze_max_age / 2);
|
||||
Assert(mxid_freezemin >= 0);
|
||||
|
||||
/* compute the cutoff multi, being careful to generate a valid value */
|
||||
mxactLimit = GetOldestMultiXactId() - mxid_freezemin;
|
||||
if (mxactLimit < FirstMultiXactId)
|
||||
mxactLimit = FirstMultiXactId;
|
||||
|
||||
safeMxactLimit =
|
||||
ReadNextMultiXactId() - autovacuum_multixact_freeze_max_age;
|
||||
if (safeMxactLimit < FirstMultiXactId)
|
||||
safeMxactLimit = FirstMultiXactId;
|
||||
|
||||
if (MultiXactIdPrecedes(mxactLimit, safeMxactLimit))
|
||||
{
|
||||
ereport(WARNING,
|
||||
(errmsg("oldest multixact is far in the past"),
|
||||
errhint("Close open transactions with multixacts soon to avoid wraparound problems.")));
|
||||
mxactLimit = safeMxactLimit;
|
||||
}
|
||||
|
||||
*multiXactCutoff = mxactLimit;
|
||||
|
||||
if (xidFullScanLimit != NULL)
|
||||
@@ -501,9 +530,23 @@ vacuum_set_xid_limits(int freeze_min_age,
|
||||
*xidFullScanLimit = limit;
|
||||
|
||||
/*
|
||||
* Compute MultiXactId limit to cause a full-table vacuum, being
|
||||
* careful not to generate an invalid multi. We just copy the logic
|
||||
* (and limits) from plain XIDs here.
|
||||
* Similar to the above, determine the table freeze age to use for
|
||||
* multixacts: as specified by the caller, or
|
||||
* vacuum_multixact_freeze_table_age, but in any case not more than
|
||||
* autovacuum_multixact_freeze_table_age * 0.95, so that if you have
|
||||
* e.g. nightly VACUUM schedule, the nightly VACUUM gets a chance to
|
||||
* freeze multixacts before anti-wraparound autovacuum is launched.
|
||||
*/
|
||||
freezetable = multixact_freeze_table_age;
|
||||
if (freezetable < 0)
|
||||
freezetable = vacuum_multixact_freeze_table_age;
|
||||
freezetable = Min(freezetable,
|
||||
autovacuum_multixact_freeze_max_age * 0.95);
|
||||
Assert(freezetable >= 0);
|
||||
|
||||
/*
|
||||
* Compute MultiXact limit causing a full-table vacuum, being careful
|
||||
* to generate a valid MultiXact value.
|
||||
*/
|
||||
mxactLimit = ReadNextMultiXactId() - freezetable;
|
||||
if (mxactLimit < FirstMultiXactId)
|
||||
@@ -511,6 +554,10 @@ vacuum_set_xid_limits(int freeze_min_age,
|
||||
|
||||
*mxactFullScanLimit = mxactLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(mxactFullScanLimit == NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1150,7 +1197,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
|
||||
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
|
||||
cluster_rel(relid, InvalidOid, false,
|
||||
(vacstmt->options & VACOPT_VERBOSE) != 0,
|
||||
vacstmt->freeze_min_age, vacstmt->freeze_table_age);
|
||||
vacstmt->freeze_min_age, vacstmt->freeze_table_age,
|
||||
vacstmt->multixact_freeze_min_age,
|
||||
vacstmt->multixact_freeze_table_age);
|
||||
}
|
||||
else
|
||||
lazy_vacuum_rel(onerel, vacstmt, vac_strategy);
|
||||
|
@@ -203,6 +203,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
|
||||
vac_strategy = bstrategy;
|
||||
|
||||
vacuum_set_xid_limits(vacstmt->freeze_min_age, vacstmt->freeze_table_age,
|
||||
vacstmt->multixact_freeze_min_age,
|
||||
vacstmt->multixact_freeze_table_age,
|
||||
onerel->rd_rel->relisshared,
|
||||
&OldestXmin, &FreezeLimit, &xidFullScanLimit,
|
||||
&MultiXactCutoff, &mxactFullScanLimit);
|
||||
@@ -210,8 +212,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
|
||||
/*
|
||||
* We request a full scan if either the table's frozen Xid is now older
|
||||
* than or equal to the requested Xid full-table scan limit; or if the
|
||||
* table's minimum MultiXactId is older than or equal to the requested mxid
|
||||
* full-table scan limit.
|
||||
* table's minimum MultiXactId is older than or equal to the requested
|
||||
* mxid full-table scan limit.
|
||||
*/
|
||||
scan_all = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
|
||||
xidFullScanLimit);
|
||||
|
Reference in New Issue
Block a user