Module 05 · Communication patterns · Lesson 1 of 10
Polling and long polling
The simplest way to get updates from a server: ask repeatedly. Why short polling wastes requests, how long polling holds the request open until there is news, what it costs in connections, and when to move to a push protocol.
Why it matters
HTTP is request-response: the client asks, the server answers, the connection is done. Nothing in that model lets a server say "something happened" on its own. Every real-time feature, from notifications to live scores, is a workaround for that gap, and polling is the first and simplest one. Knowing its costs precisely is what tells you when the more complex options in this module earn their keep.
The idea
Short polling asks on a timer: every few seconds the client sends GET /updates?since=... and the server answers immediately, usually with nothing. Latency is bounded by the interval, and most requests are wasted.
Long polling asks once and waits: the server holds the request open until there is an update or a timeout (typically 20 to 60 seconds), then responds. The client immediately sends the next request. Updates arrive within a network round trip of happening, and the request count drops to roughly one per update plus one per timeout.
What long polling costs
Each waiting client holds an open connection and, in thread-per-request servers, a thread. Ten thousand idle clients is ten thousand open connections, which is fine for an event-loop server and ruinous for a small thread pool. Intermediaries (load balancers, proxies) have idle timeouts, so the hold time must be shorter than the shortest timeout on the path, often 30 seconds or less. Each timeout and reconnect is a full HTTP request with headers and possibly a TLS resumption. And ordering is the client's job: it must send a cursor or last-seen id so nothing is missed between one response and the next request.
| Short polling | Long polling | Push (SSE / WebSocket) | |
|---|---|---|---|
| Latency | Up to one interval | One round trip | One round trip |
| Wasted requests | Most of them | One per timeout | None after connect |
| Server state | None | One held connection per client | One persistent connection per client |
| Infrastructure friendliness | Perfect: plain HTTP | Good: plain HTTP, mind idle timeouts | Needs proxies and balancers that support long connections |
| Implementation effort | Trivial | Low | Moderate: reconnects, heartbeats, fan-out |
| Use when | Updates are rare and seconds of delay are fine | Near-real-time with plain HTTP, moderate client counts | Frequent updates, many clients, bidirectional needs |
Making it work well
Hold the request for a fixed window below the infrastructure's idle timeout and return 204 on expiry so the client distinguishes "nothing" from an error. Accept a since cursor and respond with everything after it, so a client that was between requests when two events fired gets both. Add jitter to reconnect timing so a fleet of clients does not re-ask in lockstep after a server restart. On the server, use an event-driven runtime or an async framework, and wake waiting requests from an in-process subscription to whatever publishes the updates rather than having each request poll the database.
Trade-offs
Polling works everywhere HTTP works, needs no special infrastructure and is trivially retried, and in exchange wastes requests (short) or holds connections (long) and gives only server-to-client flow. It remains the right choice for low-frequency updates, for clients behind restrictive proxies, and as the fallback when a WebSocket cannot be established.
In an interview
Use polling as the baseline you then justify leaving: "Start with long polling on GET /updates with a cursor; a 25-second hold keeps us under the balancer's 30-second idle timeout. If we expect tens of thousands of concurrent clients or sub-second updates, move to SSE or WebSockets." Naming the idle timeout and the cursor shows you have operated it.
Check yourself
A long-polling endpoint holds requests for 60 seconds. Clients report occasional 504 errors with no update received. The most likely cause is: