PostgreSQL 19 Fixes NOTIFY So Redis Pipelines ScaleBytePith<br>MainHomeSearchMy ProfileAbout Us<br>Explore TopicsWorldPoliticsBusinessTechnologyScienceHealthTravelLifestyleEnvironmentOpinionBreakingSportsEntertainment
Sign In
View Profile →<br>Sign Out
A search box that's 30 seconds behind reality feels broken even when nothing is technically wrong. This is the plumbing that fixes that — with the honest limits included, not glossed over.
Why "just query the database again" stops working<br>Most apps start with the simplest possible design: every few seconds, a background job asks the database "anything new since last time?" and rebuilds whatever index needs updating. This works at small scale. The problem is that it ties freshness to an arbitrary timer instead of to the moment something actually happened, and the repeated SELECT ... WHERE updated_at > ? gets more expensive as the table grows.
An event-driven pipeline flips this: Postgres announces a change the moment it happens, and a message system carries that announcement to whoever's listening. Nobody polls.
Polling versus event-driven index updatesTwo paths from a database write to an updated search index: a timer-based poll that ends up stale, and an event-driven path that updates within milliseconds.Two Ways an App Learns a Row ChangedSame database write, two very different delays before the index catches upThe old way: a timer checks in, whether or not anything happenedNew order row savedPoller runs a queryevery 5 to 30 secondsIndex updatesup to 30s staleThe event-driven way: Postgres speaks up the moment it happensNew order row savedPostgres NOTIFY firesRedis XADD appends eventIndex updatesmilliseconds laterArchitecture pattern, not a measured benchmark<br>What NOTIFY does, what changed in PG19, and the 8KB ceiling you must design around<br>Postgres has had a "tell me when something happens" mechanism for years: any session can run LISTEN orders_changed, and any other session can run NOTIFY orders_changed (usually via pg_notify() inside a trigger) to broadcast a message to every session listening on that channel — no polling required.
Two things matter here. First, PostgreSQL 19 — currently in beta, with Beta 2 released July 16, 2026 — changes NOTIFY so it wakes only backends actually listening on that channel, instead of most connected backends. That's a real scalability fix for pipelines like this one.
Second, and this is easy to miss in a first draft: pg_notify payloads are capped at 8000 bytes. Sending a full row as JSON through NOTIFY works fine in a demo and then fails in production the day someone adds a large text or JSONB column, or the row itself grows. The fix is to never send the row — send only an identifier and an operation type, and let the consumer fetch the current state when it processes the event:
SQLCopy<br>CREATE OR REPLACE FUNCTION notify_order_change() RETURNS trigger AS $$<br>BEGIN<br>PERFORM pg_notify(<br>'orders_changed',<br>json_build_object('id', NEW.id, 'op', TG_OP)::text<br>);<br>RETURN NEW;<br>END;<br>$$ LANGUAGE plpgsql;
CREATE TRIGGER orders_notify<br>AFTER INSERT OR UPDATE ON orders<br>FOR EACH ROW EXECUTE FUNCTION notify_order_change();
This has a second benefit beyond staying under 8KB: because the consumer re-reads the row by id instead of trusting a stale snapshot, it naturally handles the case where two updates to the same row arrive close together — the second read just picks up whatever is current.
NOTIFY wake-up behavior before and after PostgreSQL 19Before version 19, a single NOTIFY woke nearly every connected backend; from version 19 onward, only backends actually listening on that channel wake up.Who Wakes Up When NOTIFY FiresSame single NOTIFY call, before and after PostgreSQL 19Before PostgreSQL 19NOTIFYAll 4 backends wake up, only 1 was listeningPostgreSQL 19 and laterNOTIFYOnly the 1 listening backend wakes upBehavior change per PostgreSQL 19 release notes, not a measured count<br>Redis Pub/Sub versus Streams — and where Redis's guarantee actually starts<br>Redis Pub/Sub looks like the obvious carrier for these events: subscribe, get messages as they arrive. The problem is that Pub/Sub stores nothing — if your worker restarts, deploys, or drops its connection, every message sent during that gap is gone. Redis Streams fix this: events written with XADD stay in the log until trimmed, so a worker that reconnects can pick up where it left off, and a consumer group lets multiple workers split the load without double-processing.
But it's worth being precise about where this durability guarantee begins. Once an event is written to the stream, Streams will not lose it. What Streams cannot protect is the moment before that — the gap between Postgres firing NOTIFY and some relay process actually being connected and writing to the stream. That gap is the subject of the Caveats section below.
Redis Pub/Sub versus Redis Streams for a change-event pipelineStreams keep messages after delivery and support consumer groups and replay; Pub/Sub does not....