From b29620302d364a1bd7b4d7e6e22b5f262c9e71a5 Mon Sep 17 00:00:00 2001
From: Aidar Imamov <imamovaj22@gmail.com>
Date: Thu, 15 Oct 2026 17:01:46 +0300
Subject: [PATCH v2] Disable startup progress timeout during standby WAL replay

The server disables periodic recovery progress logging controlled by
log_startup_progress_interval in standby mode, because a standby
remains in recovery until promotion and would otherwise emit recovery
progress messages indefinitely.

However, startup operations executed afterward, such as
ResetUnloggedRelations(), can re-enable the startup progress timeout.
As a result, the timeout could previously remain active during standby
WAL replay.

This caused no user-visible issue because recovery progress messages
are not emitted during standby WAL replay. However, the timeout still
generated unnecessary periodic wakeups.

Fix this by disabling the startup progress timeout again when WAL
replay starts in standby mode. This preserves progress reporting for
earlier startup operations while keeping the timeout disabled during
standby WAL replay as intended.

Also clarify the log_startup_progress_interval documentation to
describe which startup operations emit progress messages, and that WAL
replay progress is not reported on standby servers.

Separately, ResolveRecoveryConflictWithLock() and
ResolveRecoveryConflictWithBufferPin() end with
disable_all_timeouts(false), which cancels every timeout the startup
process has, not just the two these functions enable.  Since commit
9ce346eabf3 the startup process also has STARTUP_PROGRESS_TIMEOUT, so a
single recovery conflict stops "redo in progress" messages for the rest
of the redo, even though redo keeps going.  Cancel only the two
timeouts each function enables.
---
 doc/src/sgml/config.sgml                  |  4 +++-
 src/backend/access/transam/xlogrecovery.c | 12 ++++++++++++
 src/backend/storage/ipc/standby.c         | 20 ++++++++++----------
 3 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e537b3b0043..35447fa6260 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7736,7 +7736,9 @@ local0.*    /var/log/postgresql
          operation. The default is 10 seconds. A setting of <literal>0</literal>
          disables the feature.  If this value is specified without units,
          it is taken as milliseconds.  This setting is applied separately to
-         each operation.
+         each operation, such as WAL replay, syncing the data
+         directory, and resetting unlogged relations.  On a standby server,
+         however, it does not apply during WAL replay.
          This parameter can only be set in the <filename>postgresql.conf</filename>
          file or on the server command line.
         </para>
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 20be788da78..5f071382f8b 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -1669,6 +1669,18 @@ PerformWalRecovery(void)
 	 */
 	CheckRecoveryConsistency();
 
+	/*
+	 * EnableStandbyMode() disables periodic recovery progress logging,
+	 * because a standby remains in recovery until promotion and would
+	 * otherwise emit recovery progress messages indefinitely.
+	 *
+	 * However, earlier startup phases executed afterward, such as
+	 * ResetUnloggedRelations(), may have re-enabled the timeout. Therefore,
+	 * disable it again before entering WAL replay.
+	 */
+	if (StandbyMode)
+		disable_startup_progress_timeout();
+
 	/*
 	 * Find the first record that logically follows the checkpoint --- it
 	 * might physically precede it, though.
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 7f011e04990..32b19814c6f 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -754,12 +754,12 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
 cleanup:
 
 	/*
-	 * Clear any timeout requests established above.  We assume here that the
-	 * Startup process doesn't have any other outstanding timeouts than those
-	 * used by this function. If that stops being true, we could cancel the
-	 * timeouts individually, but that'd be slower.
+	 * Cancel only the timeouts established above.  The startup process also
+	 * has STARTUP_PROGRESS_TIMEOUT, which must not be canceled here, or
+	 * recovery progress reporting would stop for the rest of the redo.
 	 */
-	disable_all_timeouts(false);
+	disable_timeout(STANDBY_LOCK_TIMEOUT, false);
+	disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
 	got_standby_lock_timeout = false;
 	got_standby_deadlock_timeout = false;
 }
@@ -865,12 +865,12 @@ ResolveRecoveryConflictWithBufferPin(void)
 	}
 
 	/*
-	 * Clear any timeout requests established above.  We assume here that the
-	 * Startup process doesn't have any other timeouts than what this function
-	 * uses.  If that stops being true, we could cancel the timeouts
-	 * individually, but that'd be slower.
+	 * Cancel only the timeouts established above.  The startup process also
+	 * has STARTUP_PROGRESS_TIMEOUT, which must not be canceled here, or
+	 * recovery progress reporting would stop for the rest of the redo.
 	 */
-	disable_all_timeouts(false);
+	disable_timeout(STANDBY_TIMEOUT, false);
+	disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
 	got_standby_delay_timeout = false;
 	got_standby_deadlock_timeout = false;
 }
-- 
2.47.1

