| 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
| 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 |