- Shipped
- September 4, 2026 at 8:02 PM UTC
- Author
- Kamo
- Commit
- 6c253b6
A durable push consumer admits exactly ONE subscriber. The second pod to bind the same name is refused with [SUB-90012] Consumer is already bound to a subscription, and nothing retries. ChatSessionSubscriptionManager names its durable after the chat session GUID, so with two MediaService pods whichever one bound a conversation first would silently own it and every member whose WebSocket landed on the other pod would receive nothing — no messages, no read receipts, no member-added events, no error. That single property is why MediaService has been pinned to one replica, and why a deploy of it is a total chat outage rather than a rollover. subscribeEphemeral() creates an unnamed consumer instead. There is no cross-pod identity to collide over, so every pod receives every message and fans out to its own STOMP clients — which is what the in-heap simple broker requires. It keeps the filterSubject the durable path learned to set (an empty filter consumes the whole stream), and adds an inactiveThreshold so a pod that is SIGKILLed without unsubscribing does not leave its consumer on the stream forever. No behaviour change for existing callers: subscribe() is untouched, and the delivery loop both paths use is now one method so they cannot drift. Ephemeral consumers do not replay messages published while nothing was subscribed. Neither did the durable ones — both use DeliverPolicy.New — so this is not a regression.