Cache Contexts and Tags: Advanced Cache Handling in Drupal 8
This article at a glance -
I previously covered the fundamentals of how caching works in Drupal 8, including what the two core caching modules do and what cache tags, contexts, and max-age are for. If you're familiar with those things, then this post is for you; otherwise, check out my previous article and get up to speed before we dive into a slightly more in-depth topic: Figuring out how you should set up caching on your site.
If you're using a simple Drupal installation with no custom code and with well-maintained contributed modules, Drupal's Internal Page Cache and Dynamic Internal Page Cache modules will likely cover your caching needs. This article focuses on some more complex and custom scenarios which, nonetheless, come up with some frequency.
The Guiding Principle
Perhaps the most frequent issue custom code has when it comes to caching is that it doesn't account for caching at all. This isn't ideal if you want to take advantage of Drupal's caching system to optimize your site's speed, and it points to one principle which can be tricky to learn and is critical to master: If you write custom code, always think about its caching implications. Always.
Often, the implications will be minimal, if there are any at all. It's important not to assume that every bit of custom code will cache perfectly on its own - that's a mistake that could lead to something being cached either for too long or not at all.
When to Disable the Cache
Most everything Drupal renders as output (to a web browser, to a RESTful web service, etc.) can be cached. However, sometimes the development time to ensure that your custom code handles caching precisely outweighs the performance benefits that caching might provide. So the first question when you're writing custom code is, should this be cached at all?
If you're building something like an administrative page that only a few users with special permissions will ever see, it may not be worth the time and effort to make sure it is cached perfectly, especially if the rules for doing so would be complicated. For these scenarios, Drupal has a "page cache kill switch" that can be triggered in code:
Calling the kill switch will stop both the page cache and the dynamic page cache from doing any caching on that page.
This should be used with caution though, and only as a last resort in situations where figuring out proper caching logic isn't worth the time. It should never be used on a page which is expected to be viewed by a large number of your website's visitors.
Rules for Cache Contexts
You should consider using a cache context if your content should look different when displayed in different situations. Let's look at a couple scenarios that benefit from using a cache context:
Say you have a site which can be accessed from two different domains and you want to display something a little different depending on which domain someone is looking at. Perhaps the site's logo and tagline change a little. In this case, the block containing the logo and tagline should be given the url.site context. With this context in place, Drupal will cache a separate version of the block for each domain and will show each domain's visitors the appropriate one.
Or, perhaps a block contains a bit of information about which content the currently logged-in user has permission to edit. This sounds like an excellent case for using the user.permissionscontext to indicate to Drupal that the block is different for each possible combination of permissions that a user might have. If two users have the same permission, the same cached version can be used for both of them.
There are many other contexts are available as well; take a look at the full list to see if one or more of them is applicable to your code.
Rules for Cache Tags
Cache tags are probably the most important caching mechanism available to custom code. Drupal includes countless cache tags which can be used to invalidate a cache entry when something about your site changes, and it is also very easy to create your own cache tags (which we'll get to in a minute). For now, I'm going to focus on some of the cache tags Drupal has by default.
Say you're creating a page which shows the top five most recently published articles on your site. Now, Drupal sites can often make use of the Views module for this sort of thing, but depending on your exact requirements Views may not be the best approach – for instance, maybe part of the content has to come from a remote service that Views can't readily integrate with. The most obvious tags needed for this page are the tags for the specific pieces of content that are being shown, which are tags in the format of node:<nid>, for instance, node:5 and node:38. With these tags in place, whenever the content gets updated, the cache entry for your page gets invalidated, and the page will be built from scratch with the update information the next time somebody views it.
But that's not all there is to think about. Perhaps this page also shows what categories (using a taxonomy structure) each article is in. Now, the articles each have an entity reference field to their categories, so if a user changes what categories the article is in, the relevant node:<nid>tags already added to your page will get cleared. Easy enough. But what if somebody changes the name of the category? That involves editing a taxonomy term, not the article node, so it won't clear any node:<nid> tags. To handle this situation, you'd want to have appropriate taxonomy_term:<id> tags. If an article with ID 6 has terms with IDs 14 and 17, the tags you'd want are node:6, taxonomy_term:14, and taxonomy_term:17, and you'll want to do this for every article shown on your page.
Fortunately, most of the time, you don't need to worry about the specific tag names. Nodes, terms, and other cacheable objects have a getCacheTags() method that gets exactly whatever tags you should use for that object.
These are all simple entity-based tags, but there are many more available as well. There are tags for when various aspects of Drupal configuration changes as well as for things such as when certain theme settings get changed. Unfortunately, since the available cache tags vary from site to site, there isn't a ready-made list of them available for you to use as a reference. You can, however, look at the "cachetags" table in your Drupal database to see a list of all the tags that have been invalidated at least once on the site. This will be pretty minimal if your site is brand-new, but as people use the site it will start filling up.
The basic idea of tags is this: If you render something on a page, and there's a chance that something displayed on it might change in the future, there should be an appropriate tag in place to watch for that change.
This is a big topic, but it looks like we're out of time for today. Next time, we'll delve a bit deeper into cache tags by seeing how to create custom ones that perfectly fit your site's needs and will also cover how to use max-age, including one important gotcha that makes them more complicated than they look. You can check that one out here.