How does the per-connection cost of a held Server-Sent Events response compare with a WebSocket connection at 200,000 viewers?
answer
- the same socket underneath both
- buffers dominate the per-viewer cost
- overhead differs per event, not per connection
- encoded binary costs about a third more
- who re-opens after a drain
basics
~20 sAt the connection level they are nearly the same: one socket, TLS state and send buffers per viewer either way. The real differences are per-message overhead, the encoding tax on binary payloads, and how each side re-establishes after a drop.
solid answer
~40 sBoth transports hold one connection per viewer, with the same kernel and TLS state and the same per-connection send buffers, so "an event stream is cheaper because it is only HTTP" is wrong at the level that dominates a fan-out tier. What genuinely differs is smaller and more specific: the event stream spends field names and line breaks per event where the socket spends a small binary header, and a text body forces binary payloads to be encoded, costing roughly a third more bytes plus the work on both ends. The other real difference is operational — when a node drains, every conforming stream client re-opens on its own, whereas socket clients re-open only through code you wrote. Size the tier on connections times bytes per second times fan-out, not on the protocol name.
go deeper
Remember that both transports hold one connection per viewer, so neither is obviously cheaper; the difference is what rides on top, not what the connection costs.
Explain the per-message differences — field names and line breaks against a small binary header, and the encoding tax on binary payloads — and why they scale with event rate.
Size a tier out loud: per-connection memory, buffer sizing, fan-out bytes at the busiest minute, and the concurrency model, then say honestly that the transport is not the lever.
Decide what the organisation is willing to operate: a fan-out tier's capacity model, its drain behaviour and its re-open wave are costs a lead signs up to long before the first busy morning.
## What both cost, which is nearly the same For a board watched by two hundred thousand devices, the cost that dominates is the cost of *holding a connection*, and that is almost identical for the two transports: - **One transport connection per viewer**, with its kernel socket and its share of the connection table. - **TLS session state** per connection, which does not care what rides above it. - **Send and receive buffers** per connection, usually the largest per-viewer number in the process. - **An entry in whatever registry fans out to it** — the structure your service walks when an event must reach everyone on a mountain. - **A file descriptor and a place in the event loop or worker model** that services it. None of those is cheaper because the bytes above them are HTTP lines rather than binary messages. A candidate who says "the event stream is lighter, it's just HTTP" has the arithmetic backwards: it is the same socket. ## Where they genuinely differ | Cost | Server-Sent Events | WebSocket | |---|---|---| | Connection state | one socket, TLS, buffers | the same | | Per-event overhead | field names plus line breaks | a small binary header | | Binary payloads | must be encoded, roughly a third more bytes | carried natively | | Upstream traffic | its own request each time | free on the open connection | | Re-establishing after a drop | the client does it | your code does it | Those differences matter in proportion to message rate. At one event per second per viewer, the per-event overhead is noise next to the payload. At hundreds of small events per second, or on a feed of encoded binary, it stops being noise and becomes the argument. ## The accounting that actually decides the tier 1. **Concurrent connections per node.** Start from the memory each connection costs in your runtime, including the buffers, and divide the node's usable memory by it. This number is the same for both transports, which is why it is the right place to start. 2. **Bytes per second per node.** Multiply event rate by event size by the number of connections that must receive it. Fan-out, not connection count, is what saturates a node on a busy morning when every lift changes state at once. 3. **The concurrency model above the socket.** A model that dedicates a worker to each in-flight response costs far more per held connection than one that multiplexes many onto a few threads. This factor swamps the protocol difference and is the first thing to check. 4. **The re-open wave after a deploy.** When a node drains, every connection it held comes back somewhere. Both transports face this; the difference is who implements the coming back. ## The operational difference, which is the interesting one Draining a node is where the two diverge in a way that shows up in an incident review rather than a capacity spreadsheet. - With **Server-Sent Events**, a drained connection is simply a response that ended, and a conforming client re-opens it by itself, presenting the last event `id` it saw. The behaviour exists whether or not anyone on your team thought about it. - With a **WebSocket** connection, a drained connection is a closed connection, and what happens next is entirely code someone wrote. If nobody wrote it, devices sit dark until a human reloads them — the classic discovery made during the first rolling deploy after launch. That is not an argument that one is cheaper; it is an argument about which costs are *visible in advance*. The stream's re-open behaviour is a property of the protocol. The socket's is a property of your codebase, and it has to be budgeted, built and tested. ## What to say when asked the number Give the shape rather than a memorised figure, and be explicit about what you would measure: - Per-connection memory in your runtime, measured with a real payload rather than assumed. - The buffer sizing per connection, since a generous default multiplied by two hundred thousand is often the whole budget. - The event rate and fan-out at the worst minute of the day, not the average. - Whether one device holds one stream or several, because the count that matters is connections, not users. Then say the honest conclusion: **the transport is not the lever.** The concurrency model, the buffer sizing and the fan-out volume decide how many devices a node holds, and both transports live or die on the same three numbers. Choose between them on direction, on what you must build, and on the path — not on an imagined per-connection saving that does not exist.
- Why is an event stream not cheaper per connection than a socket?Because the expensive parts sit below the protocol. Each viewer costs a transport connection, TLS state, send and receive buffers and an entry in your fan-out registry, and none of those change because the bytes above them are text lines. The differences that do exist are per message and per payload, so they scale with event rate rather than with the number of viewers.
- What dominates memory in a fan-out tier holding hundreds of thousands of connections?Per-connection buffers, usually by a wide margin, followed by whatever you retain per connection in application state and any message copies held while fanning out. Sizing buffers generously is invisible at a thousand connections and decisive at two hundred thousand. Measure a real connection under a real payload instead of trusting a default.
- Does draining a node cost the same on both transports?The traffic cost is the same — every connection comes back somewhere. The engineering cost is not. A conforming event-stream client re-opens on its own and presents the last event id it saw, while a socket client re-opens only if someone implemented it. Teams routinely discover the difference during their first rolling deploy.
saying these in an interview costs you the question
- Says an event stream is lighter because it is only HTTP
- Sizes a fan-out tier by protocol rather than by buffers and fan-out
- Ignores that a socket client re-opens only through code you wrote
- Forgets that binary payloads must be encoded into a text body
- Quotes a connections-per-node figure without naming the concurrency model