From: | "Joel Jacobson" <joel(at)compiler(dot)org> |
---|---|
To: | "Thomas Munro" <thomas(dot)munro(at)gmail(dot)com> |
Cc: | pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>, "Heikki Linnakangas" <hlinnaka(at)iki(dot)fi>, "Rishu Bagga" <rishu(dot)postgres(at)gmail(dot)com> |
Subject: | Re: Optimize LISTEN/NOTIFY |
Date: | 2025-07-24 21:03:27 |
Message-ID: | 0b4d402a-9ac2-4aa8-acf8-8231dbe579ea@app.fastmail.com |
Views: | Whole Thread | Raw Message | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-hackers |
On Wed, Jul 23, 2025, at 04:44, Thomas Munro wrote:
> On Wed, Jul 23, 2025 at 1:39 PM Joel Jacobson <joel(at)compiler(dot)org> wrote:
>> In their patch, in asyn.c's SignalBackends(), they do
>> SendInterrupt(INTERRUPT_ASYNC_NOTIFY, procno) instead of
>> SendProcSignal(pid, PROCSIG_NOTIFY_INTERRUPT, procnos[i]). They don't
>> seem to check if the backend is already signalled or not, but maybe
>> SendInterrupt() has signal coalescing built-in so it would be a noop
>> with almost no cost?
>
> Yeah:
>
> + old_pending = pg_atomic_fetch_or_u32(&proc->pendingInterrupts, interruptMask);
> +
> + /*
> + * If the process is currently blocked waiting for an interrupt to arrive,
> + * and the interrupt wasn't already pending, wake it up.
> + */
> + if ((old_pending & (interruptMask | SLEEPING_ON_INTERRUPTS)) ==
> SLEEPING_ON_INTERRUPTS)
> + WakeupOtherProc(proc);
Thanks for confirming the coalescing logic in SendInterrupt. That's a
great low-level optimization. It's clear we're both targeting the same
problem of redundant wake-ups under contention, but approaching it from
different architectural levels.
The core difference, as I see it, is *where* the state management
resides. The "Interrupts vs signals" patch set creates a unified
machinery where the 'pending' state for all subsystems is combined into
a single atomic bitmask. This is a valid approach.
However, I've been exploring an alternative pattern that decouples the
state management from the signaling machinery, allowing each subsystem
to manage its own state independently. I believe this leads to a
simpler, more modular migration path. I've developed a two-patch series
for `async.c` to demonstrate this concept.
1. The first patch introduces a lock-free, atomic finite state machine
(FSM) entirely within async.c. By using a subsystem-specific atomic
integer and CAS operations, async.c can now robustly manage its own
listener states (IDLE, SIGNALLED, PROCESSING). This solves the
redundant signal problem at the source, as notifiers can now observe
a listener's state and refrain from sending a wakeup if one is
already pending.
2. The second patch demonstrates that once state is managed locally, the
wakeup mechanism becomes trivial.** The expensive `SendProcSignal`
call is replaced with a direct `SetLatch`. This leverages the
existing, highly-optimized `WaitEventSet` infrastructure as a simple,
efficient "poke."
This suggests a powerful, incremental migration pattern: first, fix a
subsystem's state management internally; second, replace its wakeup
mechanism. This vertical, module-by-module approach seems complementary
to the horizontal, layer-by-layer refactoring in the "Interrupts vs
signals" thread.
I'll post a more detailed follow-up in that thread to discuss the
broader architectural implications. Attached are the two patches,
reframed to better illustrate this two-step pattern.
/Joel
Attachment | Content-Type | Size |
---|---|---|
pgbench-script.txt | text/plain | 7.0 KB |
pgbench-results.txt | text/plain | 1020 bytes |
0001-Optimize-LISTEN-NOTIFY-signaling-with-a-lock-free-at.patch | application/octet-stream | 14.1 KB |
0002-Optimize-LISTEN-NOTIFY-wakeup-by-replacing-signal-wi.patch | application/octet-stream | 3.3 KB |
From | Date | Subject | |
---|---|---|---|
Next Message | Jacob Champion | 2025-07-24 21:05:33 | Re: More protocol.h replacements this time into walsender.c |
Previous Message | Thom Brown | 2025-07-24 20:58:00 | Re: Retail DDL |