From: Masahiko Sawada Date: Thu, 16 Jul 2026 19:13:16 +0000 (-0700) Subject: Correct logical decoding status at end of recovery with minimal WAL level. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=baf0ec4652c2c42c76cf108bb95860b61c1eff16;p=thirdparty%2Fpostgresql.git Correct logical decoding status at end of recovery with minimal WAL level. Crash recovery running with wal_level='minimal' can replay an XLOG_LOGICAL_DECODING_STATUS_CHANGE record that activates logical decoding, if the server previously ran with a higher wal_level and crashed after the last logical slot was dropped but before the checkpointer deactivated logical decoding. Replaying such a record is correct since it reflects the status at the time it was written. However, UpdateLogicalDecodingStatusEndOfRecovery() asserted that logical decoding is never active with wal_level='minimal', causing an assertion failure at the end of recovery. In production builds, logical decoding would remain active while running with wal_level='minimal'. Instead of special-casing wal_level='minimal', recompute the status at the end of recovery as usual: no logical slot can exist with wal_level='minimal' as RestoreSlotFromDisk() would have rejected it, so the recomputation always deactivates logical decoding in this case, also writing the corresponding status change record. Oversight in 67c20979ce7. Reviewed-by: Guoqing Yang Reviewed-by: Hayato Kuroda Discussion: https://postgr.es/m/CAD21AoAnPAugUnDic+ESvrfXjXHk2bss9eHAD7zP0-Chy2UabA@mail.gmail.com Backpatch-through: 19 --- diff --git a/src/backend/replication/logical/logicalctl.c b/src/backend/replication/logical/logicalctl.c index c11d1316450..624965ef95d 100644 --- a/src/backend/replication/logical/logicalctl.c +++ b/src/backend/replication/logical/logicalctl.c @@ -561,19 +561,17 @@ UpdateLogicalDecodingStatusEndOfRecovery(void) Assert(RecoveryInProgress()); - /* - * With 'minimal' WAL level, there are no logical replication slots during - * recovery. Logical decoding is always disabled, so there is no need to - * synchronize XLogLogicalInfo. - */ - if (wal_level == WAL_LEVEL_MINIMAL) - { - Assert(!IsXLogLogicalInfoEnabled() && !IsLogicalDecodingEnabled()); - return; - } - LWLockAcquire(LogicalDecodingControlLock, LW_EXCLUSIVE); + /* + * With 'minimal' WAL level, no logical replication slot can exist (see + * RestoreSlotFromDisk()), so the new status is always false. However, + * logical decoding could have been enabled during recovery by replaying + * an XLOG_LOGICAL_DECODING_STATUS_CHANGE record from WAL generated with a + * higher wal_level, e.g. if the server crashed right after the last + * logical slot was dropped and then restarted with wal_level='minimal'. + * The code below disables logical decoding in that case. + */ if (wal_level == WAL_LEVEL_LOGICAL || CheckLogicalSlotExists()) new_status = true;