<literal>FrozenTransactionId</literal>, which does not follow the normal XID
comparison rules and is always considered older
than every normal XID.
Frozen row versions are treated as if the inserting XID were
<literal>FrozenTransactionId</literal>, so that they will appear to be
<quote>in the past</quote> to all normal transactions regardless of wraparound
issues, and so such row versions will be valid until deleted, no matter
how long that is.
</para>
<note>
<para>
In <productname>PostgreSQL</productname> versions before 9.4, freezing was
implemented by actually replacing a row's insertion XID
with <literal>FrozenTransactionId</literal>, which was visible in the
row's <structname>xmin</structname> system column. Newer versions just set a flag
bit, preserving the row's original <structname>xmin</structname> for possible
forensic use. However, rows with <structname>xmin</structname> equal
to <literal>FrozenTransactionId</literal> (2) may still be found
in databases <application>pg_upgrade</application>'d from pre-9.4 versions.
</para>
<para>
Also, system catalogs may contain rows with <structname>xmin</structname> equal
to <literal>BootstrapTransactionId</literal> (1), indicating that they were
inserted during the first phase of <application>initdb</application>.
Like <literal>FrozenTransactionId</literal>, this special XID is treated as
older than every normal XID.
</para>
</note>
<para>
<xref linkend="guc-vacuum-freeze-min-age"/>
controls how old an XID value has to be before rows bearing that XID will be
frozen. Increasing this setting may avoid unnecessary work if the
rows that would otherwise be frozen will soon be modified again,
but decreasing this setting increases
the number of transactions that can elapse before the table must be
vacuumed again.
</para>
<para>
<command>VACUUM</command> uses the <link linkend="storage-vm">visibility map</link>
to determine which pages of a table must be scanned. Normally, it
will skip pages that don't have any dead row versions even if those pages
might still have row versions with old XID values. Therefore, normal
<command>VACUUM</command>s won't always freeze every old row version in the table.
When that happens, <command>VACUUM</command> will eventually need to perform an
<firstterm>aggressive vacuum</firstterm>, which will freeze all eligible unfrozen
XID and MXID values, including those from all-visible but not all-frozen pages.
</para>
<para>
If a table is building up a backlog of all-visible but not all-frozen
pages, a normal vacuum may choose to scan skippable pages in an effort to
freeze them. Doing so decreases the number of pages the next aggressive
vacuum must scan. These are referred to as <firstterm>eagerly
scanned</firstterm> pages. Eager scanning can be tuned to attempt to freeze
more all-visible pages by increasing <xref
linkend="guc-vacuum-max-eager-freeze-failure-rate"/>. Even if eager
scanning has kept the number of all-visible but not all-frozen pages to a
minimum, most tables still require periodic aggressive vacuuming. However,
any pages successfully eager frozen may be skipped during an aggressive
vacuum, so eager freezing may minimize the overhead of aggressive vacuums.
</para>
<para>
<xref linkend="guc-vacuum-freeze-table-age"/>
controls when a table is aggressively vacuumed. All all-visible but not all-frozen
pages are scanned if the number of transactions that have passed since the
last such scan is greater than <varname>vacuum_freeze_table_age</varname> minus
<varname>vacuum_freeze_min_age</varname>. Setting
<varname>vacuum_freeze_table_age</varname> to 0 forces <command>VACUUM</command> to
always use its aggressive strategy.
</para>
<para>
The maximum time that a table