Build1 publisher2 min readPublished Updated
Polling 100,000 clients at 1 Hz spends 819 Mbps on request headers before any payload
A dev.to post works the header-overhead sum for short polling and lands on 102.4 MB/s of pure framing metadata. The same sum separates polling from a held-open connection, and the three protocols in its title all sit on the same side of it.
The Engineer · Build desk

What happened
- A dev.to post on real-time API design puts the redundant header block on every HTTP/1.1 request at 500 bytes to 2 KB, and models the waste as clients times update frequency times header size.
- Filled in with 100,000 connected clients, one update per second and 1,024 bytes of headers, the sum gives 102.4 MB/s, about 819.2 Mbps, before any application payload moves.
- The post itemises the per-request compute alongside the bytes: O(N) ASCII header parsing, context switches between proxy threads and the kernel network stack, and short-lived allocations that trigger GC pauses.
- Long polling adds connection churn, which the post says brings TLS 1.3 handshake amplification and L4 source port exhaustion on ingress load balancers capped near 65,535 ports per outbound IP.
- WebSocket, in the post's trace, opens as an ordinary HTTP/1.1 GET carrying Upgrade and Sec-WebSocket-Key, gets a 101 Switching Protocols reply, then carries masked binary frames.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint At 100,000 concurrent clients the first thing to run out is address space, not CPU: one ingress source address covers about 65,535 ports, so the churn model needs at least two before anyone tunes a thread pool.
- decision The header sum settles one question, whether to hold the connection open. Picking among WebSocket, SSE and gRPC-Web needs different evidence, because all three drive the per-request header term to zero.
- cost The wasted bytes are client-sent request headers, so the bill lands on ingress capacity and L7 parsing at the proxy, and each response adds its own header block on the way back out.
The post picks H = 1,024 bytes, the middle of its own 500-byte-to-2-KB band [1]. Run the ends instead. At 500 bytes, 100,000 clients at 1 Hz produce 400 Mbps of header traffic; at 2,048 bytes, 1.638 Gbps [1]. The spread is a factor of four, and what moves you along it is mostly the size of the Cookie header you set. Session cookie size is therefore a capacity-planning input. I have never seen it in a capacity plan.
For 819.2 Mbps to be your number and not the post's, 100,000 clients have to be connected at once, each has to issue a request every second, and the payload has to be small enough that 1 KB of headers dominates it [3]. On a 40-byte price tick, headers are 96 percent of the bytes on the wire; on a 50 KB document sync, 2 percent [5].
The same N times f gives the arrival rate: 100,000 requests per second at the proxy tier [8]. Each one pays the parse-and-allocate path the post itemises, which is where the garbage collector shows up in a latency histogram [4].
The other half of the comparison sits in the frame diagram. The base WebSocket frame header is a FIN bit, three reserved bits, a 4-bit opcode, a MASK bit and a 7-bit payload length [7]. That is 16 bits, two bytes [2]. Against 1,024 bytes of request headers per update, the server-to-client direction costs 512 times less per update [3]. Client-to-server frames are masked [7], so they carry a key on top of the two bytes.
The sum penalises per-request framing, and any channel that stays open removes it, so the same reasoning recommends SSE and gRPC-Web exactly as strongly as WebSocket [7]. The supplied text covers short polling, long polling and the WebSocket upgrade, and breaks off inside the frame diagram before reaching the other two [10]. The stated requirement is latency: to get sub-10ms delivery across distributed client boundaries, the post writes, infrastructure engineering must "abandon state-less request-response loops in favor of long-lived full-duplex framing channels" [8].
What to watch
- A measured distribution of your own Cookie header would settle whether H is nearer 500 bytes or 2 KB.
- A port-exhaustion counter on the ingress tier would show whether the connection table fails before the CPU does under burst.
- Whether real state changes arrive at 1 Hz or far less often, since empty polls are what make the 819.2 Mbps pure waste.