putyourlightson / craft-blitz

Intelligent static page caching for creating lightning-fast sites with Craft CMS.
https://putyourlightson.com/plugins/blitz
Other
149 stars 36 forks source link

Implement the stale-while-revalidate pattern #381

Closed nickdunn closed 2 years ago

nickdunn commented 2 years ago

I haven't properly thought this through, but as I was implementing stale-while-revalidate (https://web.dev/stale-while-revalidate/) on another site (non-Craft, using nginx caching and background fetching) it got me thinking whether Blitz could use this approach too.

Rather than pre-warming at the point of re-saving an entry, you'd warm on demand when a cache miss is detected. This would need a change to the cache clearing strategy: rather than deleting the cache entry itself, you'd want to somehow mark it as "expired" but retain it (e.g. move it to a stale directory inside the cache on disk, or save under a stale prefixed key name in Redis) so that it can still be served, while Blitz issues a background/queue task to refresh the cache.

This way, you'd prevent a stampede of warming requests being issued when a heavily re-used entry is saved, e.g. primary navigation. This was one of the reasons I ended up not using warming at all for a very large site, as I didn't want to generate a large number of requests unnecessarily. Doing this just-in-time feels like a safer and more sustainable approach for sites that have a very long tail of URLs.

bencroker commented 2 years ago

Yup, a stale-while-revalidate implementation is already on the list for Blitz 4.

nickdunn commented 2 years ago

That's great to know! Thanks. Dare I ask what timescale you're working towards?

bencroker commented 2 years ago

Well that depends a lot on the release date for Craft 4. It's a big feature, but we'll be aiming for some time this year.

bencroker commented 2 years ago

This has been added for the 4.0.0 release, see https://github.com/putyourlightson/craft-blitz/blob/v4/CHANGELOG.md for a working list of changes.

nickdunn commented 2 years ago

Nice, thanks! Does it only work with a warmer, or can it work without based on expiry/stale status?

bencroker commented 2 years ago

Currently only with a warmer. The approach you're describing is complicated by the fact that a common use-case of Blitz is to store cached files in the filesystem (and serve them using server rewrites) or in edge nodes with a reverse proxy (CDN), in which case requests never even reach the plugin. You can think of cache warmers as cache revalidators in Blitz 4. I've yet to land on the exact naming/wording for this, but the idea is that your site will continue serving stale versions of cached pages until the warmer revalidates them. I realise this is a different definition of stale-while-revalidate than what you originally suggested, but it feels like it will work across many more setups and be more robust in the long-term. Let me know if you have further thoughts though, I'd be happy to hear them!

bencroker commented 2 years ago

I thought about this some more and managed to implement it in https://github.com/putyourlightson/craft-blitz/commit/fd17387897913aa22f916425ddf8ff87b9b86cae

As explained, it will work only in cases where the cache is being served by Blitz, but at least it will now be possible to do what you were asking for!

nickdunn commented 2 years ago

Added the ability to revalidate cached pages that have expired when serving cached responses, meaning that a stale-while-revalidate setup can be achieved by disabling the "Clear Cache Automatically" setting

Ooh interesting thanks for revisiting. Just to check I'm understanding this correctly, this implementation:

Or conversely, if this requires "Clear Cache Automatically" to be disabled how do pages expire, must it always be based on a duration and not when entries are edited?

Sorry, the penny hasn't dropped yet!

bencroker commented 2 years ago

Yeah, this is all still in flux, but we're switching how this is controlled to a new "Invalidation Mode" setting which should hopefully make this all easier to grasp. The implementation you describe above should be possible with the "Expire only" value. https://github.com/putyourlightson/craft-blitz/blob/f1705428df6d1fffb0dfa51000c615c02ab6a490/src/templates/_settings.twig#L93-L105

nickdunn commented 2 years ago

Aha! Yes this makes more sense :-)

Can't wait for this one — thank you.

bencroker commented 2 years ago

Released in version 4.0.0.