Commit 1e55e7d1 authored by Peter Geoghegan's avatar Peter Geoghegan

Add wraparound failsafe to VACUUM.

Add a failsafe mechanism that is triggered by VACUUM when it notices
that the table's relfrozenxid and/or relminmxid are dangerously far in
the past.  VACUUM checks the age of the table dynamically, at regular
intervals.

When the failsafe triggers, VACUUM takes extraordinary measures to
finish as quickly as possible so that relfrozenxid and/or relminmxid can
be advanced.  VACUUM will stop applying any cost-based delay that may be
in effect.  VACUUM will also bypass any further index vacuuming and heap
vacuuming -- it only completes whatever remaining pruning and freezing
is required.  Bypassing index/heap vacuuming is enabled by commit
8523492d, which made it possible to dynamically trigger the mechanism
already used within VACUUM when it is run with INDEX_CLEANUP off.

It is expected that the failsafe will almost always trigger within an
autovacuum to prevent wraparound, long after the autovacuum began.
However, the failsafe mechanism can trigger in any VACUUM operation.
Even in a non-aggressive VACUUM, where we're likely to not advance
relfrozenxid, it still seems like a good idea to finish off remaining
pruning and freezing.   An aggressive/anti-wraparound VACUUM will be
launched immediately afterwards.  Note that the anti-wraparound VACUUM
that follows will itself trigger the failsafe, usually before it even
begins its first (and only) pass over the heap.

The failsafe is controlled by two new GUCs: vacuum_failsafe_age, and
vacuum_multixact_failsafe_age.  There are no equivalent reloptions,
since that isn't expected to be useful.  The GUCs have rather high
defaults (both default to 1.6 billion), and are expected to generally
only be used to make the failsafe trigger sooner/more frequently.

