measure the render path, then remember pictures instead of caching pages

The entry said to measure first and put the number in the commit, so: a plain page
renders in 14µs, a twelve-picture gallery in 1.23ms. Of that, ~102µs per picture
was reading, hashing and decoding bytes the previous request had already read.

Remembering that one fact — keyed by path, size and modification time — brings the
same gallery to 63µs. 19.5× faster, 21× fewer bytes allocated, twenty-odd lines.
After which nothing is slow enough to justify caching whole pages, so ADR-0044
declines the page cache and leaves the parked validity model parked, now with a
measurement rather than an intuition behind its trigger.

That parked model has five axes and was written before any code existed. The
problem it would have been built for turned out to be one repeated file read.

Benchmarks live in internal/web so they measure through the real handler, which is
also what conventions.md wants before any cache goes in the render path. The
invalidation risk has its own test: an edited picture is a different key, so the
memo cannot serve yesterday's dimensions. Everything runs clean under -race, since
the map is read by concurrent requests.
This commit is contained in:
2026-07-31 11:00:37 +06:00
parent ebc8756434
commit 02adf84928
6 changed files with 180 additions and 3 deletions
+19
View File
@@ -595,3 +595,22 @@ bundle an author wants out of the feed has no way to say so; and entries without
titles only, until `summary` is parsed.
Revisit if: someone wants a dated bundle excluded, or one section kept out of the main feed. *That* is the
real trigger for declared types, and it is now a sharper one than "feeds exist".
## ADR-0044 — No page cache; the cost was repeated picture inspection
Date: 2026-07-30 · Status: accepted (the parked cache validity model stays parked)
Decision: do not build a page cache. Instead remember what each picture is — its size and its derivative names
— keyed by path, file size and modification time. Rendering stays request-time with no stored output, no
validity records and no invalidation graph.
Why: measured before deciding, as the entry required. A plain page rendered in 14µs and a twelve-picture
gallery in 1.23ms, of which ~102µs per picture was reading, hashing and decoding bytes already read on the
previous request. Remembering that one fact takes a map behind a mutex and brings the same gallery to 63µs —
19.5× faster, 21× fewer bytes allocated — after which nothing on the site is slow enough to justify caching
whole pages. A validity model with five axes, written before any code existed, would have been built to solve a
problem that turned out to be one repeated file read.
Consequence: cheap — twenty-odd lines, no stored HTML, and the only invalidation question is "did the file
change", answered by the filesystem. Expensive — one map grows with the number of pictures on the site and is
never evicted, which is correct for a single-author site and wrong for an unbounded one; and every future
"cache the page" instinct now has to beat 63µs rather than 1.23ms.
Revisit if: a page render exceeds a few milliseconds after this, or output stops being a pure function of
content — a comment stream, a per-visitor fragment. Then the parked validity model is the right shape, and its
five axes will have consumers instead of guesses.