# 6. Choosing, and when the answer is neither Previous: [5. The limits](05-limits.md) --- ## The decision, in one table | If | Use | Because | |---|---|---| | Server pushes, client never speaks | **SSE** | A return type. No broker, no converter stack, no origin policy. Reconnect is free. | | Clients talk to each other, or to the server, often | **STOMP over WebSocket** | Routing, presence, per-user destinations, and a path to a real broker without touching controllers. | | One-off progress on an operation the client already started | **SSE** | The stream is scoped to the request. Nothing to clean up. | | Payloads are binary, or large | **WebSocket** | SSE is line-oriented UTF-8; binary costs a third in base64. | | The consumer is another service, not a browser | **neither** — see below | Browsers are the only reason to accept SSE's or STOMP's constraints. | | You need the consumer to say "slow down" | **neither** | Covered in the next article. | ## The costs nobody puts in the table **SSE costs a connection per stream, per tab.** Over HTTP/1.1 a browser allows about six concurrent connections per origin, and an open `EventSource` is one of them. Three tabs of your dashboard leaves three for everything else on the page. Over HTTP/2 they multiplex over one connection and the limit effectively disappears — so "are we behind HTTP/2 end to end, including the proxy?" is a real input to this decision, not a detail. **SSE costs one serialisation per subscriber per event.** The fan-out in [`EmitterRegistry.broadcast`](../src/main/java/com/ankurm/ssews/EmitterRegistry.java) is a loop: a thousand open dashboards means a thousand `send()` calls on one scheduler thread, every tick. The obvious fix — serialise once and write the bytes N times — is not something `SseEmitter` offers. **SSE does not pin threads, though.** That objection is wrong, and [`sse-concurrency.txt`](output/sse-concurrency.txt) is the evidence: ``` === server.tomcat.threads.max=10, clients=40 === emitters open : 40 connector pool size : 10 (max 10) connector active : 0 one broadcast reached: 40 streams clients that read it : 40 ``` **STOMP costs a stateful server.** Sessions live in one JVM's memory. Two instances behind a load balancer do not share subscriptions, so a message published on instance A never reaches a subscriber on instance B — which is the real reason to move to a broker relay, well before you need persistence. ## Should you build this at all? Three cases where the honest answer is no: - **Updates arrive every few minutes.** Polling every thirty seconds is one endpoint, no lifecycle, no proxy configuration, no reconnect logic and no leaked emitters. The right answer more often than it is chosen. - **You want a chat and you have one server and no plan for a second.** Everything in [chapter 4](04-stomp.md) is real work, and the first time you scale out you will be doing it again with a broker relay. Decide about the broker first. - **The client is another service.** SSE's advantage is that browsers implement it. STOMP's advantage is that browsers can speak it. Neither advantage applies service-to-service, and neither protocol gives you flow control, deadlines or a schema — which is what the companion article on RSocket, gRPC and raw WebSocket is about. ## Everything else, one line each - **Behind nginx**, a chunked SSE response is buffered by default and the client sees nothing until the buffer fills. `proxy_buffering off` or the `X-Accel-Buffering: no` response header. - **`EventSource` cannot send an `Authorization` header.** Cookie, or a query parameter you then have to keep out of access logs. - **Heartbeats.** SSE: send a `:` comment every N seconds. STOMP: `setHeartbeatValue` plus a `TaskScheduler`, and both sides must agree. - **Compression.** `text/event-stream` compresses extremely well and Boot's HTTP compression is off by default; `server.compression.mime-types` must list it explicitly. - **Surefire does not discover static nested test classes.** Seven of this module's seventeen tests silently did not run until they were split into top-level classes. Check the count, not the colour. --- Previous: [5. The limits](05-limits.md)