Setting and Clearing Custom Cache Tags in Drupal (v8.8+)
Drupal (v8.8+) Learn how to use Views Cache Tags module along with custom code to control Pantheon Advanced Page Cache.
Pantheon Advanced Page Cache module is a bridge between Drupal cache metadata and the Pantheon Global CDN.
When you turn on this module your Drupal site will start emitting the HTTP headers necessary to make the Pantheon Global CDN aware of data underlying the response. Then, when the underlying data changes (nodes and taxonomy terms are updated, user permissions changed), this module will clear only the relevant pages from the edge cache.
This module has no configuration settings of its own, just enable it and it will pass along information already present in Drupal to the Global CDN.
To take finer grain control of how Drupal handles its cache data on both the Global CDN and internal Drupal caches, you can set and clear your own custom tags. This guide will show you how to do this using a mix of custom code and Views Custom Cache Tags.
Before You Begin
Before starting this guide, you should:
Install and authenticate Terminus
Have an open sandbox slot on your Pantheon account. To follow along with this guide it is best to use the Dev environment of a newly created Drupal site. You could use a pre-existing Drupal site, but some of the details would change.
So that you can easily copy and paste the example commands in this guide, define your site name with a local environment variable. Replace
cache-tags-demo
with a unique site name:export TERMINUS_SITE=cache-tags-demo
Set up a new Drupal site
First, set up a new Drupal site and add the Pantheon Advanced Page Cache module.
Create a new Drupal site from your local command line environment using Terminus:
terminus site:create $TERMINUS_SITE $TERMINUS_SITE "Drupal"
You can replace the second instance of
$TERMINUS_SITE
with a site label.Install Drupal:
terminus drush $TERMINUS_SITE.dev -- site-install -y
The command above modifies the
settings.php
file on the Dev environment.Commit this change in the Pantheon Dashboard or through the command line:
terminus env:commit $TERMINUS_SITE.dev --message="Installing Drupal"
Add and enable the Pantheon Advanced Page Cache module, which is responsible for sending cache metadata to the Pantheon Global CDN:
terminus drush $TERMINUS_SITE.dev -- dl pantheon_advanced_page_cache terminus drush $TERMINUS_SITE.dev -- en pantheon_advanced_page_cache -y
Commit the new code:
terminus env:commit $TERMINUS_SITE.dev --message="Adding Pantheon Advanced Page Cache."
Log in to your newly created site. This command will give you a one-time log-in link for the admin user:
terminus drush $TERMINUS_SITE.dev -- user-login
Turn on full page caching by setting the Page cache maximum age field to "10 min", then clear caches. We can do this from our Drupal site at
/admin/config/development/performance
.You can also make those same changes using Drush via Terminus:
terminus drush $TERMINUS_SITE.dev -- cset system.performance cache.page.max_age 600 -y terminus drush $TERMINUS_SITE.dev -- cr
View HTTP Headers
You can now look at HTTP Headers with the steps above completed.
Make a new article node complete with at least one taxonomy term in the tags field:
Use
curl -IH
to view the headers returned from that page:curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/node/1
The first request you will see in the list is the initial HTML response. All of the subsequent requests for assets like CSS and images happen after this first HTML response kicks things off:
HTTP/2 200 date: Thu, 11 Jan 2018 17:05:01 GMT cache-control: max-age=600, public content-language: en content-type: text/html; charset=UTF-8 etag: W/"1515689631" expires: Sun, 19 Nov 1978 05:00:00 GMT last-modified: Thu, 11 Jan 2018 16:53:51 GMT link: </node/1>; rel="canonical" link: </node/1>; rel="shortlink" link: </node/1>; rel="revision" server: nginx surrogate-key-raw: block_view config:block.block.bartik_account_menu config:block.block.bartik_branding config:block.block.bartik_breadcrumbs config:block.block.bartik_content config:block.block.bartik_footer config:block.block.bartik_help config:block.block.bartik_local_actions config:block.block.bartik_local_tasks config:block.block.bartik_main_menu config:block.block.bartik_messages config:block.block.bartik_page_title config:block.block.bartik_powered config:block.block.bartik_search config:block.block.bartik_tools config:block_emit_list config:color.theme.bartik config:search.settings config:system.menu.account config:system.menu.footer config:system.menu.main config:system.menu.tools config:system.site config:user.role.anonymous http_response node:1 node_view rendered taxonomy_term:1 user:0 user:1 user_view x-content-type-options: nosniff x-drupal-cache: HIT x-drupal-dynamic-cache: MISS x-frame-options: SAMEORIGIN x-generator: Drupal ${MAJOR_VERSION} (https://www.drupal.org) x-pantheon-styx-hostname: styx-fe3-b-3174343232-r3qrq x-styx-req-id: styx-d1a4bdde194dbd2b07eeac64d3ac75bb x-ua-compatible: IE=edge accept-ranges: bytes via: 1.1 varnish age: 0 x-served-by: cache-mdw17331-MDW x-cache: MISS x-cache-hits: 0 x-timer: S1515690301.238914,VS0,VE48 vary: Accept-Encoding, Cookie, Cookie x-robots-tag: noindex content-length: 10497
You can also view headers in a web browser, if you have a browser extension to add the HTTP debugging request header,
Pantheon-Debug
, with a value of1
(here are some extensions for Chrome and Firefox). In an another browser (or a Chrome incognito window or Firefox Private Window), open the article you just created. In your browser's page inspector, you can view the HTTP requests made by the page. You may need to refresh the page to see all the network requests.By clicking on the first request you can see more detailed information like the HTTP headers.
The rest of this guide will show you how to make content changes and inspect the changing HTTP headers, referencing curl -I
output because browser inspector tools have a lot of additional information that would distract from this demonstration. But if you are more comfortable in the browser, you can continue using that incognito window.
For a walk through of how some of these different headers change caching behavior, see our Frontend Performance Guide. For this guide, we're going to focus on Surrogate-Key-Raw
and Age
.
Review HTTP Headers
The Surrogate-Key-Raw
header tell us all of the Drupal elements that comprise the page. Most critically, we see node:1
and taxonomy_term:1
. This tells us that this page contained renderings of those two entities.
The Age
header tells us the number of seconds that the page has been cached. If you curl again you should see the age number go up.
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/node/1
surrogate-key-raw: block_view config:block.block.bartik_account_menu config:block.block.bartik_branding config:block.block.bartik_breadcrumbs config:block.block.bartik_content config:block.block.bartik_footer config:block.block.bartik_help config:block.block.bartik_local_actions config:block.block.bartik_local_tasks config:block.block.bartik_main_menu config:block.block.bartik_messages config:block.block.bartik_page_title config:block.block.bartik_powered config:block.block.bartik_search config:block.block.bartik_tools config:block_emit_list config:color.theme.bartik config:search.settings config:system.menu.account config:system.menu.footer config:system.menu.main config:system.menu.tools config:system.site config:user.role.anonymous http_response node:1 node_view rendered taxonomy_term:1 user:0 user:1 user_view
age: 40
The remaining steps in this guide trim the output of the curl
commands to only show the relevant data.
View some of the headers on the listing page for the taxonomy term (
/taxonomy/term/1
):curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 Surrogate-Key-Raw: block_view config:block.block.bartik_account_menu config:block.block.bartik_branding config:block.block.bartik_breadcrumbs config:block.block.bartik_content config:block.block.bartik_footer config:block.block.bartik_help config:block.block.bartik_local_actions config:block.block.bartik_local_tasks config:block.block.bartik_main_menu config:block.block.bartik_messages config:block.block.bartik_page_title config:block.block.bartik_powered config:block.block.bartik_search config:block.block.bartik_tools config:block_emit_list config:color.theme.bartik config:search.settings config:system.menu.account config:system.menu.footer config:system.menu.main config:system.menu.tools config:system.site config:user.role.anonymous config:views.view.taxonomy_term http_response node:1 node_emit_list node_view rendered taxonomy_term:1 taxonomy_term_view user:1 user_view Age: 0
You will see
node:1
andtaxonomy_term:1
again because this is the first time we have requested the listing page from curl. The age is 0 because the response wasn't cached.Curl again to make the age will go up:
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 age: 15
Make a page node (
/node/add/page
).View the headers:
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/node/2 surrogate-key-raw: block_view config:block.block.bartik_account_menu config:block.block.bartik_branding config:block.block.bartik_breadcrumbs config:block.block.bartik_content config:block.block.bartik_footer config:block.block.bartik_help config:block.block.bartik_local_actions config:block.block.bartik_local_tasks config:block.block.bartik_main_menu config:block.block.bartik_messages config:block.block.bartik_page_title config:block.block.bartik_powered config:block.block.bartik_search config:block.block.bartik_tools config:block_emit_list config:color.theme.bartik config:search.settings config:system.menu.account config:system.menu.footer config:system.menu.main config:system.menu.tools config:system.site config:user.role.anonymous http_response node:2 node_view rendered user:0 user:1 age: 0
Run curl again and the age will increase.
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/node/2 age: 18
Open the edit screen and decide what caching behavior you want for
/node/1
,/node/2
, and/taxonomy/term/1
. (node/1/edit
):Check the age on the three pages:
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/node/1 age: 267
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 age: 256
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/node/2 age: 165
Click the button to save node 1 in your browser and then curl the three pages again:
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/node/1 age: 0
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 age: 0
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/node/2 age: 246
The pages that contained a rendering of Node 1 were cleared. Node 2's page was not.
Considerations
If you add a new node that used taxonomy term 1, you would want the listing page for term 1 to be cleared.
Add a new article and use the same taxonomy term:
Run curl on the taxonomy listing page.
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 Age: 60
The taxonomy listing was not cleared. In order to clear the taxonomy term when a new node is added that uses that term, you need to write custom code.
Clear An Existing Cache Tag
Follow the steps below to add a custom module that uses a hook to clear the cache tag for all taxonomy terms.
Connect to your Dev environment via SFTP.
Open
code/modules
> create a new directory calledcustom_cache_tags
.> ope your newly created folder:Create a new file named
custom_cache_tags.info.yml
and add the following:custom_cache_tags.info.ymlname: Custom Cache Tags type: module description: 'Customized cache tag clearing' core: 8.x
Create a new file named
custom_cache_tags.module
and add the following:custom_cache_tags.module<?php /** * @file * Contains custom_cache_tags.module. */ use Drupal\Core\Cache\Cache; use Drupal\node\NodeInterface; /** * Implements hook_node_insert(). */ function custom_cache_tags_node_insert(NodeInterface $node) { custom_cache_tags_invalidate_all_terms_referenced_by_node($node); } /** * Invalidate the cache for all taxonomy terms referenced by a node. * * */ function custom_cache_tags_invalidate_all_terms_referenced_by_node(NodeInterface $node) { // This code is copied and adapted from taxonomy_build_node_index(). // Only act upon published nodes where this revision is the default // Because only such nodes would appear in the taxonomy listing. if ($node->isPublished() && $node->isDefaultRevision()) { // Collect a unique list of all the term IDs from all node fields. $tid_all = []; $entity_reference_class = 'Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem'; foreach ($node->getFieldDefinitions() as $field) { $field_name = $field->getName(); $class = $field->getItemDefinition()->getClass(); $is_entity_reference_class = ($class === $entity_reference_class) || is_subclass_of($class, $entity_reference_class); if ($is_entity_reference_class && $field->getSetting('target_type') == 'taxonomy_term') { foreach ($node->getTranslationLanguages() as $language) { foreach ($node->getTranslation($language->getId())->$field_name as $item) { if (!$item->isEmpty()) { $tid_all[$item->target_id] = $item->target_id; } } } } } // Insert index entries for all the node's terms. if (!empty($tid_all)) { foreach ($tid_all as $tid) { $cache_tag = 'taxonomy_term:' . $tid; Cache::invalidateTags(array($cache_tag)); } } } }
This code clears all references to every taxonomy term referenced by a new published node.
Enable the new custom module and commit your code:
terminus drush $TERMINUS_SITE.dev -- en custom_cache_tags -y
terminus env:commit $TERMINUS_SITE.dev --message="Add custom_cache_tags"
Clear all caches so that the new hook is detected by Drupal:
terminus drush $TERMINUS_SITE.dev -- cr
Check the age of the taxonomy listing again by curling a few times to test your code.
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 Age: 0
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 Age: 5
Now whenever you add content, the referenced taxonomy term pages are automatically cleared. When you add another article that references term 1, that age should reset to zero.
Make the new article node and use the same taxonomy term:
Run curl again:
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 Age: 0
The age of 0 tells us that adding the new node cleared the cache.
Set a Custom Cache Tag
The code we added clears all references to each taxonomy term every time a node is added that references the term. Clearing caches that broadly might be too aggressive if we are just concerned about listings of our taxonomy term. We can be more targeted in our clearing by adding a module that will set a more specific tag.
Download and enable the Views Custom Cache Tags module:
terminus drush $TERMINUS_SITE.dev -- dl views_custom_cache_tag
terminus drush $TERMINUS_SITE.dev -- en views_custom_cache_tag -y
Commit your code changes:
terminus env:commit $TERMINUS_SITE.dev --message="adding views_custom_cache_tag"
Edit the View that controls taxonomy terms (
admin/structure/views/view/taxonomy_term
) and change the caching settings from "Tag based" to “Custom Tag based". You may have to expand the Advanced column:Use
taxonomy-listing:{{ raw_arguments.tid }}
for the custom tag and then Save the View:Clear all caches to see the change:
terminus drush $TERMINUS_SITE.dev -- cr
Curl the listing page a few times again:
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 Surrogate-Key-Raw: block_view config:block.block.bartik_account_menu config:block.block.bartik_branding config:block.block.bartik_breadcrumbs config:block.block.bartik_content config:block.block.bartik_footer config:block.block.bartik_help config:block.block.bartik_local_actions config:block.block.bartik_local_tasks config:block.block.bartik_main_menu config:block.block.bartik_messages config:block.block.bartik_page_title config:block.block.bartik_powered config:block.block.bartik_search config:block.block.bartik_tools config:block_emit_list config:color.theme.bartik config:search.settings config:system.menu.account config:system.menu.footer config:system.menu.main config:system.menu.tools config:system.site config:user.role.anonymous config:views.view.taxonomy_term http_response node:1 node:3 node:4 node:5 node:6 node_view rendered taxonomy-listing:1 taxonomy_term:1 taxonomy_term_view user:1 user_view Age: 8
Edit the custom module allow
taxonomy-listing:1
to be cleared when a new node is added that references term 1 by changing the code incustom_cache_tags.module
from:$cache_tag = 'taxonomy_term:' . $tid;
To:
$cache_tag = 'taxonomy-listing:' . $tid;
Check that adding a new article clears the taxonomy listing page:
curl -IH "Pantheon-Debug:1" http://dev-$TERMINUS_SITE.pantheonsite.io/taxonomy/term/1 Age: 0
Additional Resources
- Where you set and clear tags will vary greatly based on the needs of your site. See the Drupal.org documentation for how you can set cache metadata directly on render arrays.
- You can also read this blog post from Aaron Wolfe of Capellic on Pantheon Advanced Page Cache in Drupal 7.