In GraphQL subscriptions over SSE, what does one multiplexed stream buy over one stream per operation?
answer
- one line each, or one shared line
- who says stop, and where
- results need an operation identifier
- control requests must find the stream
- open the stream before subscribing
basics
~20 sOne held-open response instead of one per subscription. The price is a control channel: the client subscribes and stops through separate ordinary HTTP requests naming the stream, every result must carry an operation identifier, and the stream becomes node-bound state.
solid answer
~50 sIn the simple shape each subscription is its own HTTP request whose response is its own event stream, so a page running a dozen operations holds a dozen responses open. The multiplexed shape opens **one** stream first, has the server hand back an identifier for it, and then sends each subscription as a **separate ordinary HTTP request** carrying the document plus that stream identifier and a client-chosen operation identifier. Every result written into the shared stream is tagged with that operation identifier so the client can route it, and stopping one subscription is another ordinary HTTP request rather than closing anything. What you buy is one open response per client instead of many. What you pay is a real control protocol on top of a transport that had none, an ordering constraint at startup, and a stream that is now state pinned to whichever server instance holds it.
code
json · 5 lines{
"extensions": { "streamId": "s_7f31c2", "operationId": "op-14" },
"query": "subscription OnApplication($req: ID!) { applicationReceived(requisitionId: $req) { id candidateName } }",
"variables": { "req": "REQ-2291" }
}go deeper
Know that both arrangements exist and what separates them: one HTTP response per subscription, or one shared response with results tagged so the client can tell them apart. Recognising the shapes is enough at this level.
Explain the mechanics of the shared arrangement — open the stream, receive an identifier, send each subscribe as its own request, tag every result, stop with another request — and name what that costs compared with simply aborting one response.
Show the operational consequences: control requests that must reach the instance holding the open response, stop-versus-event races the client resolves by dropping unknown identifiers, and the startup ordering bug that only appears once the round trip gets slow.
Own the decision. Argue from concurrent subscriptions per client and the operational surface you are prepared to run, and be explicit that neither arrangement is standardised, so the shape becomes a compatibility contract between your clients and servers.
## The same transport, arranged two ways Server-sent events give a GraphQL server one primitive: a response body it can keep writing into, with nothing coming back the other way. The convention that grew around that primitive has two arrangements, and knowing both — and why the second exists — is the whole of this question. **One stream per operation.** Every subscription is its own HTTP request. The document is in that request, the results come back on that response, and aborting the request ends that subscription and nothing else. There is no correlation problem because there is nothing to correlate: one stream carries one operation's results. **One multiplexed stream.** The client makes a request that opens a single long-lived event stream and receives an identifier for it — a token the server generates and associates with that open response. From then on, subscribing is *not* something that happens on the stream. It is a separate, ordinary HTTP request that carries the subscription document, the stream identifier, and an operation identifier the client picks. The server registers the operation against the open response and returns immediately; results appear later on the shared stream, each tagged with that operation identifier. Stopping a subscription is another separate request naming the same two identifiers. ## What the second arrangement is actually buying One open response per client rather than one per subscription. A job-board recruiter dashboard that watches 14 requisitions holds 14 responses open in the first arrangement and one in the second. Every held-open response is a socket the browser, every intermediary and the server all have to keep alive and account for, and a page that runs many concurrent subscriptions is exactly the case where holding one per operation stops being free. There is a second, quieter benefit: subscriptions become cheap to start and stop. In the first arrangement, changing a filter argument means tearing down a connection and building a new one. In the second it is a stop request and a subscribe request against a stream that never moved. ## What it costs **A control protocol you now have to design and version.** The simple arrangement has exactly two verbs — open, close — and both are HTTP. The multiplexed arrangement adds subscribe and stop as request types with their own bodies, their own error cases and their own compatibility surface. **Demultiplexing.** Results on the shared stream are useless unless the client can tell which operation produced each one, so every payload carries the client's operation identifier alongside the result. The client keeps a map from identifier to handler, and a result for an identifier it has already stopped has to be dropped rather than delivered — stop requests and in-flight events race, always. **The stream becomes node-bound state.** The open response lives in one server process. A subscribe request naming that stream identifier is a *different* HTTP request and can land anywhere behind a load balancer; if it lands on an instance that does not hold the response, it has nothing to register the operation against. So the multiplexed arrangement needs the control requests routed back to the instance holding the stream, which the simple arrangement never needs because request and response are the same exchange. **A startup ordering constraint — and it is the classic production-only bug.** The client must open the stream and *have the identifier* before it sends any subscribe request. A client written to fire both concurrently works fine against a local server where the stream opens in about 3 ms, and fails against production where the same open crosses several hops and takes 200-400 ms: the subscribe request arrives before the stream is registered, and the server rejects it or the client waits for events that will never come. The symptom is a dashboard that is empty only in production, on only some page loads. ## How to choose Count concurrent subscriptions per client and weigh them against the operational surface you are willing to own. One or two long-lived subscriptions per page — an order status, a single requisition's applications — do not justify a control protocol; take the simple arrangement, and get unsubscribe for free from HTTP. A dashboard that fans out across many entities at once, or one where subscriptions start and stop as the user navigates, is where the multiplexed arrangement earns its complexity. And it is worth being honest in the interview that this is a convention question, not a specification question: neither arrangement is defined by any ratified GraphQL document, so the shape a given client and server agree on is a compatibility decision, not a standards one.
- Why do control requests in the multiplexed arrangement have to reach a particular server instance?Because the open response is in-process state. The instance that answered the stream request is the only one holding a body it can write into, so a subscribe naming that stream identifier has to arrive there to be registered against it. Any other instance knows the identifier is not its own and can only reject. That is why this arrangement needs affinity for its control requests, and why the simple one—where the subscription and its results are the same exchange—does not.
- A client's dashboard is empty only in production, on some loads. What would you suspect first?A race between opening the stream and sending the first subscribe. Locally the stream opens in a few milliseconds so the ordering is accidentally correct; in production the open takes hundreds and the subscribe arrives at a server that has not registered the stream yet, so it is rejected or silently drops. The fix is to make the subscribe path await the stream identifier rather than firing concurrently, and to have the server answer an unknown stream identifier with a clear error rather than nothing.
- In the multiplexed arrangement, what happens to events already in flight when a client stops an operation?They can still arrive, because the stop is a separate request that races with events the server has already written. The client must treat the operation identifier as authoritative and discard results for identifiers it no longer holds a handler for. Servers usually mark the operation stopped as soon as the request lands so no further executions run, but they cannot recall bytes already on the wire.
One stream per operation is a separate phone line for each topic. Multiplexing is one line where every message says which topic it belongs to — cheaper to hold, but now you need an agreed way to say "stop telling me about that one".
saying these in an interview costs you the question
- Says the client subscribes by writing onto the shared stream
- Thinks multiplexing is defined by a GraphQL specification
- Forgets results need an operation identifier to be routed
- Assumes any server instance can serve a subscribe request
- Fires the first subscribe before the stream identifier arrives
- Claims multiplexing removes the need for an unsubscribe path