Skip to content

Instantly share code, notes, and snippets.

@mandrius
Last active March 19, 2018 12:44
Show Gist options
  • Save mandrius/dc9bd62a0859a6c751e6e13e0b7673c2 to your computer and use it in GitHub Desktop.
Save mandrius/dc9bd62a0859a6c751e6e13e0b7673c2 to your computer and use it in GitHub Desktop.
Cache keys
Cache keys are unique identifiers for a specific variation of a thing that is being cached. This provides the cache API with the information it needs to be able to retrieve the specific item from whatever storage is being used for cached data.
When rendering a node (data being displayed) in the teaser view mode (configuration indicating a specific representation), the cache keys would be e.g. ['node', 5, 'teaser']
Cache tags
Cache tags vary the cache based on the data that was used to build the element. For example, entities, users, or configuration.
Using cache tags you could associate some cached data with a specific node. When that node is edited, and the content of it changes, the related cached data becomes outdated and no longer valid.
When thinking about cache tags, ask yourself, "What does this item depend on in order to derive its content?" When those things change the cache needs to be invalidated so we can rebuild the item with the new data.
The node teaser shows the node title, authoring date, author, author's profile picture, and body. The dependencies are the node itself, the user entity associated with the author, the file entity associated with the profile picture and the text format associated with the body field. If any of them change, then the cached HTML for the node 5 teaser must be regenerated. So my cache tags would be: ['node:5', 'user:3', 'file:4', 'config:filter.format.basic_html'].
Data objects should in most cases have a ::getCacheTags() method that returns any tags for that object. So, $node->getCacheTags() would return node:5. Rather than hard coding these, or trying to memorize patterns, use these methods instead.
Example: your content displays the user name, and you want to add the appropriate tags: $user->getCacheTags().
If you are defining your own data model, or using something other than the Entity API or Configuration API for data storage, you should consider adding your own custom cache tags, and then invalidating them at the appropriate times.
Read more about cache tags.
Cache contexts
Cache contexts vary the cache based on contextual information. This could be the day of the week, the current user's last login date, or the value of a setting in the administrative UI. Cache contexts are analogous to the HTTP Vary header.
When determining cache contexts, ask yourself, "Does the thing that is being rendered vary based on contextual information?" For example if permissions, language, user-configured settings, or day of the week change, is this content still going to be valid? If an element varies based on whether or not the current user is authenticated, using a cache context of ['user.role:anonymous'] would instruct the cache to store multiple variations of this object: one for users with the anonymous role, and another for everyone else.
A practical example from Drupal core: the teaser of a node is rendered differently for users in different timezones because the "authored on" date field is displayed using a localized time. So the cache contexts for the teaser would be ['timezone'].
There is a fixed list of contexts provided by core. You can see that list and read more about how to use each context in the documentation.
You can create your own context by defining a new tagged service. Cache contexts are services tagged with 'cache.context', whose classes implement \Drupal\Core\Cache\Context\CacheContextInterface.
Read more about cache contexts.
Cache max-age
Cache max-age varies the cache based on time. For example, "Valid for 1 hour."
Max-age is a either a positive integer expressing a number of seconds, 0, or Cache::PERMANENT. Cache::PERMANENT is the default.
0 means cachable for zero seconds, which is the same as saying not cacheable at all or disabled. Use this to prevent an item from ever being cached.
\Drupal\Core\Cache\Cache::PERMANENT means cache forever, only invalidate based on cache tags.
Read more about cache max-age.
Invalidating cached items
Caches that vary on max-age, tags, or contexts, that are provided by core or another module should already be invalidated at the appropriate times. You can always manually invalidate any of these as needed.
If you define your own custom cache tags, you'll also need to define when they are invalidated. Generally this would be when the data they represent is updated. If your code has $custom_object->save() or $custom_object->update() methods you would trigger something like the following in those methods to invalidate cached items and force them to be rebuilt the next time they are used.
\Drupal\Core\Cache\Cache::invalidateTags(['custom_object:1']);
Or via the cache_tags.invalidator service.
$this->container('cache_tags.invalidator')->invlidateTags(['custom_object:1']);
Note: for the most part you should be using either the Entity or the Configuration systems to store your module's data. Both of those already handle the required invalidation for {entity_type}:{entity_id} and configuration:{config_id} tags.
Debugging Drupal's cache
With your site in development mode, you'll see some additional HTTP headers that show you what is in the context and tags for each page.
Look for the X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags HTTP headers.
Screenshot shows x-drupal-cache-contexts and x-drupal-cache-tags headers in the chrome web inspector. Each contains a list of the Drupal contexts and tags used respectively.
This feature is controlled by the http.response.debug_cacheability_headers: true setting in sites/default/services.yml or sites/development.services.yml depending on your setup. It is off by default, and should stay off on production.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment