Re: Optimize LISTEN/NOTIFY

From: "Joel Jacobson" <joel(at)compiler(dot)org>
To: pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Cc: "Tom Lane" <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Subject: Re: Optimize LISTEN/NOTIFY
Date: 2026-01-13 18:09:53
Message-ID: b4795171-da6f-43bd-a85a-a4e1c7b1b0b5@app.fastmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Tue, Jan 13, 2026, at 13:10, Joel Jacobson wrote:
> On Tue, Jan 13, 2026, at 06:46, Joel Jacobson wrote:
>> On Wed, Jan 7, 2026, at 21:29, Tom Lane wrote:
>>> That seems a little weird: surely such usage is not something we need
>>> to optimize. Maybe it can be justified because it makes the code
>>> simpler, but that's not immediately obvious to me.
>>
>> The reason for not sticking to the two-boolean approach (staged/current)
>> like in v32 was to minimize shared dshash operations in favor of local
>> hash table operations.

I made a closer comparison between v32 and v34, adopting the same naming
in v32 to allow for easier comparison. This exercise paid off; I now
remember another important reason why I looked for alternatives to the
two-boolean approach. There is a significant difference for UNLISTEN *.

In v32, we need to scan the *entire* global hash table while holding an
exclusive lock.

In v34, we only need to scan our own local hash tables.

v32 (naming adopted to match v34):
```
static void
PrepareTableEntriesForUnlistenAll(void)
{
dshash_seq_status status;
GlobalChannelEntry *entry;

dshash_seq_init(&status, globalChannelTable, true);
while ((entry = dshash_seq_next(&status)) != NULL)
{
if (entry->key.dboid == MyDatabaseId)
{
ListenerEntry *listeners = (ListenerEntry *) dsa_get_address(globalChannelDSA,
entry->listenersArray);

for (int i = 0; i < entry->numListeners; i++)
{
if (listeners[i].procNo == MyProcNumber && listeners[i].staged)
{
listeners[i].staged = false;
pendingListenChannels = lappend(pendingListenChannels,
pstrdup(entry->key.channel));
}
}
}
}
dshash_seq_term(&status);
}
```

v34:
```
static void
PrepareTableEntriesForUnlistenAll(void)
{
HASH_SEQ_STATUS seq;
struct ChannelName *channelEntry;
struct PendingListenEntry *pending;

hash_seq_init(&seq, pendingListenActions);
while ((pending = (struct PendingListenEntry *) hash_seq_search(&seq)) != NULL)
pending->action = PENDING_UNLISTEN;

if (localChannelTable != NULL)
{
hash_seq_init(&seq, localChannelTable);
while ((channelEntry = (struct ChannelName *) hash_seq_search(&seq)) != NULL)
{
bool found;

pending = (struct PendingListenEntry *)
hash_search(pendingListenActions, channelEntry->channel, HASH_ENTER, &found);
pending->action = PENDING_UNLISTEN;
}
}
}
```

/Joel

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Joe Conway 2026-01-13 18:44:42 Re: how to gate experimental features (SQL/PGQ)
Previous Message Robert Haas 2026-01-13 18:03:15 Re: CREATE TABLE LIKE INCLUDING TRIGGERS