Commit 801c2dc7 authored by Alvaro Herrera's avatar Alvaro Herrera

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.
parent de4b6558
...@@ -4892,6 +4892,33 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; ...@@ -4892,6 +4892,33 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem> </listitem>
</varlistentry> </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"> <varlistentry id="guc-autovacuum-vacuum-cost-delay" xreflabel="autovacuum_vacuum_cost_delay">
<term><varname>autovacuum_vacuum_cost_delay</varname> (<type>integer</type>)</term> <term><varname>autovacuum_vacuum_cost_delay</varname> (<type>integer</type>)</term>
<indexterm> <indexterm>
...@@ -5300,7 +5327,7 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; ...@@ -5300,7 +5327,7 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
<structname>pg_class</>.<structfield>relfrozenxid</> field has reached <structname>pg_class</>.<structfield>relfrozenxid</> field has reached
the age specified by this setting. The default is 150 million the age specified by this setting. The default is 150 million
transactions. Although users can set this value anywhere from zero to 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 to 95% of <xref linkend="guc-autovacuum-freeze-max-age">, so that a
periodical manual <command>VACUUM</> has a chance to run before an periodical manual <command>VACUUM</> has a chance to run before an
anti-wraparound autovacuum is launched for the table. For more 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; ...@@ -5331,6 +5358,47 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem> </listitem>
</varlistentry> </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"> <varlistentry id="guc-bytea-output" xreflabel="bytea_output">
<term><varname>bytea_output</varname> (<type>enum</type>)</term> <term><varname>bytea_output</varname> (<type>enum</type>)</term>
<indexterm> <indexterm>
......
...@@ -108,7 +108,8 @@ ...@@ -108,7 +108,8 @@
<listitem> <listitem>
<simpara>To protect against loss of very old data due to <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> </listitem>
</orderedlist> </orderedlist>
...@@ -379,6 +380,11 @@ ...@@ -379,6 +380,11 @@
<secondary>wraparound</secondary> <secondary>wraparound</secondary>
</indexterm> </indexterm>
<indexterm>
<primary>wraparound</primary>
<secondary>of transaction IDs</secondary>
</indexterm>
<para> <para>
<productname>PostgreSQL</productname>'s MVCC transaction semantics <productname>PostgreSQL</productname>'s MVCC transaction semantics
depend on being able to compare transaction ID (<acronym>XID</>) 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. ...@@ -597,6 +603,54 @@ HINT: Stop the postmaster and vacuum that database in single-user mode.
page for details about using single-user mode. page for details about using single-user mode.
</para> </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>
<sect2 id="autovacuum"> <sect2 id="autovacuum">
......
...@@ -985,7 +985,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI ...@@ -985,7 +985,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
<para> <para>
Custom <xref linkend="guc-vacuum-freeze-min-age"> parameter. Note that Custom <xref linkend="guc-vacuum-freeze-min-age"> parameter. Note that
autovacuum will ignore attempts to set a per-table 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. <xref linkend="guc-autovacuum-freeze-max-age"> setting.
</para> </para>
</listitem> </listitem>
...@@ -1014,6 +1014,43 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI ...@@ -1014,6 +1014,43 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</listitem> </listitem>
</varlistentry> </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> </variablelist>
</refsect2> </refsect2>
......
...@@ -171,6 +171,14 @@ static relopt_int intRelOpts[] = ...@@ -171,6 +171,14 @@ static relopt_int intRelOpts[] =
}, },
-1, 0, 1000000000 -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", "autovacuum_freeze_max_age",
...@@ -179,6 +187,14 @@ static relopt_int intRelOpts[] = ...@@ -179,6 +187,14 @@ static relopt_int intRelOpts[] =
}, },
-1, 100000000, 2000000000 -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", "autovacuum_freeze_table_age",
...@@ -186,6 +202,14 @@ static relopt_int intRelOpts[] = ...@@ -186,6 +202,14 @@ static relopt_int intRelOpts[] =
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
}, -1, 0, 2000000000 }, -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 */ /* list terminator */
{{NULL}} {{NULL}}
}; };
...@@ -1166,6 +1190,12 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind) ...@@ -1166,6 +1190,12 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_max_age)}, offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_max_age)},
{"autovacuum_freeze_table_age", RELOPT_TYPE_INT, {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_table_age)}, 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, {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)}, offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL, {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
......
...@@ -2055,11 +2055,13 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) ...@@ -2055,11 +2055,13 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
Assert(MultiXactIdIsValid(oldest_datminmxid)); Assert(MultiXactIdIsValid(oldest_datminmxid));
/* /*
* The place where we actually get into deep trouble is halfway around * Since multixacts wrap differently from transaction IDs, this logic is
* from the oldest potentially-existing XID/multi. (This calculation is * not entirely correct: in some scenarios we could go for longer than 2
* probably off by one or two counts for Xids, because the special XIDs * billion multixacts without seeing any data loss, and in some others we
* reduce the size of the loop a little bit. But we throw in plenty of * could get in trouble before that if the new pg_multixact/members data
* slop below, so it doesn't matter.) * 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); multiWrapLimit = oldest_datminmxid + (MaxMultiXactId >> 1);
if (multiWrapLimit < FirstMultiXactId) if (multiWrapLimit < FirstMultiXactId)
...@@ -2093,12 +2095,13 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) ...@@ -2093,12 +2095,13 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
/* /*
* We'll start trying to force autovacuums when oldest_datminmxid gets to * 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 * Note: autovacuum_multixact_freeze_max_age is a PGC_POSTMASTER parameter
* seem worth adding separate GUCs for that purpose. * 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) if (multiVacLimit < FirstMultiXactId)
multiVacLimit += FirstMultiXactId; multiVacLimit += FirstMultiXactId;
......
...@@ -313,7 +313,8 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid) ...@@ -313,7 +313,8 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
* value. It doesn't look practical to update shared state from a GUC * value. It doesn't look practical to update shared state from a GUC
* assign hook (too many processes would try to execute the hook, * assign hook (too many processes would try to execute the hook,
* resulting in race conditions as well as crashes of those not connected * 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; xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age;
if (xidVacLimit < FirstNormalTransactionId) if (xidVacLimit < FirstNormalTransactionId)
......
...@@ -850,7 +850,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, ...@@ -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 * Since we're going to rewrite the whole table anyway, there's no reason
* not to be aggressive about this. * 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, &OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
NULL); NULL);
......
...@@ -55,6 +55,8 @@ ...@@ -55,6 +55,8 @@
*/ */
int vacuum_freeze_min_age; int vacuum_freeze_min_age;
int vacuum_freeze_table_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 */ /* A few variables that don't seem worth passing around as parameters */
...@@ -398,6 +400,8 @@ get_rel_oids(Oid relid, const RangeVar *vacrel) ...@@ -398,6 +400,8 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
void void
vacuum_set_xid_limits(int freeze_min_age, vacuum_set_xid_limits(int freeze_min_age,
int freeze_table_age, int freeze_table_age,
int multixact_freeze_min_age,
int multixact_freeze_table_age,
bool sharedRel, bool sharedRel,
TransactionId *oldestXmin, TransactionId *oldestXmin,
TransactionId *freezeLimit, TransactionId *freezeLimit,
...@@ -406,9 +410,11 @@ vacuum_set_xid_limits(int freeze_min_age, ...@@ -406,9 +410,11 @@ vacuum_set_xid_limits(int freeze_min_age,
MultiXactId *mxactFullScanLimit) MultiXactId *mxactFullScanLimit)
{ {
int freezemin; int freezemin;
int mxid_freezemin;
TransactionId limit; TransactionId limit;
TransactionId safeLimit; TransactionId safeLimit;
MultiXactId mxactLimit; MultiXactId mxactLimit;
MultiXactId safeMxactLimit;
/* /*
* We can always ignore processes running lazy vacuum. This is because we * We can always ignore processes running lazy vacuum. This is because we
...@@ -462,13 +468,36 @@ vacuum_set_xid_limits(int freeze_min_age, ...@@ -462,13 +468,36 @@ vacuum_set_xid_limits(int freeze_min_age,
*freezeLimit = limit; *freezeLimit = limit;
/* /*
* simplistic MultiXactId removal limit: use the same policy as for * Determine the minimum multixact freeze age to use: as specified by
* freezing Xids (except we use the oldest known mxact instead of the * caller, or vacuum_multixact_freeze_min_age, but in any case not more
* current next value). * 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) if (mxactLimit < FirstMultiXactId)
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; *multiXactCutoff = mxactLimit;
if (xidFullScanLimit != NULL) if (xidFullScanLimit != NULL)
...@@ -501,9 +530,23 @@ vacuum_set_xid_limits(int freeze_min_age, ...@@ -501,9 +530,23 @@ vacuum_set_xid_limits(int freeze_min_age,
*xidFullScanLimit = limit; *xidFullScanLimit = limit;
/* /*
* Compute MultiXactId limit to cause a full-table vacuum, being * Similar to the above, determine the table freeze age to use for
* careful not to generate an invalid multi. We just copy the logic * multixacts: as specified by the caller, or
* (and limits) from plain XIDs here. * 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; mxactLimit = ReadNextMultiXactId() - freezetable;
if (mxactLimit < FirstMultiXactId) if (mxactLimit < FirstMultiXactId)
...@@ -511,6 +554,10 @@ vacuum_set_xid_limits(int freeze_min_age, ...@@ -511,6 +554,10 @@ vacuum_set_xid_limits(int freeze_min_age,
*mxactFullScanLimit = mxactLimit; *mxactFullScanLimit = mxactLimit;
} }
else
{
Assert(mxactFullScanLimit == NULL);
}
} }
/* /*
......
...@@ -205,6 +205,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, ...@@ -205,6 +205,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
vac_strategy = bstrategy; vac_strategy = bstrategy;
vacuum_set_xid_limits(vacstmt->freeze_min_age, vacstmt->freeze_table_age, 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, onerel->rd_rel->relisshared,
&OldestXmin, &FreezeLimit, &xidFullScanLimit, &OldestXmin, &FreezeLimit, &xidFullScanLimit,
&MultiXactCutoff, &mxactFullScanLimit); &MultiXactCutoff, &mxactFullScanLimit);
...@@ -212,8 +214,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, ...@@ -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 * 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 * 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 * table's minimum MultiXactId is older than or equal to the requested
* full-table scan limit. * mxid full-table scan limit.
*/ */
scan_all = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid, scan_all = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
xidFullScanLimit); xidFullScanLimit);
......
...@@ -3242,6 +3242,8 @@ _copyVacuumStmt(const VacuumStmt *from) ...@@ -3242,6 +3242,8 @@ _copyVacuumStmt(const VacuumStmt *from)
COPY_SCALAR_FIELD(options); COPY_SCALAR_FIELD(options);
COPY_SCALAR_FIELD(freeze_min_age); COPY_SCALAR_FIELD(freeze_min_age);
COPY_SCALAR_FIELD(freeze_table_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(relation);
COPY_NODE_FIELD(va_cols); COPY_NODE_FIELD(va_cols);
......
...@@ -1503,6 +1503,8 @@ _equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b) ...@@ -1503,6 +1503,8 @@ _equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
COMPARE_SCALAR_FIELD(options); COMPARE_SCALAR_FIELD(options);
COMPARE_SCALAR_FIELD(freeze_min_age); COMPARE_SCALAR_FIELD(freeze_min_age);
COMPARE_SCALAR_FIELD(freeze_table_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(relation);
COMPARE_NODE_FIELD(va_cols); COMPARE_NODE_FIELD(va_cols);
......
...@@ -8726,6 +8726,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose ...@@ -8726,6 +8726,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = $3 ? 0 : -1; n->freeze_min_age = $3 ? 0 : -1;
n->freeze_table_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->relation = NULL;
n->va_cols = NIL; n->va_cols = NIL;
$$ = (Node *)n; $$ = (Node *)n;
...@@ -8740,6 +8742,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose ...@@ -8740,6 +8742,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = $3 ? 0 : -1; n->freeze_min_age = $3 ? 0 : -1;
n->freeze_table_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->relation = $5;
n->va_cols = NIL; n->va_cols = NIL;
$$ = (Node *)n; $$ = (Node *)n;
...@@ -8754,6 +8758,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose ...@@ -8754,6 +8758,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = $3 ? 0 : -1; n->freeze_min_age = $3 ? 0 : -1;
n->freeze_table_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; $$ = (Node *)n;
} }
| VACUUM '(' vacuum_option_list ')' | VACUUM '(' vacuum_option_list ')'
...@@ -8761,9 +8767,17 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose ...@@ -8761,9 +8767,17 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
VacuumStmt *n = makeNode(VacuumStmt); VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM | $3; n->options = VACOPT_VACUUM | $3;
if (n->options & VACOPT_FREEZE) if (n->options & VACOPT_FREEZE)
{
n->freeze_min_age = n->freeze_table_age = 0; n->freeze_min_age = n->freeze_table_age = 0;
n->multixact_freeze_min_age = 0;
n->multixact_freeze_table_age = 0;
}
else else
{
n->freeze_min_age = n->freeze_table_age = -1; 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->relation = NULL;
n->va_cols = NIL; n->va_cols = NIL;
$$ = (Node *) n; $$ = (Node *) n;
...@@ -8773,9 +8787,17 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose ...@@ -8773,9 +8787,17 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
VacuumStmt *n = makeNode(VacuumStmt); VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM | $3; n->options = VACOPT_VACUUM | $3;
if (n->options & VACOPT_FREEZE) if (n->options & VACOPT_FREEZE)
{
n->freeze_min_age = n->freeze_table_age = 0; n->freeze_min_age = n->freeze_table_age = 0;
n->multixact_freeze_min_age = 0;
n->multixact_freeze_table_age = 0;
}
else else
{
n->freeze_min_age = n->freeze_table_age = -1; 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->relation = $5;
n->va_cols = $6; n->va_cols = $6;
if (n->va_cols != NIL) /* implies analyze */ if (n->va_cols != NIL) /* implies analyze */
...@@ -8805,6 +8827,8 @@ AnalyzeStmt: ...@@ -8805,6 +8827,8 @@ AnalyzeStmt:
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = -1; n->freeze_min_age = -1;
n->freeze_table_age = -1; n->freeze_table_age = -1;
n->multixact_freeze_min_age = -1;
n->multixact_freeze_table_age = -1;
n->relation = NULL; n->relation = NULL;
n->va_cols = NIL; n->va_cols = NIL;
$$ = (Node *)n; $$ = (Node *)n;
...@@ -8817,6 +8841,8 @@ AnalyzeStmt: ...@@ -8817,6 +8841,8 @@ AnalyzeStmt:
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = -1; n->freeze_min_age = -1;
n->freeze_table_age = -1; n->freeze_table_age = -1;
n->multixact_freeze_min_age = -1;
n->multixact_freeze_table_age = -1;
n->relation = $3; n->relation = $3;
n->va_cols = $4; n->va_cols = $4;
$$ = (Node *)n; $$ = (Node *)n;
......
...@@ -117,6 +117,7 @@ double autovacuum_vac_scale; ...@@ -117,6 +117,7 @@ double autovacuum_vac_scale;
int autovacuum_anl_thresh; int autovacuum_anl_thresh;
double autovacuum_anl_scale; double autovacuum_anl_scale;
int autovacuum_freeze_max_age; int autovacuum_freeze_max_age;
int autovacuum_multixact_freeze_max_age;
int autovacuum_vac_cost_delay; int autovacuum_vac_cost_delay;
int autovacuum_vac_cost_limit; int autovacuum_vac_cost_limit;
...@@ -145,6 +146,8 @@ static MultiXactId recentMulti; ...@@ -145,6 +146,8 @@ static MultiXactId recentMulti;
/* Default freeze ages to use for autovacuum (varies by database) */ /* Default freeze ages to use for autovacuum (varies by database) */
static int default_freeze_min_age; static int default_freeze_min_age;
static int default_freeze_table_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 */ /* Memory context for long-lived data */
static MemoryContext AutovacMemCxt; static MemoryContext AutovacMemCxt;
...@@ -186,6 +189,8 @@ typedef struct autovac_table ...@@ -186,6 +189,8 @@ typedef struct autovac_table
bool at_doanalyze; bool at_doanalyze;
int at_freeze_min_age; int at_freeze_min_age;
int at_freeze_table_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_delay;
int at_vacuum_cost_limit; int at_vacuum_cost_limit;
bool at_wraparound; bool at_wraparound;
...@@ -1130,7 +1135,7 @@ do_start_worker(void) ...@@ -1130,7 +1135,7 @@ do_start_worker(void)
/* Also determine the oldest datminmxid we will consider. */ /* Also determine the oldest datminmxid we will consider. */
recentMulti = ReadNextMultiXactId(); recentMulti = ReadNextMultiXactId();
multiForceLimit = recentMulti - autovacuum_freeze_max_age; multiForceLimit = recentMulti - autovacuum_multixact_freeze_max_age;
if (multiForceLimit < FirstMultiXactId) if (multiForceLimit < FirstMultiXactId)
multiForceLimit -= FirstMultiXactId; multiForceLimit -= FirstMultiXactId;
...@@ -1956,11 +1961,15 @@ do_autovacuum(void) ...@@ -1956,11 +1961,15 @@ do_autovacuum(void)
{ {
default_freeze_min_age = 0; default_freeze_min_age = 0;
default_freeze_table_age = 0; default_freeze_table_age = 0;
default_multixact_freeze_min_age = 0;
default_multixact_freeze_table_age = 0;
} }
else else
{ {
default_freeze_min_age = vacuum_freeze_min_age; default_freeze_min_age = vacuum_freeze_min_age;
default_freeze_table_age = vacuum_freeze_table_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); ReleaseSysCache(tuple);
...@@ -2511,6 +2520,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, ...@@ -2511,6 +2520,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
{ {
int freeze_min_age; int freeze_min_age;
int freeze_table_age; int freeze_table_age;
int multixact_freeze_min_age;
int multixact_freeze_table_age;
int vac_cost_limit; int vac_cost_limit;
int vac_cost_delay; int vac_cost_delay;
...@@ -2544,12 +2555,24 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, ...@@ -2544,12 +2555,24 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
? avopts->freeze_table_age ? avopts->freeze_table_age
: default_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 = palloc(sizeof(autovac_table));
tab->at_relid = relid; tab->at_relid = relid;
tab->at_dovacuum = dovacuum; tab->at_dovacuum = dovacuum;
tab->at_doanalyze = doanalyze; tab->at_doanalyze = doanalyze;
tab->at_freeze_min_age = freeze_min_age; tab->at_freeze_min_age = freeze_min_age;
tab->at_freeze_table_age = freeze_table_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_limit = vac_cost_limit;
tab->at_vacuum_cost_delay = vac_cost_delay; tab->at_vacuum_cost_delay = vac_cost_delay;
tab->at_wraparound = wraparound; tab->at_wraparound = wraparound;
...@@ -2568,7 +2591,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, ...@@ -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 * Check whether a relation needs to be vacuumed or analyzed; return each into
* "dovacuum" and "doanalyze", respectively. Also return whether the vacuum is * "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 * 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 * 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, ...@@ -2587,7 +2610,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
* analyze. This is asymmetric to the VACUUM case. * analyze. This is asymmetric to the VACUUM case.
* *
* We also force vacuum if the table's relfrozenxid is more than freeze_max_age * 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 * A table whose autovacuum_enabled option is false is
* automatically skipped (unless we have to vacuum it due to freeze_max_age). * automatically skipped (unless we have to vacuum it due to freeze_max_age).
...@@ -2629,6 +2653,7 @@ relation_needs_vacanalyze(Oid relid, ...@@ -2629,6 +2653,7 @@ relation_needs_vacanalyze(Oid relid,
/* freeze parameters */ /* freeze parameters */
int freeze_max_age; int freeze_max_age;
int multixact_freeze_max_age;
TransactionId xidForceLimit; TransactionId xidForceLimit;
MultiXactId multiForceLimit; MultiXactId multiForceLimit;
...@@ -2662,6 +2687,10 @@ relation_needs_vacanalyze(Oid relid, ...@@ -2662,6 +2687,10 @@ relation_needs_vacanalyze(Oid relid,
? Min(relopts->freeze_max_age, autovacuum_freeze_max_age) ? Min(relopts->freeze_max_age, autovacuum_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); av_enabled = (relopts ? relopts->enabled : true);
/* Force vacuum if table is at risk of wraparound */ /* Force vacuum if table is at risk of wraparound */
...@@ -2673,7 +2702,7 @@ relation_needs_vacanalyze(Oid relid, ...@@ -2673,7 +2702,7 @@ relation_needs_vacanalyze(Oid relid,
xidForceLimit)); xidForceLimit));
if (!force_vacuum) if (!force_vacuum)
{ {
multiForceLimit = recentMulti - autovacuum_freeze_max_age; multiForceLimit = recentMulti - multixact_freeze_max_age;
if (multiForceLimit < FirstMultiXactId) if (multiForceLimit < FirstMultiXactId)
multiForceLimit -= FirstMultiXactId; multiForceLimit -= FirstMultiXactId;
force_vacuum = MultiXactIdPrecedes(classForm->relminmxid, force_vacuum = MultiXactIdPrecedes(classForm->relminmxid,
...@@ -2755,6 +2784,8 @@ autovacuum_do_vac_analyze(autovac_table *tab, ...@@ -2755,6 +2784,8 @@ autovacuum_do_vac_analyze(autovac_table *tab,
vacstmt.options |= VACOPT_ANALYZE; vacstmt.options |= VACOPT_ANALYZE;
vacstmt.freeze_min_age = tab->at_freeze_min_age; vacstmt.freeze_min_age = tab->at_freeze_min_age;
vacstmt.freeze_table_age = tab->at_freeze_table_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 */ /* we pass the OID, but might need this anyway for an error message */
vacstmt.relation = &rangevar; vacstmt.relation = &rangevar;
vacstmt.va_cols = NIL; vacstmt.va_cols = NIL;
......
...@@ -1975,6 +1975,26 @@ static struct config_int ConfigureNamesInt[] = ...@@ -1975,6 +1975,26 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL 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, {"vacuum_defer_cleanup_age", PGC_SIGHUP, REPLICATION_MASTER,
gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."), gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."),
...@@ -2398,6 +2418,16 @@ static struct config_int ConfigureNamesInt[] = ...@@ -2398,6 +2418,16 @@ static struct config_int ConfigureNamesInt[] =
200000000, 100000000, 2000000000, 200000000, 100000000, 2000000000,
NULL, NULL, NULL 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 */ /* see max_connections */
{"autovacuum_max_workers", PGC_POSTMASTER, AUTOVACUUM, {"autovacuum_max_workers", PGC_POSTMASTER, AUTOVACUUM,
......
...@@ -482,6 +482,9 @@ ...@@ -482,6 +482,9 @@
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze #autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum #autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
# (change requires restart) # (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_vacuum_cost_delay = 20ms # default vacuum cost delay for
# autovacuum, in milliseconds; # autovacuum, in milliseconds;
# -1 means use vacuum_cost_delay # -1 means use vacuum_cost_delay
...@@ -509,6 +512,8 @@ ...@@ -509,6 +512,8 @@
#lock_timeout = 0 # in milliseconds, 0 is disabled #lock_timeout = 0 # in milliseconds, 0 is disabled
#vacuum_freeze_min_age = 50000000 #vacuum_freeze_min_age = 50000000
#vacuum_freeze_table_age = 150000000 #vacuum_freeze_table_age = 150000000
#vacuum_multixact_freeze_min_age = 5000000
#vacuum_multixact_freeze_table_age = 150000000
#bytea_output = 'hex' # hex, escape #bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64' #xmlbinary = 'base64'
#xmloption = 'content' #xmloption = 'content'
......
...@@ -136,6 +136,8 @@ extern PGDLLIMPORT int default_statistics_target; /* PGDLLIMPORT for ...@@ -136,6 +136,8 @@ extern PGDLLIMPORT int default_statistics_target; /* PGDLLIMPORT for
* PostGIS */ * PostGIS */
extern int vacuum_freeze_min_age; extern int vacuum_freeze_min_age;
extern int vacuum_freeze_table_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 */ /* in commands/vacuum.c */
...@@ -156,6 +158,8 @@ extern void vac_update_relstats(Relation relation, ...@@ -156,6 +158,8 @@ extern void vac_update_relstats(Relation relation,
TransactionId frozenxid, TransactionId frozenxid,
MultiXactId minmulti); MultiXactId minmulti);
extern void vacuum_set_xid_limits(int freeze_min_age, int freeze_table_age, 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, bool sharedRel,
TransactionId *oldestXmin, TransactionId *oldestXmin,
TransactionId *freezeLimit, TransactionId *freezeLimit,
......
...@@ -2533,6 +2533,10 @@ typedef struct VacuumStmt ...@@ -2533,6 +2533,10 @@ typedef struct VacuumStmt
int options; /* OR of VacuumOption flags */ int options; /* OR of VacuumOption flags */
int freeze_min_age; /* min freeze age, or -1 to use default */ int freeze_min_age; /* min freeze age, or -1 to use default */
int freeze_table_age; /* age at which to scan whole table */ 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 */ RangeVar *relation; /* single table to process, or NULL */
List *va_cols; /* list of column names, or NIL for all */ List *va_cols; /* list of column names, or NIL for all */
} VacuumStmt; } VacuumStmt;
......
...@@ -25,6 +25,7 @@ extern double autovacuum_vac_scale; ...@@ -25,6 +25,7 @@ extern double autovacuum_vac_scale;
extern int autovacuum_anl_thresh; extern int autovacuum_anl_thresh;
extern double autovacuum_anl_scale; extern double autovacuum_anl_scale;
extern int autovacuum_freeze_max_age; extern int autovacuum_freeze_max_age;
extern int autovacuum_multixact_freeze_max_age;
extern int autovacuum_vac_cost_delay; extern int autovacuum_vac_cost_delay;
extern int autovacuum_vac_cost_limit; extern int autovacuum_vac_cost_limit;
......
...@@ -206,6 +206,9 @@ typedef struct AutoVacOpts ...@@ -206,6 +206,9 @@ typedef struct AutoVacOpts
int freeze_min_age; int freeze_min_age;
int freeze_max_age; int freeze_max_age;
int freeze_table_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 vacuum_scale_factor;
float8 analyze_scale_factor; float8 analyze_scale_factor;
} AutoVacOpts; } AutoVacOpts;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment