From b1aed078e579d45303f4834fdb7db6292391a848 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Wed, 16 Sep 2026 15:39:29 +0300
Subject: [PATCH 1/1] Fix documentation related to widening multixid offsets to
 64 bits

The explanation of aggressive scans claimed that if the multixact
members grows to 2 billion entries, we launch autovacuum even if it's
nominally disabled. That's no longer true; anti-wraparound vacuum is
only triggered if we're in danger of running out of multixids, we no
longer launch it just to shrink the 'members' SLRU. There's no
wraparound danger with 'members' anymore, so it's no worse than if a
user table becomes very bloated.

We do still adjust the freeze threshold though, so that if autovacuum
runs, whether on schedule or as an emergency autovacuum to avoid
wraparound, it will freeze more aggressively if the members SLRU is
large.

I removed the mention of the 4 billion member threshold, even though
that still exists, because the explanation felt misleading. The
aggressiveness is scaled proportionally between 2 and 4 billion
members. At 4 billion it's as aggressive as it gets, but there's no
sudden cliff.

In the passing, also fix one comment in the code for what oldestOffset
is used for.  The freezing cutoff is adjusted to avoid the *members*
SLRU from growing too large.

Reported-by: Noah Misch <noah@leadboat.com>
Discussion: https://www.postgresql.org/message-id/20260827231757.78.noahmisch@microsoft.com
Backpatch-through: 19
---
 doc/src/sgml/maintenance.sgml          | 17 +++++++----------
 src/backend/access/transam/multixact.c |  2 +-
 2 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 137175ca3b5..9719a28cf1d 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -789,8 +789,8 @@ HINT:  Execute a database-wide VACUUM in that database.
      32-bit counter and corresponding storage, all of which requires
      careful aging management, storage cleanup, and wraparound handling.
      There is a separate storage area which holds the list of members in
-     each multixact, which also uses a 32-bit counter and which must also
-     be managed.  The system function
+     each multixact that cannot wrap around, but must still be managed
+     to keep disk usage under control.  The system function
      <function>pg_get_multixact_members()</function> described in
      <xref linkend="functions-pg-snapshot"/> can be used to examine the
      transaction IDs associated with a multixact ID.
@@ -824,16 +824,13 @@ HINT:  Execute a database-wide VACUUM in that database.
     <para>
      As a safety device, an aggressive vacuum scan will
      occur for any table whose multixact-age is greater than <xref
-     linkend="guc-autovacuum-multixact-freeze-max-age"/>. Also, if the number
-     of multixact member entries created exceeds approximately 2 billion
-     entries (occupying roughly 10GB in the
+     linkend="guc-autovacuum-multixact-freeze-max-age"/>. The aggressive
+     scans will occur even if autovacuum is nominally disabled. Also, if
+     the number of multixact member entries created exceeds approximately
+     2 billion entries (occupying roughly 10GB in the
      <literal>pg_multixact/members</literal> directory), aggressive vacuum
      scans will occur more often for all tables, starting with those that
-     have the oldest multixact-age. Both of these kinds of aggressive
-     scans will occur even if autovacuum is nominally disabled. At approximately
-     4 billion entries (occupying roughly 20GB in the
-     <literal>pg_multixact/members</literal> directory), even more aggressive
-     vacuum scans are triggered to reclaim member storage space.
+     have the oldest multixact-age.
     </para>
 
     <para>
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 70a4ea69486..e1c110f6bd1 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -2167,7 +2167,7 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
 	 * Offsets are 64-bits wide and never wrap around, so we don't need to
 	 * consider them for emergency autovacuum purposes.  But now that we're in
 	 * a consistent state, determine MultiXactState->oldestOffset.  It will be
-	 * used to adjust the freezing cutoff, to keep the offsets disk usage in
+	 * used to adjust the freezing cutoff, to keep the members disk usage in
 	 * check.
 	 */
 	SetOldestOffset();
-- 
2.47.3

