Contact support

We reply by e-mail, usually within two days.

Google reCAPTCHA checks this submission against abuse; data is sent to Google. The script loads only once you open this form.

← All posts

The cache that lives in memory

Close to a fifth of the counter images this service draws no longer change anything. A mouse-tracking script from the previous generation reloads the image on every movement, and since those requests stopped incrementing anything, two of them in a row produce the same picture, byte for byte.

An obvious candidate for a cache. The interesting decisions were where to put it and what to key it on.

Not on the disk

This runs on a Raspberry Pi, and the disk is an SD card. Caching drawn images to it would have meant roughly 70 MB of writes a day, in exchange for a measured saving of 1.6 minutes of CPU time a day.

That is a bad trade. SD cards fail from writing, and the thing being bought is a rounding error on a machine that is not CPU-bound. So the cache lives in /dev/shm, which is tmpfs, which is RAM. Measured there: 0.027 ms to read, 0.036 ms to write, 1.9 GB free.

Everything disappears on reboot. For a cache that is not a loss, it is the normal case.

Redis runs on the same Pi. It was not used, for a reason that has nothing to do with technology: it belongs to a different application and is password-protected for it. Sharing one instance would tie two unrelated services together, so that a bad day for one becomes a bad day for the other.

The key contains the numbers

The usual cache key is an identity plus an expiry, and the expiry is a bet: nothing important changed in the last five minutes. For a counter, the thing that changes is exactly the thing on display.

So the displayed values go into the key. If a number changes, it is a different key, and the image is drawn fresh. The bet disappears rather than being made carefully. Every counted visit increments at least the lifetime total, so no counted visit can be served a stale picture. The expiry that remains is only an upper bound for the values that are not in the key — weekly, monthly and yearly figures.

What had to be checked first

A cache is only safe if the thing it caches is a function of its key. That is a claim about every design, and it was checked rather than assumed:

  • no design uses rand(), mt_rand(), shuffle() or uniqid()
  • no design reads the clock more finely than by the hour
  • the tracking parameters from the old script are read nowhere in the tree

That last one produced the best failure of the whole exercise. While those parameters were still part of the key, every request carried slightly different mouse coordinates, so every key was unique. The cache ran for a full day and recorded 13 hits. It was working perfectly and doing nothing, which is the hardest kind of broken to notice.

And if anything here fails — unreadable directory, full disk, corrupt entry — the image is simply drawn the normal way. A cache must never be the reason a counter does not appear.

Advertisement