CrawlCheck

Findings · 2026-08-20 · By · 0 views

Cache negative lookups too: a miss that is never cached repays forever

A cache that only records successes speeds up the requests that were already fast and does nothing for the ones that hurt.

Scanning the same domain three times in a row, no code change between them, gave three different answers: 36.8s, 20.6s, 12.9s. A single reading would have said “about twenty seconds” and sent us looking for something twenty seconds long. There isn't anything twenty seconds long. The number was an average of a fast path and a slow path, and averaging them hid both.

Time the stage, not the request #

Comparing the three runs stage by stage put nearly all of the variance in three stages, each swinging by seven or eight seconds. Everything else was stable to within half a second.

The part that was actually wrong #

One of those stages asks the Chrome UX Report whether Google holds real-world performance data for the site. For a local contractor the answer is no, and it always will be — CrUX only publishes an origin once it has enough Chrome visits, and a regional business does not get them.

So the honest reading: the scan was spending up to 8.2 seconds to learn nothing. That is fine once. It is not fine every time. It was every time, and the reason is a shape worth recognising.

The cache was read at the top of the lookup and written at the bottom. Every path that failed returned before it reached the write.

The consequence is exactly backwards. A successful lookup — already the fast case, since the data existed and came back — was cached and nearly free from then on. A failed lookup paid for several uncached round trips, and none of their results was stored. The slow path stayed slow, on every scan, permanently.

Why “cache the miss” is not the whole rule #

The tempting fix is to move the write to the end of every path. That is too blunt, because the failures are not the same kind of fact.

Same function, same return, two different lifetimes — because one describes the thing being measured and the other describes the instrument.

We then broke our own rule #

We shipped the fix, and it cached a third thing we had not thought about: a transient “pending” state. That is neither a fact about the site nor a bad credential — it is our request not having finished yet. Cached for 24 hours, it froze the pending state and stopped the retry from ever running again. An intermittent failure became a permanent one, and it stayed that way for six hours.

We corrected it. The general rule, stated properly: cache facts about the thing you measured; never cache facts about your own attempt to measure it.

The result #

After the fix, the field-data lookup went from up to 8,240ms to 3ms once warm. The measurement did not change. We just stopped paying for the same empty answer on every scan.

Three things that transfer #

  1. Time the stage, not the request. A total duration cannot tell you whether you have one slow thing or one occasionally-slow thing, and those need opposite fixes.
  2. Check which branches reach your cache write. If it sits after the happy path, your cache is only accelerating the case that did not need it.
  3. Classify your failures before caching them. “The answer is no” and “we could not ask” look identical in a return value and must never share a TTL.

Every figure above came out of this scanner.

Point it at your own domain and see the same measurements, free.

Scan a domain — free

The main product

Found this on your own site? We fix it for $749.

Scan free to see where you stand. The fix is one site, every finding implemented and re-measured, with a sealed before and after.

Questions this post answers

What is a negative cache?

A stored record that a lookup failed, with its own expiry, so the same failing request is not repeated on every run. A cache that only stores successes re-runs the expensive miss forever.

How long should a miss be cached?

Long enough that the cost stops mattering and short enough that a fix is noticed.

Related findings

How anything measured in this article was measured15client identitiesone second, one address5machine filesapex and www114named agentsresolved from robots.txt24sections scoredreach, read, quoteHow anything measured here was measured15 client identities5 machine files114 named agents24 sections scoredone second, one addressapex and wwwresolved from robots.txtreach, read, quote
No account, nothing installed, and the same sequence on every domain — which is what makes one scan comparable to another. Run it on your own site.

Comments

Comments are read before they appear. Nothing is published automatically, and no account is needed.

Writing about this? Facts, live figures and marks — every number on that page is dated and traceable to a scan.

All findings · The dataset · How the dataset works