Automattic/batcache — pull request #39 by joehoyle (2014-08-29 20:51:18+00:00)
Title: Stale cache

## Allow serving of stale cache

Typically when chosing a TTL for cache, it's a trade off between performance for the end user and how long you want to be serving out of date content on your page (purgin aside). For high traffic this often isn't an issue, as the percentage of users hitting uncached content is small. 100 visits a minute with a cache TTL of 5 minutes will only result in on in 500 visitors experience a (relatively) slow load time.

The problem with this approach is for low traffic sites. If you have one visitor every hour, it's likely those visitors will always be hitting an uncached page and experiance a slow site. There is not enough visitors to keep the cache "warm". This means the percentage of visitors having a bad experience is much higher. One option would be to increase cache TTL number to something very large, like 2 hours - however this creates likely hood of serving _very_ stale content, and in the event of a traffic spike, one would have benefitted from using a shorter TTL and be able to serve updated content in a more timely manor.

This pull-request aims to make this process a little more robust by passing a "stale" cache to the user loading the site, and then building the page (with any updated content) and pushing it to the cache for the next visit. Varnish calls this a `grace` response, serve an expired item while simultaniously sending a request to the backend to update the cache for the next person.

Nginx doesn't yet offer this functionality, though there is a Bounty to add support: https://www.bountysource.com/issues/972735-proxy_cache_use_stale-run-updating-in-new-thread-and-serve-stale-data-to-all

With this pull request, let's assume `max_age` is set to 15 seconds, and stale_max_age is set to 120 seconds. This will happen from non cache existing:

1. 0 Seconds - User visits, cache `MISS`, user waits for page to be generated
2. 10 Seconds - User visits, cache `HIT`, user is given a page generated at `0 seconds`
3. 40 seconds - User visits, cache `UPDATING`, user is given a page genereted at `0 seconds`
4. 50 seconds - User visits, cache `HIT`, user is given a page genreated at `40 seconds`

With the above flow, the user is given a page within circa 30 milliseconds while still getting the latest content. This is all made possible with the PHP-FPM function `fastcgi_finish_request()`
which is used to push the stale cache item to the user, close the connection and continue generating the page as if it were a `MISS` to repopualte the cache with the updated version of the pag
e.

There are a couple of kinks to work out still, as this is somewhat voodoo.

1. It's not currently possible to set new headers if a stale page has been served, as PHP Core doesn't have much of a concept or consideration for `fastcgi_finish_request()` if you have output your content already, it won't allow the use of `header()`, so Batcache's uses of `sent_headers()` doesn't function.
2. Stale cache serving for redirects / IF-Modified-Since is not currently supported