0%

When the Server Has News

The Scoreboard That Polls

On quiz night the news happens on the server: the scorekeeper sends a new score, and the server is the first to know. A viewer’s program cannot hear that news, because with a client only hears from a server when it asks. So the simplest plan is to ask on a timer. Every two seconds the viewer sends a request for the current scores, and the server answers with whatever it has. This is called polling.

Here is what that looks like for the first ten seconds, with one score change at 7.5 seconds:

seconds   0     2     4     6     8     10
          |     |     |     |     |     |
polls     P     P     P     P     P     P
                                 ^
                                 score changes at 7.5

The viewer polls at 0, 2, 4, 6, 8 and 10 seconds. The polls at 0 to 6 seconds find nothing new. The score changes at 7.5 seconds, and nobody hears about it until the poll at 8 seconds asks. That change reaches the viewer half a second late, and the poll at 10 seconds is empty again.

Simulate one hour

One change, half a second late, says little. To see what polling costs over a whole evening, the program below simulates an hour with twelve score changes at fixed times, given in seconds since the start. It needs no network and no clock: every time is a number in a , so it prints the same result every time you run it. Run it:

Try it

It prints polls per hour: 1800, polls that carried news: 12, polls with no news: 1788 and average lateness: 1.0 seconds.

Line by line: an hour has 3600 seconds, so a poll every interval seconds makes 3600 // interval polls. The works out how long each change waits. Polls happen at whole multiples of the interval, so change % interval is how far past the last poll the change happened, and interval minus that is how long it waits for the next one. The first change, at 120.25 seconds, is 0.25 seconds past the poll at 120, so it waits 1.75 seconds for the poll at 122. The outer % interval covers a change that lands exactly on a poll: it waits 0 seconds, not a whole interval. total_wait adds up every wait, and the last line divides by the number of changes.

Each change falls between a different pair of polls, so twelve polls carry news, one change each, and every other poll comes back empty.

Why about half the interval

A score change can happen at any moment between two polls. Right after a poll, it waits almost a whole interval; right before one, almost nothing. Averaged over many changes, the wait comes to about half the interval: about one second for a 2-second interval.

The twelve times in the list were chosen so that the simulation shows that average exactly. They come in pairs that sit the same distance on either side of a multiple of ten seconds, 120.25 and 419.75 for example, so for a 2-second or a 10-second interval the two waits of a pair add up to exactly one interval. Real score changes only come close to the average over a long evening.

The simulation also leaves something out. Each request and each reply takes time to cross the network, as you estimated in Networks with Python, and that delay comes on top of the wait for the next poll. So a real viewer is a little later than these numbers say, never earlier.

The same program with a poll every 10 seconds. What does it print?

score_changes = [120.25, 419.75, 661.5, 908.5, 1332.75, 1707.25, 2043.5, 2296.5, 2614.25, 2955.75, 3220.75, 3489.25]
interval = 10
polls_per_hour = 3600 // interval
total_wait = 0
for change in score_changes:
    wait = (interval - change % interval) % interval
    total_wait += wait
print(f"polls per hour: {polls_per_hour}")
print(f"polls that carried news: {len(score_changes)}")
print(f"polls with no news: {polls_per_hour - len(score_changes)}")
print(f"average lateness: {total_wait / len(score_changes)} seconds")

Every poll costs a request

A poll is not a small thing. Each one is a complete HTTP request and a complete response on a , with a request line, headers, a status line and more headers, exactly the bytes you wrote by hand in HTTP with Python. Even the cheapest empty poll, a request answered with a bodiless 304 as in that course’s chapter on caching, still sends a whole request head and receives a whole response head. Here is one, trimmed to the lines that matter here:

GET /scores HTTP/1.1
Host: quiz.test
If-None-Match: "v12"

HTTP/1.1 304 Not Modified
ETag: "v12"

The server answers almost every one of those requests with “nothing new”. With a 2-second interval, one viewer sends 1,800 requests an hour to hear about twelve changes. A thousand viewers send 1.8 million.

You halve the polling interval from 2 seconds to 1 second. What happens?

Why do almost all polls carry no news?

Polling forces a choice between lateness and wasted requests, and no interval gives you both a fast scoreboard and a quiet server. The next lesson looks at two ways HTTP itself was stretched to do better.