From a514c5fd850e30210e98c1ff6099607603105b11 Mon Sep 17 00:00:00 2001 From: Manu Date: Thu, 15 Oct 2026 06:51:07 +0000 Subject: [PATCH v3] Avoid unnecessary end-of-recovery barrier waits Commit 67c20979ce7 made end-of-recovery processing emit a process signal barrier and wait for all processes to refresh their cached logical WAL logging state. This can hold up promotion indefinitely if a backend cannot process the barrier, for example while blocked sending data to a client that has stopped reading. At wal_level=logical, logical WAL logging is enabled regardless of the cached state, so no cache synchronization is needed. Skip emitting and waiting for the barrier in this case. When logical decoding will be disabled after recovery, stale caches can only cause extra logical information to be written to WAL, which is safe. Still emit the barrier so processes update their caches asynchronously, but no longer wait for it to complete. These changes allow promotion to finish without waiting for unresponsive backends to process this barrier in either case. Retain the wait in other cases, even if the shared state has not changed, to prevent processes with stale caches from omitting required logical information after recovery. Backpatch-through: 19 --- src/backend/replication/logical/logicalctl.c | 33 +++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/backend/replication/logical/logicalctl.c b/src/backend/replication/logical/logicalctl.c index 8554df75a18..182d15e5653 100644 --- a/src/backend/replication/logical/logicalctl.c +++ b/src/backend/replication/logical/logicalctl.c @@ -664,9 +664,9 @@ DisableLogicalDecoding(void) } /* - * Updates the logical decoding status at end of recovery, and ensures that - * all running processes have the updated XLogLogicalInfo status. This - * function must be called before accepting writes. + * Update the logical decoding status at end of recovery, synchronizing + * process-local WAL logging state when necessary. This function must + * be called before accepting writes. */ void UpdateLogicalDecodingStatusEndOfRecovery(void) @@ -746,16 +746,25 @@ UpdateLogicalDecodingStatusEndOfRecovery(void) LWLockRelease(LogicalDecodingControlLock); /* - * Ensure all running processes have the updated status. We don't need to - * wait for running transactions to finish as we don't accept any writes - * yet. On the other hand, we need to wait for synchronizing - * XLogLogicalInfo even if we've not updated the status above as the - * status have been turned on and off during recovery, having running - * processes have different status on their local caches. + * With wal_level = logical, XLogLogicalInfoActive() is always true, so no + * barrier is needed. + * + * Otherwise, notify all processes to refresh XLogLogicalInfo. Wait only + * if new_status is true, even if the shared status is unchanged, since + * recovery may have left stale caches. Writes are not yet allowed, so we + * need not wait for running transactions to finish. + * + * If new_status is false, stale caches only cause extra logical + * information in WAL, which is safe (see DisableLogicalDecoding()). */ - if (IsUnderPostmaster) - WaitForProcSignalBarrier( - EmitProcSignalBarrier(PROCSIGNAL_BARRIER_UPDATE_XLOG_LOGICAL_INFO)); + if (IsUnderPostmaster && wal_level != WAL_LEVEL_LOGICAL) + { + uint64 generation; + + generation = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_UPDATE_XLOG_LOGICAL_INFO); + if (new_status) + WaitForProcSignalBarrier(generation); + } INJECTION_POINT("startup-logical-decoding-status-change-end-of-recovery", NULL); } -- 2.56.0