Skip to content

Postgres LISTEN/NOTIFY Scales Once You Stop Trusting It

DBOS pushed 60K writes/sec through the primitive that took down Recall.ai. The difference is usage, not Postgres.

Ji-ho Choi
Ji-ho Choi
Security & Cloud Editor · Jul 24, 2026 · 4 min read
Postgres LISTEN/NOTIFY Scales Once You Stop Trusting It

A year ago, Recall.ai published a postmortem with a blunt title: "Postgres LISTEN/NOTIFY does not scale." Their database went down three times in a week under lock contention, and the culprit was the pub/sub primitive everyone recommends as the free alternative to Redis. Now DBOS has published a direct rebuttal claiming 60K notified writes per second — a 20x improvement over their own naive baseline.

Here's the uncomfortable part: both posts are correct. They agree on every mechanical detail. What they disagree on is what LISTEN/NOTIFY is, and that's the distinction worth internalizing before you decide whether it belongs in your stack.

The lock nobody reads about until it bites

The mechanics are unintuitive enough that Recall.ai only found them in the Postgres source. When a transaction issues NOTIFY, Postgres takes a global exclusive lock during the commit phase to guarantee notifications enter the queue in commit order. One transaction with a pending notification commits at a time. That single ordering guarantee quietly disables group commit and serializes your write path.

At Recall.ai's scale — tens of thousands of simultaneous writers, each transaction firing its own NOTIFY — hundreds of processes ended up queued behind one database-wide lock. The paradoxical telemetry (load spiking while CPU and I/O cratered) is the classic signature of lock convoy, not resource exhaustion. Their fix was to rip LISTEN/NOTIFY out entirely, which took under a day because only one codepath used it.

DBOS hit the same wall in their benchmark: a NOTIFY-per-write implementation of their streaming API topped out at 2.9K writes per second, pathetic for the hardware involved. Same lock, same convoy.

What DBOS did differently

The rebuttal's entire 20x gain comes from one structural change: stop notifying per transaction. DBOS buffers notifications in memory and flushes them periodically in a single batch transaction. The global lock is still there — it's taken once per flush instead of once per write. Throughput jumped to 60K writes per second at 15–100ms delivery latency, and at that ceiling the Postgres CPU was fully saturated. That last detail matters: the bottleneck became the database doing real work, which is the honest definition of "it scales."

The cost is that a buffered notification can die with the process that buffered it. DBOS's answer is the load-bearing design decision of the whole post: listeners also poll the database at a low frequency as a fallback, so a lost notification degrades latency rather than losing data. The notification is a ping — a hint that it's worth reading the table now instead of at the next poll interval. The table is the source of truth. It always was.

Two caveats before you quote the headline number. Per details shared in the Hacker News discussion, the benchmark ran on a 96-core, 384GB machine — serious hardware, not your Supabase free tier. And 60K/s sustained says little about burst behavior, which is precisely where Recall.ai died. Scale your expectations to your instance.

This pattern is older than the argument

What DBOS "discovered" is the pattern every serious Postgres-backed job system already ships. Graphile Worker and Ruby's Que both use LISTEN/NOTIFY as a wake-up signal over a jobs table, with polling as backstop. It's the transactional outbox with a doorbell attached. The teams that got burned by LISTEN/NOTIFY are, almost uniformly, the teams that treated NOTIFY as a message bus — payload in the notification, delivery assumed, one NOTIFY per event.

That misuse is easy to fall into because the API invites it: NOTIFY accepts a payload string, so it looks like a queue. But the payload caps at 8,000 bytes, delivery is at-most-once to currently-connected listeners, notifications don't cross to read replicas, and LISTEN is session state — which means it silently breaks behind PgBouncer in transaction-pooling mode, the default posture for most production Postgres deployments. None of these are bugs. They're the spec of a doorbell, not a mailbox.

Don't expect upstream to change the calculus soon, either. There's active work on the pgsql-hackers list — a multicast optimization so NOTIFY wakes only the backends listening on the relevant channel, rather than signaling every listener — and DBOS notes a patch aimed at Postgres 19. But that work narrows the many-idle-listeners problem. The global commit-ordering lock stays. Batching remains your job.

Where this actually leaves you

If you're running a typical product on one Postgres primary and you need job queues, cache invalidation, or "something changed, go look" signaling at up to low tens of thousands of events per second: LISTEN/NOTIFY plus an events table is genuinely enough, and adding Kafka or Redis for it is résumé-driven infrastructure. The recipe is short. Write events to a table inside your transaction. Fire NOTIFY from a batching flusher, not per row — or if your write rate is modest, per transaction is fine until it isn't. Listeners treat the notification as a trigger to SELECT new rows, and poll every few seconds regardless. Hold a dedicated non-pooled connection for LISTEN.

If you have massive fan-out, multi-region consumers, replay requirements, or consumers that can't share the primary's connection budget — that's not a doorbell problem. Logical replication and CDC (Debezium into whatever bus you already run) give you durable, ordered delivery without touching the commit path, at the price of managing replication slots that can eat your disk if a consumer stalls. That's the real successor to LISTEN/NOTIFY at scale, not a bigger doorbell.

The verdict on the argument itself: DBOS wins the benchmark, but the framing war is a draw, because "does LISTEN/NOTIFY scale?" was always the wrong question. Signals scale; messages don't. Postgres gave you a doorbell and a filing cabinet. Use both, and you can defer the Kafka conversation for a long time.

Sources & further reading

  1. Postgres LISTEN/NOTIFY actually scales — dbos.dev
  2. Postgres LISTEN/NOTIFY does not scale — recall.ai
  3. Postgres LISTEN/NOTIFY actually scales - discussion — news.ycombinator.com
Ji-ho Choi
Written by
Ji-ho Choi · Security & Cloud Editor

Ji-ho covers the increasingly tangled overlap between cloud architecture and security, drawing on a background as a penetration tester to keep his reporting grounded in real-world attack paths. He never lets a vendor claim go unquestioned and insists that every buzzword come with a proof of concept.

Discussion 0

Join the discussion

Sign in or create an account to comment and vote.

No comments yet

Be the first to weigh in.

Related Reading