index 000a46fabb04ca21d5ebb853348e36f4cc0472b3..e12778b263c6c6b8dd5fa9a5f7b029a02a06e365 100644 (file)
@@ -4892,6 +4892,33 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem>
</varlistentry>
+ <varlistentry id="guc-autovacuum-multixact-freeze-max-age" xreflabel="autovacuum_multixact_freeze_max_age">
+ <term><varname>autovacuum_multixact_freeze_max_age</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>autovacuum_multixact_freeze_max_age</varname> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the maximum age (in multixacts) that a table's
+ <structname>pg_class</>.<structfield>relminmxid</> field can
+ attain before a <command>VACUUM</> operation is forced to
+ prevent multixact ID wraparound within the table.
+ Note that the system will launch autovacuum processes to
+ prevent wraparound even when autovacuum is otherwise disabled.
+ </para>
+
+ <para>
+ Vacuuming multixacts also allows removal of old files from the
+ <filename>pg_multixact/members</> and <filename>pg_multixact/offsets</>
+ subdirectories, which is why the default is a relatively low
+ 400 million multixacts.
+ This parameter can only be set at server start, but the setting
+ can be reduced for individual tables by changing storage parameters.
+ For more information see <xref linkend="vacuum-for-multixact-wraparound">.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-autovacuum-vacuum-cost-delay" xreflabel="autovacuum_vacuum_cost_delay">
<term><varname>autovacuum_vacuum_cost_delay</varname> (<type>integer</type>)</term>
<indexterm>
@@ -5300,7 +5327,7 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
<structname>pg_class</>.<structfield>relfrozenxid</> field has reached
the age specified by this setting. The default is 150 million
transactions. Although users can set this value anywhere from zero to
- one billion, <command>VACUUM</> will silently limit the effective value
+ two billions, <command>VACUUM</> will silently limit the effective value
to 95% of <xref linkend="guc-autovacuum-freeze-max-age">, so that a
periodical manual <command>VACUUM</> has a chance to run before an
anti-wraparound autovacuum is launched for the table. For more
@@ -5331,6 +5358,47 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem>
</varlistentry>
+ <varlistentry id="guc-vacuum-multixact-freeze-table-age" xreflabel="vacuum_multixact_freeze_table_age">
+ <term><varname>vacuum_multixact_freeze_table_age</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>vacuum_multixact_freeze_table_age</> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ <command>VACUUM</> performs a whole-table scan if the table's
+ <structname>pg_class</>.<structfield>relminmxid</> field has reached
+ the age specified by this setting. The default is 150 million multixacts.
+ Although users can set this value anywhere from zero to two billions,
+ <command>VACUUM</> will silently limit the effective value to 95% of
+ <xref linkend="guc-autovacuum-multixact-freeze-max-age">, so that a
+ periodical manual <command>VACUUM</> has a chance to run before an
+ anti-wraparound is launched for the table.
+ For more information see <xref linkend="vacuum-for-multixact-wraparound">.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="guc-vacuum-multixact-freeze-min-age" xreflabel="vacuum_multixact_freeze_min_age">
+ <term><varname>vacuum_multixact_freeze_min_age</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>vacuum_multixact_freeze_min_age</> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies the cutoff age (in multixacts) that <command>VACUUM</>
+ should use to decide whether to replace multixact IDs with a newer
+ transaction ID or multixact ID while scanning a table. The default
+ is 5 million multixacts.
+ Although users can set this value anywhere from zero to one billion,
+ <command>VACUUM</> will silently limit the effective value to half
+ the value of <xref linkend="guc-autovacuum-multixact-freeze-max-age">,
+ so that there is not an unreasonably short time between forced
+ autovacuums.
+ For more information see <xref linkend="vacuum-for-multixact-wraparound">.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-bytea-output" xreflabel="bytea_output">
<term><varname>bytea_output</varname> (<type>enum</type>)</term>
<indexterm>
index 339891e8a0c172d080f91a7d9c262c2d8d35c313..8ff309b78fe487bf9f50a5536ede44b85b10fc5e 100644 (file)
<listitem>
<simpara>To protect against loss of very old data due to
- <firstterm>transaction ID wraparound</>.</simpara>
+ <firstterm>transaction ID wraparound</> or
+ <firstterm>multixact ID wraparound</>.</simpara>
</listitem>
</orderedlist>
<secondary>wraparound</secondary>
</indexterm>
+ <indexterm>
+ <primary>wraparound</primary>
+ <secondary>of transaction IDs</secondary>
+ </indexterm>
+
<para>
<productname>PostgreSQL</productname>'s MVCC transaction semantics
depend on being able to compare transaction ID (<acronym>XID</>)
@@ -597,6 +603,54 @@ HINT: Stop the postmaster and vacuum that database in single-user mode.
page for details about using single-user mode.
</para>
+ <sect3 id="vacuum-for-multixact-wraparound">
+ <title>Multixacts and Wraparound</title>
+
+ <indexterm>
+ <primary>MultiXactId</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>wraparound</primary>
+ <secondary>of multixact IDs</secondary>
+ </indexterm>
+
+ <para>
+ <firstterm>Multixacts</> are used to implement row locking by
+ multiple transactions: since there is limited space in the tuple
+ header to store lock information, that information is stored as a
+ multixact separately in the <filename>pg_multixact</> subdirectory,
+ and only its ID is in the <structfield>xmax</> field
+ in the tuple header.
+ Similar to transaction IDs, multixact IDs are implemented as a
+ 32-bit counter and corresponding storage, all of which requires
+ careful aging management, storage cleanup, and wraparound handling.
+ </para>
+
+ <para>
+ During a <command>VACUUM</> table scan, either partial or of the whole
+ table, any multixact ID older than
+ <xref linkend="guc-vacuum-multixact-freeze-min-age">
+ is replaced by a different value, which can be the zero value, a single
+ transaction ID, or a newer multixact ID. For each table,
+ <structname>pg_class</>.<structfield>relminmxid</> stores the oldest
+ possible value still stored in any tuple of that table. Every time this
+ value is older than
+ <xref linkend="guc-vacuum-multixact-freeze-table-age">, a whole-table
+ scan is forced. Whole-table <command>VACUUM</> scans, regardless of
+ what causes them, enable advancing the value for that table.
+ Eventually, as all tables in all databases are scanned and their
+ oldest multixact values are advanced, on-disk storage for older
+ multixacts can be removed.
+ </para>
+
+ <para>
+ As a safety device, a whole-table vacuum scan will occur for any table
+ whose multixact-age is greater than
+ <xref linkend="guc-autovacuum-multixact-freeze-max-age">.
+ This will occur even if autovacuum is nominally disabled.
+ </para>
+ </sect3>
</sect2>
<sect2 id="autovacuum">
index e0b8a4ecaf60a0595f2e340aa4b9fc1d5a1d2812..7a01c63d5f028815d7dc78b8b9cb4efbd13e515e 100644 (file)
@@ -985,7 +985,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
<para>
Custom <xref linkend="guc-vacuum-freeze-min-age"> parameter. Note that
autovacuum will ignore attempts to set a per-table
- <literal>autovacuum_freeze_min_age</> larger than the half system-wide
+ <literal>autovacuum_freeze_min_age</> larger than half the system-wide
<xref linkend="guc-autovacuum-freeze-max-age"> setting.
</para>
</listitem>
@@ -1014,6 +1014,43 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>autovacuum_multixact_freeze_min_age</literal>, <literal>toast.autovacuum_multixact_freeze_min_age</literal> (<type>integer</type>)</term>
+ <listitem>
+ <para>
+ Custom <xref linkend="guc-vacuum-multixact-freeze-min-age"> parameter.
+ Note that autovacuum will ignore attempts to set a per-table
+ <literal>autovacuum_multixact_freeze_min_age</> larger than half the
+ system-wide <xref linkend="guc-autovacuum-multixact-freeze-max-age">
+ setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>autovacuum_multixact_freeze_max_age</literal>, <literal>toast.autovacuum_multixact_freeze_max_age</literal> (<type>integer</type>)</term>
+ <listitem>
+ <para>
+ Custom <xref linkend="guc-autovacuum-multixact-freeze-max-age"> parameter. Note
+ that autovacuum will ignore attempts to set a per-table
+ <literal>autovacuum_multixact_freeze_max_age</> larger than the
+ system-wide setting (it can only be set smaller). Note that while you
+ can set <literal>autovacuum_multixact_freeze_max_age</> very small,
+ or even zero, this is usually unwise since it will force frequent
+ vacuuming.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>autovacuum_multixact_freeze_table_age</literal>, <literal>toast.autovacuum_multixact_freeze_table_age</literal> (<type>integer</type>)</term>
+ <listitem>
+ <para>
+ Custom <xref linkend="guc-vacuum-multixact-freeze-table-age"> parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect2>
index fa08c45a139c874998cd3337afd8be57a78fba4f..530a1aee7bbe20f1b00a7d185462eb7b6761cb71 100644 (file)
},
-1, 0, 1000000000
},
+ {
+ {
+ "autovacuum_multixact_freeze_min_age",
+ "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
+ },
+ -1, 0, 1000000000
+ },
{
{
"autovacuum_freeze_max_age",
},
-1, 100000000, 2000000000
},
+ {
+ {
+ "autovacuum_multixact_freeze_max_age",
+ "Multixact age at which to autovacuum a table to prevent multixact wraparound",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
+ },
+ -1, 100000000, 2000000000
+ },
{
{
"autovacuum_freeze_table_age",
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
}, -1, 0, 2000000000
},
+ {
+ {
+ "autovacuum_multixact_freeze_table_age",
+ "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
+ }, -1, 0, 2000000000
+ },
+
/* list terminator */
{{NULL}}
};
@@ -1166,6 +1190,12 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_max_age)},
{"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_table_age)},
+ {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
+ offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_min_age)},
+ {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
+ offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_max_age)},
+ {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
+ offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_table_age)},
{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
index 93824653376fd6f785d7f2cdf44e40bce3be2720..d4ad6787a59dddce26ba80da251031dc541e2288 100644 (file)
@@ -2055,11 +2055,13 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
Assert(MultiXactIdIsValid(oldest_datminmxid));
/*
- * The place where we actually get into deep trouble is halfway around
- * from the oldest potentially-existing XID/multi. (This calculation is
- * probably off by one or two counts for Xids, because the special XIDs
- * reduce the size of the loop a little bit. But we throw in plenty of
- * slop below, so it doesn't matter.)
+ * Since multixacts wrap differently from transaction IDs, this logic is
+ * not entirely correct: in some scenarios we could go for longer than 2
+ * billion multixacts without seeing any data loss, and in some others we
+ * could get in trouble before that if the new pg_multixact/members data
+ * stomps on the previous cycle's data. For lack of a better mechanism we
+ * use the same logic as for transaction IDs, that is, start taking action
+ * halfway around the oldest potentially-existing multixact.
*/
multiWrapLimit = oldest_datminmxid + (MaxMultiXactId >> 1);
if (multiWrapLimit < FirstMultiXactId)
@@ -2093,12 +2095,13 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
/*
* We'll start trying to force autovacuums when oldest_datminmxid gets to
- * be more than autovacuum_freeze_max_age mxids old.
+ * be more than autovacuum_multixact_freeze_max_age mxids old.
*
- * It's a bit ugly to just reuse limits for xids that way, but it doesn't
- * seem worth adding separate GUCs for that purpose.
+ * Note: autovacuum_multixact_freeze_max_age is a PGC_POSTMASTER parameter
+ * so that we don't have to worry about dealing with on-the-fly changes in
+ * its value. See SetTransactionIdLimit.
*/
- multiVacLimit = oldest_datminmxid + autovacuum_freeze_max_age;
+ multiVacLimit = oldest_datminmxid + autovacuum_multixact_freeze_max_age;
if (multiVacLimit < FirstMultiXactId)
multiVacLimit += FirstMultiXactId;
index c03fa6895452d479f9c2709eb92e5ad7dc8cd331..51b6b1a30213b2691be61faa2ffde5b6246ae6dd 100644 (file)
@@ -313,7 +313,8 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
* value. It doesn't look practical to update shared state from a GUC
* assign hook (too many processes would try to execute the hook,
* resulting in race conditions as well as crashes of those not connected
- * to shared memory). Perhaps this can be improved someday.
+ * to shared memory). Perhaps this can be improved someday. See also
+ * SetMultiXactIdLimit.
*/
xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age;
if (xidVacLimit < FirstNormalTransactionId)
index 14a5e5a2d41629e0b75e21e05a2f096052246f66..8b18e4acb72b53307397a6e07428b8ba396e63b5 100644 (file)
@@ -850,7 +850,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
* Since we're going to rewrite the whole table anyway, there's no reason
* not to be aggressive about this.
*/
- vacuum_set_xid_limits(0, 0, OldHeap->rd_rel->relisshared,
+ vacuum_set_xid_limits(0, 0, 0, 0, OldHeap->rd_rel->relisshared,
&OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
NULL);
index 3455a0b9ae653e94857ca0b9b95b1dcc248e718f..5ae7763534b35be938ac45960ef775460739bdcb 100644 (file)
*/
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,
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
*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)
*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)
*mxactFullScanLimit = mxactLimit;
}
+ else
+ {
+ Assert(mxactFullScanLimit == NULL);
+ }
}
/*
index 75e5f157eaaa1242d8936f2aec98a515cf3d69b3..d77892ee7f8ce0f1cfd5ca67b5f0d4e0b2b5e37d 100644 (file)
@@ -205,6 +205,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);
@@ -212,8 +214,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);
index f90cb6797de7f819cdbb087128a9d92b25e75340..6a59703025be5a30b0920120621d82cbacb60baa 100644 (file)
COPY_SCALAR_FIELD(options);
COPY_SCALAR_FIELD(freeze_min_age);
COPY_SCALAR_FIELD(freeze_table_age);
+ COPY_SCALAR_FIELD(multixact_freeze_min_age);
+ COPY_SCALAR_FIELD(multixact_freeze_table_age);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(va_cols);
index 9438e7861d8f189a9362ea661684bb6b78f3fac9..0bcbf42bfc4e5fdc64ed03865ba895ba7c2b902f 100644 (file)
@@ -1503,6 +1503,8 @@ _equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
COMPARE_SCALAR_FIELD(options);
COMPARE_SCALAR_FIELD(freeze_min_age);
COMPARE_SCALAR_FIELD(freeze_table_age);
+ COMPARE_SCALAR_FIELD(multixact_freeze_min_age);
+ COMPARE_SCALAR_FIELD(multixact_freeze_table_age);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(va_cols);
index 0787eb7c5d4c2eda4ec57592c2f3bbe0281a1de0..ab3538a4afafaa863d00361058ab97c87e04274a 100644 (file)
@@ -8726,6 +8726,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_VERBOSE;
n->freeze_min_age = 3ドル ? 0 : -1;
n->freeze_table_age = 3ドル ? 0 : -1;
+ n->multixact_freeze_min_age = 3ドル ? 0 : -1;
+ n->multixact_freeze_table_age = 3ドル ? 0 : -1;
n->relation = NULL;
n->va_cols = NIL;
$$ = (Node *)n;
@@ -8740,6 +8742,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_VERBOSE;
n->freeze_min_age = 3ドル ? 0 : -1;
n->freeze_table_age = 3ドル ? 0 : -1;
+ n->multixact_freeze_min_age = 3ドル ? 0 : -1;
+ n->multixact_freeze_table_age = 3ドル ? 0 : -1;
n->relation = 5ドル;
n->va_cols = NIL;
$$ = (Node *)n;
@@ -8754,6 +8758,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_VERBOSE;
n->freeze_min_age = 3ドル ? 0 : -1;
n->freeze_table_age = 3ドル ? 0 : -1;
+ n->multixact_freeze_min_age = 3ドル ? 0 : -1;
+ n->multixact_freeze_table_age = 3ドル ? 0 : -1;
$$ = (Node *)n;
}
| VACUUM '(' vacuum_option_list ')'
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM | 3ドル;
if (n->options & VACOPT_FREEZE)
+ {
n->freeze_min_age = n->freeze_table_age = 0;
+ n->multixact_freeze_min_age = 0;
+ n->multixact_freeze_table_age = 0;
+ }
else
+ {
n->freeze_min_age = n->freeze_table_age = -1;
+ n->multixact_freeze_min_age = -1;
+ n->multixact_freeze_table_age = -1;
+ }
n->relation = NULL;
n->va_cols = NIL;
$$ = (Node *) n;
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM | 3ドル;
if (n->options & VACOPT_FREEZE)
+ {
n->freeze_min_age = n->freeze_table_age = 0;
+ n->multixact_freeze_min_age = 0;
+ n->multixact_freeze_table_age = 0;
+ }
else
+ {
n->freeze_min_age = n->freeze_table_age = -1;
+ n->multixact_freeze_min_age = -1;
+ n->multixact_freeze_table_age = -1;
+ }
n->relation = 5ドル;
n->va_cols = 6ドル;
if (n->va_cols != NIL) /* implies analyze */
n->options |= VACOPT_VERBOSE;
n->freeze_min_age = -1;
n->freeze_table_age = -1;
+ n->multixact_freeze_min_age = -1;
+ n->multixact_freeze_table_age = -1;
n->relation = NULL;
n->va_cols = NIL;
$$ = (Node *)n;
n->options |= VACOPT_VERBOSE;
n->freeze_min_age = -1;
n->freeze_table_age = -1;
+ n->multixact_freeze_min_age = -1;
+ n->multixact_freeze_table_age = -1;
n->relation = 3ドル;
n->va_cols = 4ドル;
$$ = (Node *)n;
index 77e683a6f1815836ae32ad38601ffee8097302b8..8926325faab29066e787efb64e2834f4f71fbd0c 100644 (file)
int autovacuum_anl_thresh;
double autovacuum_anl_scale;
int autovacuum_freeze_max_age;
+int autovacuum_multixact_freeze_max_age;
int autovacuum_vac_cost_delay;
int autovacuum_vac_cost_limit;
/* Default freeze ages to use for autovacuum (varies by database) */
static int default_freeze_min_age;
static int default_freeze_table_age;
+static int default_multixact_freeze_min_age;
+static int default_multixact_freeze_table_age;
/* Memory context for long-lived data */
static MemoryContext AutovacMemCxt;
bool at_doanalyze;
int at_freeze_min_age;
int at_freeze_table_age;
+ int at_multixact_freeze_min_age;
+ int at_multixact_freeze_table_age;
int at_vacuum_cost_delay;
int at_vacuum_cost_limit;
bool at_wraparound;
/* Also determine the oldest datminmxid we will consider. */
recentMulti = ReadNextMultiXactId();
- multiForceLimit = recentMulti - autovacuum_freeze_max_age;
+ multiForceLimit = recentMulti - autovacuum_multixact_freeze_max_age;
if (multiForceLimit < FirstMultiXactId)
multiForceLimit -= FirstMultiXactId;
{
default_freeze_min_age = 0;
default_freeze_table_age = 0;
+ default_multixact_freeze_min_age = 0;
+ default_multixact_freeze_table_age = 0;
}
else
{
default_freeze_min_age = vacuum_freeze_min_age;
default_freeze_table_age = vacuum_freeze_table_age;
+ default_multixact_freeze_min_age = vacuum_multixact_freeze_min_age;
+ default_multixact_freeze_table_age = vacuum_multixact_freeze_table_age;
}
ReleaseSysCache(tuple);
@@ -2511,6 +2520,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
{
int freeze_min_age;
int freeze_table_age;
+ int multixact_freeze_min_age;
+ int multixact_freeze_table_age;
int vac_cost_limit;
int vac_cost_delay;
? avopts->freeze_table_age
: default_freeze_table_age;
+ multixact_freeze_min_age = (avopts &&
+ avopts->multixact_freeze_min_age >= 0)
+ ? avopts->multixact_freeze_min_age
+ : default_multixact_freeze_min_age;
+
+ multixact_freeze_table_age = (avopts &&
+ avopts->multixact_freeze_table_age >= 0)
+ ? avopts->multixact_freeze_table_age
+ : default_multixact_freeze_table_age;
+
tab = palloc(sizeof(autovac_table));
tab->at_relid = relid;
tab->at_dovacuum = dovacuum;
tab->at_doanalyze = doanalyze;
tab->at_freeze_min_age = freeze_min_age;
tab->at_freeze_table_age = freeze_table_age;
+ tab->at_multixact_freeze_min_age = multixact_freeze_min_age;
+ tab->at_multixact_freeze_table_age = multixact_freeze_table_age;
tab->at_vacuum_cost_limit = vac_cost_limit;
tab->at_vacuum_cost_delay = vac_cost_delay;
tab->at_wraparound = wraparound;
@@ -2568,7 +2591,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
*
* Check whether a relation needs to be vacuumed or analyzed; return each into
* "dovacuum" and "doanalyze", respectively. Also return whether the vacuum is
- * being forced because of Xid wraparound.
+ * being forced because of Xid or multixact wraparound.
*
* relopts is a pointer to the AutoVacOpts options (either for itself in the
* case of a plain table, or for either itself or its parent table in the case
@@ -2587,7 +2610,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
* analyze. This is asymmetric to the VACUUM case.
*
* We also force vacuum if the table's relfrozenxid is more than freeze_max_age
- * transactions back.
+ * transactions back, and if its relminmxid is more than
+ * multixact_freeze_max_age multixacts back.
*
* A table whose autovacuum_enabled option is false is
* automatically skipped (unless we have to vacuum it due to freeze_max_age).
/* freeze parameters */
int freeze_max_age;
+ int multixact_freeze_max_age;
TransactionId xidForceLimit;
MultiXactId multiForceLimit;
? Min(relopts->freeze_max_age, autovacuum_freeze_max_age)
: autovacuum_freeze_max_age;
+ multixact_freeze_max_age = (relopts && relopts->multixact_freeze_max_age >= 0)
+ ? Min(relopts->multixact_freeze_max_age, autovacuum_multixact_freeze_max_age)
+ : autovacuum_multixact_freeze_max_age;
+
av_enabled = (relopts ? relopts->enabled : true);
/* Force vacuum if table is at risk of wraparound */
xidForceLimit));
if (!force_vacuum)
{
- multiForceLimit = recentMulti - autovacuum_freeze_max_age;
+ multiForceLimit = recentMulti - multixact_freeze_max_age;
if (multiForceLimit < FirstMultiXactId)
multiForceLimit -= FirstMultiXactId;
force_vacuum = MultiXactIdPrecedes(classForm->relminmxid,
vacstmt.options |= VACOPT_ANALYZE;
vacstmt.freeze_min_age = tab->at_freeze_min_age;
vacstmt.freeze_table_age = tab->at_freeze_table_age;
+ vacstmt.multixact_freeze_min_age = tab->at_multixact_freeze_min_age;
+ vacstmt.multixact_freeze_table_age = tab->at_multixact_freeze_table_age;
/* we pass the OID, but might need this anyway for an error message */
vacstmt.relation = &rangevar;
vacstmt.va_cols = NIL;
index 2812a73d5454bd3cb13b83c06b3a980218b27ace..86afde17de536540a9e1c2f7ca38bd989ad6376d 100644 (file)
NULL, NULL, NULL
},
+ {
+ {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
+ NULL
+ },
+ &vacuum_multixact_freeze_min_age,
+ 5000000, 0, 1000000000,
+ NULL, NULL, NULL
+ },
+
+ {
+ {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
+ NULL
+ },
+ &vacuum_multixact_freeze_table_age,
+ 150000000, 0, 2000000000,
+ NULL, NULL, NULL
+ },
+
{
{"vacuum_defer_cleanup_age", PGC_SIGHUP, REPLICATION_MASTER,
gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."),
200000000, 100000000, 2000000000,
NULL, NULL, NULL
},
+ {
+ /* see varsup.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
+ {"autovacuum_multixact_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
+ gettext_noop("Multixact age at which to autovacuum a table to prevent multixact wraparound."),
+ NULL
+ },
+ &autovacuum_multixact_freeze_max_age,
+ 400000000, 10000000, 2000000000,
+ NULL, NULL, NULL
+ },
{
/* see max_connections */
{"autovacuum_max_workers", PGC_POSTMASTER, AUTOVACUUM,
index d10e8a5783a2fc210ac16563582a8d3fe27fb4ff..480c9e9797e3238a495f2435ecea465bedb5e696 100644 (file)
#track_counts = on
#track_io_timing = off
#track_functions = none # none, pl, all
-#track_activity_query_size = 1024 # (change requires restart)
+#track_activity_query_size = 1024 # (change requires restart)
#update_process_title = on
#stats_temp_directory = 'pg_stat_tmp'
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
# (change requires restart)
+#autovacuum_multixact_freeze_max_age = 400000000 # maximum Multixact age
+ # before forced vacuum
+ # (change requires restart)
#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for
# autovacuum, in milliseconds;
# -1 means use vacuum_cost_delay
#lock_timeout = 0 # in milliseconds, 0 is disabled
#vacuum_freeze_min_age = 50000000
#vacuum_freeze_table_age = 150000000
+#vacuum_multixact_freeze_min_age = 5000000
+#vacuum_multixact_freeze_table_age = 150000000
#bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64'
#xmloption = 'content'
index 7c368905393dc1d262f1a2888a0fe01980a16c62..70350e02cb2d8e3bb2683dad6748c7520db46b35 100644 (file)
@@ -136,6 +136,8 @@ extern PGDLLIMPORT int default_statistics_target; /* PGDLLIMPORT for
* PostGIS */
extern int vacuum_freeze_min_age;
extern int vacuum_freeze_table_age;
+extern int vacuum_multixact_freeze_min_age;
+extern int vacuum_multixact_freeze_table_age;
/* in commands/vacuum.c */
@@ -156,6 +158,8 @@ extern void vac_update_relstats(Relation relation,
TransactionId frozenxid,
MultiXactId minmulti);
extern 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,
index ad58b3949b60848c2e3911770e576d5ba5eeddcf..f649ad4aa07227b7f4c6b16e99b12f8ba7f80f20 100644 (file)
int options; /* OR of VacuumOption flags */
int freeze_min_age; /* min freeze age, or -1 to use default */
int freeze_table_age; /* age at which to scan whole table */
+ int multixact_freeze_min_age; /* min multixact freeze age,
+ * or -1 to use default */
+ int multixact_freeze_table_age; /* multixact age at which to
+ * scan whole table */
RangeVar *relation; /* single table to process, or NULL */
List *va_cols; /* list of column names, or NIL for all */
} VacuumStmt;
index 543445cc2cbe5086eb0c16b4af0bde7e81b7da93..a43fcb11d1c14a255ba9da6eeefcde7aed6e26e6 100644 (file)
@@ -25,6 +25,7 @@ extern double autovacuum_vac_scale;
extern int autovacuum_anl_thresh;
extern double autovacuum_anl_scale;
extern int autovacuum_freeze_max_age;
+extern int autovacuum_multixact_freeze_max_age;
extern int autovacuum_vac_cost_delay;
extern int autovacuum_vac_cost_limit;
index 9b8a4c9aa50b56ec8d8095bc4c5ef9e0c3430546..c87dadc0ebdfd0baa247c878fcf8b1dbbdd55613 100644 (file)
int freeze_min_age;
int freeze_max_age;
int freeze_table_age;
+ int multixact_freeze_min_age;
+ int multixact_freeze_max_age;
+ int multixact_freeze_table_age;
float8 vacuum_scale_factor;
float8 analyze_scale_factor;
} AutoVacOpts;