From 2ea10ae333022b777904e3e64da7f62485a44c90 Mon Sep 17 00:00:00 2001
From: "duankunren.dkr" <duankunren.dkr@alibaba-inc.com>
Date: Thu, 19 Mar 2026 22:07:51 +0800
Subject: [PATCH] Fix multixact compat logic via unconditional zero

---
 src/backend/access/transam/multixact.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 26b8d4e1230..a9a64d6c9c4 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -897,10 +897,21 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 	 * multixid was assigned.  If we're replaying WAL that was generated by
 	 * such a version, the next page might not be initialized yet.  Initialize
 	 * it now.
+	 *
+	 * The previous condition checked latest_page_number == pageno, but that
+	 * fails after a crash-restart: StartupMultiXact() sets
+	 * latest_page_number to page(checkPoint.nextMulti), which can be
+	 * next_pageno or even higher when the checkpoint captured an advanced
+	 * nextMXact.  In that case, the == check doesn't match and we skip
+	 * initialization, causing SimpleLruReadPage(next_pageno) to fail with
+	 * "read too few bytes" because the page doesn't exist on disk.
+	 *
+	 * Use an unconditional check instead: during recovery, whenever we cross
+	 * a page boundary, always ensure the next page is initialized.
+	 * SimpleLruZeroPage is idempotent, and we use pre_initialized_offsets_page
+	 * to skip the subsequent ZERO_OFF_PAGE replay, so this is safe.
 	 */
-	if (InRecovery &&
-		next_pageno != pageno &&
-		MultiXactOffsetCtl->shared->latest_page_number == pageno)
+	if (InRecovery && next_pageno != pageno)
 	{
 		elog(DEBUG1, "next offsets page is not initialized, initializing it now");
 
-- 
2.32.0.3.g01195cf9f

