Re: Fix "unexpected logical decoding status change" error; from concurrent logical decoding activation

From: Nikolay Samokhvalov <nik(at)postgres(dot)ai>
To: Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com>
Cc: Srinath Reddy Sadipiralla <srinath2133(at)gmail(dot)com>, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Fix "unexpected logical decoding status change" error; from concurrent logical decoding activation
Date: 2026-09-22 06:32:16
Message-ID: CAM527d_eV_BAYFiQnfZLSPfHoihye=nOi-OnAM_57pdH+F+gfA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Thu, Jul 16, 2026 at 6:52 AM Masahiko Sawada
<sawada(dot)mshk(at)gmail(dot)com> wrote:
> For slot synchronization, the local slot could be created and
> persisted based on the remote slot information fetched before the
> deactivation was replayed, leaving a valid slot whose restart_lsn
> precedes the deactivation. Decoding such a slot after a failover fails
> with:
>
> ERROR: unexpected logical decoding status change 0
>
> These races are confined to the narrow window between checking the
> logical decoding status and the new slot becoming visible; once the
> slot is visible, the invalidation performed by the deactivation
> already covers it. So the fix is simple: re-check the logical decoding
> status after the new slot becomes visible. Regular slot creation
> raises an error and slot synchronization skips persisting the slot. If
> the deactivation happens after the recheck instead, it is guaranteed
> to invalidate the now-visible slot as usual. The attached 0002
> implements this.

The disable/re-enable case described in the comment above the final
IsLogicalDecodingEnabled() check in update_and_persist_local_synced_slot() is
reachable.

On b73d13c3, the reproducer uses this sequence:

1. Slot sync fetches failover slot S and pauses at
replication-slot-create-begin, before creating the local slot.
2. The primary drops S. The standby replays the logical-decoding
deactivation while no local S exists to invalidate.
3. The primary recreates S. The standby replays the reactivation.
4. The old slot sync resumes with the first incarnation's restart_lsn.

The final IsLogicalDecodingEnabled() check now returns true, so the old slot
information is persisted. After promoting the standby, decoding that slot
fails with:

ERROR: unexpected logical decoding status change 0

The attached patch adds a logical-decoding status generation. Slot sync
records it before fetching remote slot information and refuses to persist a
new slot if the generation changed in the meantime. It drops the temporary
slot so that the next attempt fetches the current incarnation.

The new injection-point test fails without the fix because the stale slot is
persisted. With the fix, it verifies that the replacement slot is fetched
and that decoding succeeds after promotion. The existing
051_effective_wal_level test and the core regression tests also pass.

This work was done by our new AI harness for Postgres testing.

Nik

Attachment Content-Type Size
v1-0001-fix-slotsync-logical-decoding-status-aba.patch application/x-patch 11.5 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message ChenhuiMo 2026-09-22 06:44:04 [RFC PATCH v1] FULL JOIN decomposition using LEFT and ANTI joins
Previous Message Chao Li 2026-09-22 06:08:29 Re: [PATCH] Explain what the default output_plugin_libraries do