From b6529d80b6aa7514c5c43a2e9f69caf35e666af4 Mon Sep 17 00:00:00 2001
From: "masao.fujii" <masao.fujii@masao.fujii’s-MacBook-Pro>
Date: Thu, 11 Jun 2026 11:18:00 +0900
Subject: [PATCH v1] Refine MultiXact wraparound hints

Replication slots can hold back XID horizons, but they do not prevent
MultiXact cleanup. Therefore, this commit removes replication slot
advice from MultiXact wraparound hint messages and update
the maintenance documentation to clarify the distinction.

Backpatch to all supported versions.
---
 doc/src/sgml/maintenance.sgml          |  6 ++++++
 src/backend/access/transam/multixact.c | 12 ++++++------
 src/backend/commands/vacuum.c          |  2 +-
 3 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 4a21bdb5de7..ec4204f88d9 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -869,6 +869,12 @@ HINT:  Execute a database-wide VACUUM in that database.
       <simpara>Running transactions and prepared transactions can be ignored if there
        is no chance that they might appear in a multixact.</simpara>
      </listitem>
+     <listitem>
+      <simpara>Unlike transaction ID wraparound, replication slots do not
+       directly hold back multixact cleanup. Dropping stale replication
+       slots is therefore not usually relevant to resolving multixact ID
+       wraparound problems.</simpara>
+     </listitem>
      <listitem>
       <simpara>MXID information is not directly visible in system views such as
        <literal>pg_stat_activity</literal>; however, looking for old XIDs is still a good
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 10cbc0d76bd..8c694658132 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -1040,14 +1040,14 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
 						 errmsg("database is not accepting commands that assign new MultiXactIds to avoid wraparound data loss in database \"%s\"",
 								oldest_datname),
 						 errhint("Execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions.")));
 			else
 				ereport(ERROR,
 						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 						 errmsg("database is not accepting commands that assign new MultiXactIds to avoid wraparound data loss in database with OID %u",
 								oldest_datoid),
 						 errhint("Execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions.")));
 		}
 
 		/*
@@ -1073,7 +1073,7 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
 						 errdetail("Approximately %.2f%% of MultiXactIds are available for use.",
 								   (double) (multiWrapLimit - result) / (MaxMultiXactId / 2) * 100),
 						 errhint("Execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions.")));
 			else
 				ereport(WARNING,
 						(errmsg_plural("database with OID %u must be vacuumed before %u more MultiXactId is used",
@@ -1084,7 +1084,7 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
 						 errdetail("Approximately %.2f%% of MultiXactIds are available for use.",
 								   (double) (multiWrapLimit - result) / (MaxMultiXactId / 2) * 100),
 						 errhint("Execute a database-wide VACUUM in that database.\n"
-								 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+								 "You might also need to commit or roll back old prepared transactions.")));
 		}
 
 		/* Re-acquire lock and start over */
@@ -2211,7 +2211,7 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
 					 errdetail("Approximately %.2f%% of MultiXactIds are available for use.",
 							   (double) (multiWrapLimit - curMulti) / (MaxMultiXactId / 2) * 100),
 					 errhint("To avoid MultiXactId assignment failures, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions.")));
 		else
 			ereport(WARNING,
 					(errmsg_plural("database with OID %u must be vacuumed before %u more MultiXactId is used",
@@ -2222,7 +2222,7 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
 					 errdetail("Approximately %.2f%% of MultiXactIds are available for use.",
 							   (double) (multiWrapLimit - curMulti) / (MaxMultiXactId / 2) * 100),
 					 errhint("To avoid MultiXactId assignment failures, execute a database-wide VACUUM in that database.\n"
-							 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+							 "You might also need to commit or roll back old prepared transactions.")));
 	}
 }
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index a4abb29cf64..38539a6fd3d 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -1177,7 +1177,7 @@ vacuum_get_cutoffs(Relation rel, const VacuumParams *params,
 		ereport(WARNING,
 				(errmsg("cutoff for freezing multixacts is far in the past"),
 				 errhint("Close open transactions soon to avoid wraparound problems.\n"
-						 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
+						 "You might also need to commit or roll back old prepared transactions.")));
 
 	/*
 	 * Determine the minimum freeze age to use: as specified by the caller, or
-- 
2.53.0

