On Sat, Sep 5, 2026 at 11:15 PM Andrey Borodin <[email protected]> wrote: > > Hi hackers, > > Here is v2. Self-review found that v1 advanced catalog_xmin from the > startup process once confirmed_flush_lsn reached the pause point. That > is not safe: confirmed output does not prove that logical decoding no > longer needs older catalog rows. >
Hi Andrey, Kirk, Nik, First up, nice idea. I like it and it's simple. But one thing kept nagging at me as I read through this, there's no ceiling on how long recovery can stay paused. You do mention this in the docs. Pausing doesn't guarantee the slot survives, and if it can't, the operator has to step in and either drop the slot or resume recovery manually. That's honest, but it does mean the problem doesn't go away, it just moves to whoever's on call. And the scenario I keep picturing is: the consumer just dies - pg_recvlogical segfaulting, whatever. Recovery pauses, waiting for a consumer that's never coming back, and as far as I can tell it just sits there. Forever, or until someone happens to notice. That's actually a bit worse than what happens today. Right now, a slow consumer gets its slot killed and the standby moves on. Not great, but at least it's self-limiting and it leaves a visible trace (invalidation_reason, something monitoring can catch). With this patch, a dead consumer instead quietly turns into a standby that's stuck in the past indefinitely. If that standby feeds anything else, a cascading replica, that stall is arguably a worse failure mode than the one you're fixing. Two things that I think would fix this without fighting the design you've already got: First, just put a limit on it. Something like a max-pause-duration setting: if the wait drags past that, give up and let it fall back to today's behavior (resume, let the slot get invalidated normally). That keeps the "unattended" promise: worst case, it degrades to exactly what happens now, rather than hanging indefinitely. And it slots in naturally, since WaitForRecoveryToResume() already has a loop that could just as easily check elapsed time alongside the conflict predicate. Second, even with a cap, it'd be nice if a long pause said something more than once. Right now you get a single log line the moment the pause starts, and after that the only way to know you're still stuck is to go poll pg_get_wal_replay_pause_state() yourself and remember when it started. A repeated warning every so often while it's stuck would mean you find out from the logs. I don't think either of these needs to block the core idea. The whole appeal of the feature is that it can run unattended; a pause with no ceiling quietly takes that back. Regards, Ajin Cherian Fujitsu Australia
