database.
A convenient way to
examine this information is to execute queries such as:
<programlisting>
SELECT c.oid::regclass as table_name,
greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm');
SELECT datname, age(datfrozenxid) FROM pg_database;
</programlisting>
The <literal>age</literal> column measures the number of transactions from the
cutoff XID to the current transaction's XID.
</para>
<tip>
<para>
When the <command>VACUUM</command> command's <literal>VERBOSE</literal>
parameter is specified, <command>VACUUM</command> prints various
statistics about the table. This includes information about how
<structfield>relfrozenxid</structfield> and
<structfield>relminmxid</structfield> advanced, and the number of
newly frozen pages. The same details appear in the server log when
autovacuum logging (controlled by <xref
linkend="guc-log-autovacuum-min-duration"/>) reports on a
<command>VACUUM</command> operation executed by autovacuum.
</para>
</tip>
<para>
While <command>VACUUM</command> scans mostly pages that have been
modified since the last vacuum, it may also eagerly scan some
all-visible but not all-frozen pages in an attempt to freeze them, but
the <structfield>relfrozenxid</structfield> will only be advanced when
every page of the table that might contain unfrozen XIDs is scanned.
This happens when
<structfield>relfrozenxid</structfield> is more than
<varname>vacuum_freeze_table_age</varname> transactions old, when
<command>VACUUM</command>'s <literal>FREEZE</literal> option is used, or when all
pages that are not already all-frozen happen to
require vacuuming to remove dead row versions. When <command>VACUUM</command>
scans every page in the table that is not already all-frozen, it should
set <literal>age(relfrozenxid)</literal> to a value just a little more than the
<varname>vacuum_freeze_min_age</varname> setting
that was used (more by the number of transactions started since the
<command>VACUUM</command> started). <command>VACUUM</command>
will set <structfield>relfrozenxid</structfield> to the oldest XID
that remains in the table, so it's possible that the final value
will be much more recent than strictly required.
If no <structfield>relfrozenxid</structfield>-advancing
<command>VACUUM</command> is issued on the table until
<varname>autovacuum_freeze_max_age</varname> is reached, an autovacuum will soon
be forced for the table.
</para>
<para>
If for some reason autovacuum fails to clear old XIDs from a table, the
system will begin to emit warning messages like this when the database's
oldest XIDs reach forty million transactions from the wraparound point:
<programlisting>
WARNING: database "mydb" must be vacuumed within 39985967 transactions
HINT: To avoid XID assignment failures, execute a database-wide VACUUM in that database.
</programlisting>
(A manual <command>VACUUM</command> should fix the problem, as suggested by the
hint; but note that the <command>VACUUM</command> should be performed by a
superuser, else it will fail to process system catalogs, which prevent it from
being able to advance the database's <structfield>datfrozenxid</structfield>.)
If these warnings are ignored, the system will refuse to assign new XIDs once
there are fewer than three million transactions left until wraparound:
<programlisting>
ERROR: database is not accepting commands that assign new XIDs to avoid wraparound data loss in database "mydb"
HINT: Execute a database-wide VACUUM in that database.
</programlisting>
In this condition any transactions already in progress can continue,
but only read-only transactions can be started. Operations that
modify database records or truncate relations