From 1db74dc0ec7115075a031d3537542e1e1caba799 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 22 Oct 2026 12:26:20 -0400 Subject: [PATCH] Clear async.c's isAdvancing flag when a notify read is aborted. asyncQueueReadAllNotifications() sets our isAdvancing flag and clears it when done. But if we error out in between, for example on a cancel while waiting for a safe snapshot, the flag stays set and SignalBackends() never advances or wakes us again, so the queue tail stops moving. To fix, clear the flag in AtAbort_Notify(). (We need not worry about this in subtransaction abort, since asyncQueueReadAllNotifications() is only called in top-level transactions.) Author: shihao zhong Reviewed-by: Tom Lane Discussion: https://postgr.es/m/CAGRkXqQ1bmVwBw2CXiyZeWWP=XkHPXLPcQ7BSg2Q5JKSvDAnTA@mail.gmail.com Backpatch-through: 19 --- src/backend/commands/async.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index eee8bc29f38..a253eb83c98 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -2413,13 +2413,24 @@ SignalBackends(void) * This is called at transaction abort. * * Revert any staged listen/unlisten changes and clean up transaction state. - * This only does anything if we abort after PreCommit_Notify has staged - * some entries. */ void AtAbort_Notify(void) { - /* Revert staged listen/unlisten changes */ + /* + * If we failed during asyncQueueReadAllNotifications(), clear our + * isAdvancing flag, else SignalBackends() would never wake us again. + * Since only our own process changes this flag, it is safe to check it + * without the lock. + */ + if (amRegisteredListener && QUEUE_BACKEND_IS_ADVANCING(MyProcNumber)) + { + LWLockAcquire(NotifyQueueLock, LW_SHARED); + QUEUE_BACKEND_IS_ADVANCING(MyProcNumber) = false; + LWLockRelease(NotifyQueueLock); + } + + /* Revert any staged listen/unlisten changes */ ApplyPendingListenActions(false); /* If we're no longer listening on anything, unregister */