Your application polls a table every two seconds looking for new work. Most of the time there is none, so you are running a query eighteen hundred times an hour to learn nothing. Lower the interval and the load rises; raise it and latency does.
LISTEN and NOTIFY invert this. The application subscribes to a channel, the database sends a message when something happens, and the polling disappears. It is built in, costs nothing extra to run, and is emphatically not a message queue — which is where teams get into trouble with it.
What changed in 2026
- Change data capture matured as the alternative. For durable, ordered event streaming, CDC tooling became the standard answer, which clarified what LISTEN/NOTIFY is actually for.
- Serverless made the connection requirement awkward. A persistent listening connection sits badly with function-based architectures that expect to be stateless.
- Pooler support stayed a sharp edge. Transaction-mode pooling continued to break it, and the failure remains quiet.
- It stayed popular for the narrow case. Cache invalidation and job wake-ups remain a genuinely good fit that needs no extra infrastructure.
How it works
A client issues LISTEN channel_name and holds the connection open. Anything connected to the same database can issue NOTIFY channel_name, 'payload'. Every listener on that channel receives the message.
The mechanism is transactional, which is more useful than it first appears: a notification issued inside a transaction is delivered only when that transaction commits. If it rolls back, nothing is sent. That means you cannot notify about a change that did not actually happen — a guarantee that is genuinely hard to get with an external queue, where the write and the publish are separate operations that can diverge.
Combined with a trigger, this gives you notifications on data changes without any application code publishing them.
What it is not
|
LISTEN/NOTIFY |
Message queue |
| Durability |
None — missed if not connected |
Persisted |
| Retries |
None |
Configurable |
| Acknowledgement |
None |
Explicit |
| Ordering |
Per channel, per connection |
Usually guaranteed |
| Payload size |
Small limit |
Large |
| Consumer groups |
No — all listeners get it |
Yes |
| Backpressure |
Queue can overflow |
Handled |
The durability row is the one that matters. If nothing is listening when a notification fires, it is gone. No buffer, no replay, no dead letter. A listener that reconnects after a network blip has missed everything that happened while it was away, and there is no way to find out what.
This makes it unsuitable as a work queue. A job that must run cannot depend on a notification that may never arrive.
The pattern that works around it: treat the notification as a hint rather than a guarantee. The durable state lives in a table; the notification just says "look now instead of waiting". If a notification is missed, a slow fallback poll — every thirty seconds rather than every two — catches it eventually. You get low latency in the common case and correctness in the uncommon one.
Practical constraints
Send identifiers, not data. Payloads have a size limit, and a large one will be rejected. Send a row ID and let the listener query for the current state. That also avoids stale payloads describing a row that changed again before the listener read it.
You need a dedicated connection. LISTEN is session-scoped, so the connection must stay open and stay yours. Behind a transaction-mode pooler this simply does not work — the connection is not yours between transactions. Hold a connection outside the pool for listening, the same constraint that applies to session-scoped advisory locks.
Watch the notification queue. Notifications are buffered until listeners consume them, and that buffer is finite. A slow listener, or a transaction that generates a large number of notifications, can fill it — and when it fills, things start failing in ways that are not obviously about notifications.
Do not notify per row in bulk operations. A trigger firing on a million-row update generates a million notifications. Notify once per statement, or notify about the batch.
Common mistakes
- Treating it as a durable queue. Missed notifications are gone.
- No fallback poll. A reconnect gap becomes silently lost work.
- Large payloads. Rejected at the size limit, often discovered in production.
- Listening through a transaction pooler. Quietly broken.
- Per-row triggers on bulk updates. Floods the queue.
- Assuming all listeners are load-balanced. Every listener on a channel receives every message; there is no consumer group semantics.
- No reconnect logic. Connections drop; a listener must reconnect and re-issue
LISTEN.
FAQ
Can I use it for real-time features?
For pushing a "something changed, refresh" signal to a backend that then updates clients, yes, and it is a good fit. Do not connect browsers to your database — the notification should reach your application, which decides what to send to users.
How does it compare to polling a table?
Lower latency and far less load in the common case. The honest comparison keeps a slow poll as a safety net, so you end up with both — which is still dramatically better than a fast poll alone.
What about change data capture instead?
CDC reads the write-ahead log and gives durable, ordered, replayable events. That is a stronger guarantee and more infrastructure. Use CDC when you need reliability; use notify when you need a cheap wake-up — see write-ahead logging.
Does it work with read replicas?
Notifications do not propagate to replicas in the usual configuration. Listeners need to connect to the instance where the notification is issued, which is a real constraint if your read traffic is on replicas — see read replicas explained.
Where to go next
For the connection constraint this shares, read advisory locks and connection pooling explained. For the durable alternative when notifications must not be missed, write-ahead logging.