Author: Masahiko Sawada <sawada.mshk@gmail.com>
Author: Peter Geoghegan <pg@bowt.ie>
Discussion: https://postgr.es/m/CAD21AoD0SkE11fMw4jD4RENAwBMcw1wasVnwpJVw3tVqPOQgAw@mail.gmail.com
Discussion: https://postgr.es/m/CAH2-WzmgH3ySGYeC-m-eOBsa2=sDwa292-CFghV4rESYo39FsQ@mail.gmail.com
parent 4f0b0966
...@@ -8644,6 +8644,39 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; ...@@ -8644,6 +8644,39 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="guc-vacuum-failsafe-age" xreflabel="vacuum_failsafe_age">
<term><varname>vacuum_failsafe_age</varname> (<type>integer</type>)
<indexterm>
<primary><varname>vacuum_failsafe_age</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Specifies the maximum age (in transactions) that a table's
<structname>pg_class</structname>.<structfield>relfrozenxid</structfield>
field can attain before <command>VACUUM</command> takes
extraordinary measures to avoid system-wide transaction ID
wraparound failure. This is <command>VACUUM</command>'s
strategy of last resort. The failsafe typically triggers
when an autovacuum to prevent transaction ID wraparound has
already been running for some time, though it's possible for
the failsafe to trigger during any <command>VACUUM</command>.
</para>
<para>
When the failsafe is triggered, any cost-based delay that is
in effect will no longer be applied, and further non-essential
maintenance tasks (such as index vacuuming) are bypassed.
</para>
<para>
The default is 1.6 billion transactions. Although users can
set this value anywhere from zero to 2.1 billion,
<command>VACUUM</command> will silently adjust the effective
value to no less than 105% of <xref
linkend="guc-autovacuum-freeze-max-age"/>.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-vacuum-multixact-freeze-table-age" xreflabel="vacuum_multixact_freeze_table_age"> <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><varname>vacuum_multixact_freeze_table_age</varname> (<type>integer</type>)
<indexterm> <indexterm>
...@@ -8690,6 +8723,39 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; ...@@ -8690,6 +8723,39 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="guc-multixact-failsafe-age" xreflabel="vacuum_multixact_failsafe_age">
<term><varname>vacuum_multixact_failsafe_age</varname> (<type>integer</type>)
<indexterm>
<primary><varname>vacuum_multixact_failsafe_age</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Specifies the maximum age (in transactions) that a table's
<structname>pg_class</structname>.<structfield>relminmxid</structfield>
field can attain before <command>VACUUM</command> takes
extraordinary measures to avoid system-wide multixact ID
wraparound failure. This is <command>VACUUM</command>'s
strategy of last resort. The failsafe typically triggers when
an autovacuum to prevent transaction ID wraparound has already
been running for some time, though it's possible for the
failsafe to trigger during any <command>VACUUM</command>.
</para>
<para>
When the failsafe is triggered, any cost-based delay that is
in effect will no longer be applied, and further non-essential
maintenance tasks (such as index vacuuming) are bypassed.
</para>
<para>
The default is 1.6 billion multixacts. Although users can set
this value anywhere from zero to 2.1 billion,
<command>VACUUM</command> will silently adjust the effective
value to no less than 105% of <xref
linkend="guc-autovacuum-multixact-freeze-max-age"/>.
</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><varname>bytea_output</varname> (<type>enum</type>)
<indexterm> <indexterm>
......
This diff is collapsed.
...@@ -62,6 +62,8 @@ int vacuum_freeze_min_age; ...@@ -62,6 +62,8 @@ 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_min_age;
int vacuum_multixact_freeze_table_age; int vacuum_multixact_freeze_table_age;
int vacuum_failsafe_age;
int vacuum_multixact_failsafe_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 */
...@@ -1134,6 +1136,62 @@ vacuum_set_xid_limits(Relation rel, ...@@ -1134,6 +1136,62 @@ vacuum_set_xid_limits(Relation rel,
} }
} }
/*
* vacuum_xid_failsafe_check() -- Used by VACUUM's wraparound failsafe
* mechanism to determine if its table's relfrozenxid and relminmxid are now
* dangerously far in the past.
*
* Input parameters are the target relation's relfrozenxid and relminmxid.
*
* When we return true, VACUUM caller triggers the failsafe.
*/
bool
vacuum_xid_failsafe_check(TransactionId relfrozenxid, MultiXactId relminmxid)
{
TransactionId xid_skip_limit;
MultiXactId multi_skip_limit;
int skip_index_vacuum;
Assert(TransactionIdIsNormal(relfrozenxid));
Assert(MultiXactIdIsValid(relminmxid));
/*
* Determine the index skipping age to use. In any case no less than
* autovacuum_freeze_max_age * 1.05.
*/
skip_index_vacuum = Max(vacuum_failsafe_age, autovacuum_freeze_max_age * 1.05);
xid_skip_limit = ReadNextTransactionId() - skip_index_vacuum;
if (!TransactionIdIsNormal(xid_skip_limit))
xid_skip_limit = FirstNormalTransactionId;
if (TransactionIdPrecedes(relfrozenxid, xid_skip_limit))
{
/* The table's relfrozenxid is too old */
return true;
}
/*
* Similar to above, determine the index skipping age to use for
* multixact. In any case no less than autovacuum_multixact_freeze_max_age
* * 1.05.
*/
skip_index_vacuum = Max(vacuum_multixact_failsafe_age,
autovacuum_multixact_freeze_max_age * 1.05);
multi_skip_limit = ReadNextMultiXactId() - skip_index_vacuum;
if (multi_skip_limit < FirstMultiXactId)
multi_skip_limit = FirstMultiXactId;
if (MultiXactIdPrecedes(relminmxid, multi_skip_limit))
{
/* The table's relminmxid is too old */
return true;
}
return false;
}
/* /*
* vac_estimate_reltuples() -- estimate the new value for pg_class.reltuples * vac_estimate_reltuples() -- estimate the new value for pg_class.reltuples
* *
......
...@@ -2657,6 +2657,24 @@ static struct config_int ConfigureNamesInt[] = ...@@ -2657,6 +2657,24 @@ static struct config_int ConfigureNamesInt[] =
0, 0, 1000000, /* see ComputeXidHorizons */ 0, 0, 1000000, /* see ComputeXidHorizons */
NULL, NULL, NULL NULL, NULL, NULL
}, },
{
{"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
&vacuum_failsafe_age,
1600000000, 0, 2100000000,
NULL, NULL, NULL
},
{
{"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
&vacuum_multixact_failsafe_age,
1600000000, 0, 2100000000,
NULL, NULL, NULL
},
/* /*
* See also CheckRequiredParameterValues() if this parameter changes * See also CheckRequiredParameterValues() if this parameter changes
...@@ -3257,7 +3275,10 @@ static struct config_int ConfigureNamesInt[] = ...@@ -3257,7 +3275,10 @@ static struct config_int ConfigureNamesInt[] =
NULL NULL
}, },
&autovacuum_freeze_max_age, &autovacuum_freeze_max_age,
/* see pg_resetwal if you change the upper-limit value */ /*
* see pg_resetwal and vacuum_failsafe_age if you change the
* upper-limit value.
*/
200000000, 100000, 2000000000, 200000000, 100000, 2000000000,
NULL, NULL, NULL NULL, NULL, NULL
}, },
......
...@@ -677,6 +677,8 @@ ...@@ -677,6 +677,8 @@
#vacuum_freeze_table_age = 150000000 #vacuum_freeze_table_age = 150000000
#vacuum_multixact_freeze_min_age = 5000000 #vacuum_multixact_freeze_min_age = 5000000
#vacuum_multixact_freeze_table_age = 150000000 #vacuum_multixact_freeze_table_age = 150000000
#vacuum_failsafe_age = 1600000000
#vacuum_multixact_failsafe_age = 1600000000
#bytea_output = 'hex' # hex, escape #bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64' #xmlbinary = 'base64'
#xmloption = 'content' #xmloption = 'content'
......
...@@ -235,6 +235,8 @@ extern int vacuum_freeze_min_age; ...@@ -235,6 +235,8 @@ 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_min_age;
extern int vacuum_multixact_freeze_table_age; extern int vacuum_multixact_freeze_table_age;
extern int vacuum_failsafe_age;
extern int vacuum_multixact_failsafe_age;
/* Variables for cost-based parallel vacuum */ /* Variables for cost-based parallel vacuum */
extern pg_atomic_uint32 *VacuumSharedCostBalance; extern pg_atomic_uint32 *VacuumSharedCostBalance;
...@@ -270,6 +272,8 @@ extern void vacuum_set_xid_limits(Relation rel, ...@@ -270,6 +272,8 @@ extern void vacuum_set_xid_limits(Relation rel,
TransactionId *xidFullScanLimit, TransactionId *xidFullScanLimit,
MultiXactId *multiXactCutoff, MultiXactId *multiXactCutoff,
MultiXactId *mxactFullScanLimit); MultiXactId *mxactFullScanLimit);
extern bool vacuum_xid_failsafe_check(TransactionId relfrozenxid,
MultiXactId relminmxid);
extern void vac_update_datfrozenxid(void); extern void vac_update_datfrozenxid(void);
extern void vacuum_delay_point(void); extern void vacuum_delay_point(void);
extern bool vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, extern bool vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple,
......
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