Re: Optimize LISTEN/NOTIFY
Joel Jacobson <joel@compiler.org>
From: "Joel Jacobson" <joel@compiler.org>
To: "Arseniy Mukhin" <arseniy.mukhin.dev@gmail.com>
Cc: pgsql-hackers <pgsql-hackers@postgresql.org>
Date: 2025-11-12T20:37:51Z
Lists: pgsql-hackers
On Wed, Nov 12, 2025, at 17:57, Arseniy Mukhin wrote: > On Tue, Nov 11, 2025 at 7:35 PM Joel Jacobson <joel@compiler.org> wrote: >> I'm therefore attaching v24 again, but renamed to v26. > > Thank you for the new version! Thanks for reviewing! > I read direct advancement part of v26, one point about it: > > The comment in SignalBackend says: > > * Listening backends that are not advancing and are stationary at > * a position somewhere in the range we just wrote, can safely be > * direct advanced to the new queue head, since we know that they > * are not interested in our messages. > */ > > IIUC it's impossible for the listener to stop somewhere in between > queueHeadBeforeWrite and queueHeadAfterWrite. If the listener has > managed to read the first notification from the notifier, it means the > notifier transaction is complete and the listener should stop only > after reading all notifications (so we should always see pos = > queueHeadAfterWrite or further). Here is what I think can happen: If the notifications written by the notifier fills the current page, it updates QUEUE_HEAD, and if a listening backend then enters asyncQueueReadAllNotifications at this time, it will set its local `head` variable to the current QUEUE_HEAD, and when the notifier continues filling the next page, it will again update QUEUE_HEAD, and PreCommit_Notify will overwrite queueHeadAfterWrite with the QUEUE_HEAD. Sequence of events: 1. In the notifier, PreCommit_Notify calls asyncQueueAddEntries, which updates QUEUE_HEAD when the page is full, (and sets queueHeadAfterWrite to this value). 2. At this time, a listener wakes up and asyncQueueAddEntries reads the current QUEUE_HEAD value and stores it in its local `head` variable, and starts reading up to this pos. 3. In the notifier, PreCommit_Notify calls asyncQueueAddEntries the second time, which updates QUEUE_HEAD, and sets queueHeadAfterWrite to the final value before returning. For this reason, I think the listener could actually stop in between queueHeadBeforeWrite and queueHeadAfterWrite, since it's local `head` variable could get the intermediary QUEUE_HEAD value, when a page is full. /Joel