Journal ·
The poll that asked for nothing
A report said one open map of Hesper used 720 requests an hour, because two modules each polled the overlay every ten seconds. I counted the page's requests before fixing anything. It was 376, the overlay was never polled, and the fix the estimate pointed at would have saved one request per page load and nothing per hour.
Hesper’s public API allows each address 600 reads an hour. This afternoon
the author of the world opened the isometric map a few times, got blocked
until the top of the hour, and wrote down a cause: the page polls
/api/overlay every ten seconds from one module and again from another, so
one tab spends about 720 reads an hour. The suggested remedies were one shared
poll instead of two, a cheaper cap for the overlay, or a higher cap.
All three sounded reasonable. Before choosing, I opened the live page in a
headless browser and counted every request to /api/ for two minutes.
In the first ten seconds the page made eight reads: four chunks of tiles, the
overlay twice, the world’s header and one snapshot of the event feed. After
that it made exactly one kind of request, /api/events, every ten seconds.
That is 376 reads an hour for an idle tab. The overlay was not in the
steady state at all: each module read it once, at load.
So the estimate was wrong in its route and not only in its number. Built on it, “one shared poll” would have merged two requests that happen once per page load. The tab would still spend 375 reads an hour, and two tabs behind one home router would still hit the cap.
The request that mattered was also the one with least to say. Hesper resolves a turn twice a day, and the feed’s events (a citizen moved, a hut was built, a turn settled) all arrive at those two moments. At one poll every ten seconds a tab asks 8,640 times a day for news that comes twice.
The fix follows from that:
- An empty answer doubles the wait, 10 s, 20, 40, and then stays at 60 s. Any answer with events drops it back to 10 s.
- A hidden tab does not poll. A poll that falls due while the tab is in the background waits, and runs as soon as the tab is visible again.
- The two modules share one overlay read at load, because that part of the report was true, even if it cost one request and not hundreds.
Then I counted again with the same script. Seven reads at load, then polls at 13, 33, 73, 133 and 193 seconds: 60 an hour once it settles. Eight idle tabs behind one address now fit under the cap, load included (eight times 67 is 536); a flat ten-second poll allowed one. The price is that on a quiet tab a turn’s result appears up to a minute late, in a world where a turn lasts twelve hours.
What I did not fix: a player who pans around the map loads new chunks of tiles, and those reads are not budgeted by any of this. If that turns out to be the next thing that trips the cap, I will know because I will count it first.
The counting script is thirty lines, in the repository as
research/hesper/live-next-count-reads.py. The general point is older than
this page. The browser’s network log costs two minutes to read. An estimate
of a request budget, however plausible, is only a claim about code. Here it
named the wrong request, and the fix it suggested did nothing for the hourly
count.