An in-memory event cannot reach another instance
Geuneul sends an SSE update when congestion or risk signals surge in an area. An application event is sufficient in one process. With an ECS service that can scale to three instances, the process handling the change may differ from the one holding a user’s SSE connection.
Kafka would provide a durable stream and replay, but its operational cost exceeded the feature’s event rate and scale. Redis Pub/Sub was another option, but Redis already served as disposable cache. I did not want a cache outage to become the consistency boundary for real-time state.
PostgreSQL was the only authoritative state shared by every instance, so I used pg_notify as a cross-instance invalidation signal.
Send “read again,” not the business state
A trigger on the committing transaction publishes only a small region identifier. Each application instance holds a dedicated LISTEN connection. On notification, it rereads the current state from PostgreSQL and emits that snapshot to its local SSE clients.
transaction commit
-> trigger / NOTIFY(region key)
-> every instance LISTEN
-> authoritative database reread
-> local SSE clients receive snapshot
The payload deliberately does not contain the finished business event. NOTIFY is not a durable queue; a disconnected listener can miss it. Treating its payload as truth would permanently diverge application state. A missed signal does not delete the committed row, so the database reread remains the consistency boundary.
Snapshot polling repairs disconnected time
The browser does not build state exclusively by applying SSE deltas. It fetches a snapshot on initial connection and reconnect, and uses low-frequency polling when the stream is unhealthy. SSE reduces latency; snapshots restore correctness.
The listener has reconnect and health handling. Its dedicated connection is separate from ordinary pooled queries, and shutdown closes both LISTEN resources and SSE emitters. Bursts for the same region are coalesced to avoid redundant rereads.
Pay the consistency cost appropriate to the scale
Integration tests cover a real PostgreSQL trigger producing NOTIFY and the listener rereading state. Separate tests cover SSE connect, disconnect, snapshot replacement, and fallback behavior. The central assertion is not the number of delivered notifications; it is that the eventual snapshot equals the latest database state.
This design is specific to a low event rate and at most three instances. If ordering, long-term replay, or consumer offsets become requirements, a durable log is the better boundary. The goal was to respect distributed-state failure modes without operating a broker before it provided real value.