4. Wizzlern
‣ 100% Drupal
‣ Drupal training and consultancy
‣ Front-, backend-development
‣ Site administration and editorial
‣ Advanced subjects
‣ Since 2009
‣ Active in the Drupal community
‣ Visit us at http://wizzlern.nl
4
8. “There
are
only
two
hard
things
in
Computer
Science:
cache
invalidation
and
naming
things.”
Phil Karlton
8
9. Drupal 7 - Page cache
‣ Core: Cache at page level.
‣ Low complexity cache invalidation.
‣ When a page is edited, the whole page cache
is cleared.
‣ When a comment is added, the whole page
cache is cleared.
9
10. Drupal 7 - Contrib cache
‣ Cache Expiration and various Varnish
modules: High complexity cache invalidation:
‣ purge / expire all URLs that contains the changing
content or is referring to it.
‣ High invalidation costs.
10
12. Drupal 8 strategy
‣ High-complexity cache invalidation
‣ Content invalidated instantaneously
‣ Cached permanently
‣ Cache chaining
‣ Rendered data cached at multiple levels:
‣ Page, Block, Entity (node, comment, user, etc.),
View, Menu tree, Text Format filter result, etc.
12
13. Render #cache
13
$build['#cache'] = array(
'tags' => ['block_view', 'config:block.block.recentcontent'],
'contexts' => ['languages:language_interface', 'user.permissions'],
'max-age' => Cache::PERMANENT,
);
Tags: Where the render result depends on.
Contexts: What makes the render result vary.
Max age: The max time the render result is valid.
For cache variations
To invalidate cache
14. Render #cache
‣ tags: Identifiers of rendered content. Used
for cache invalidation.
Example: node:42, user_view,
config:image.style.large
‣ contexts: Contexts by which the content
may vary.
‣ max-age: The maximum time cached
content is valid. Defaults to permanent.
14
15. #cache context
‣ A string that refers to one of the available
cache context services.
‣ Examples:
‣ https://www.drupal.org/developing/api/8/
cache/contexts
15
languages
:type
route
.menu_active_trails
:menu_name
.name
session
theme
timezone
user
.is_super_user
.node_grants
:operation
.permissions
.roles
:role
Contexts may be hierarchical:
parent.child
16. #cache tags
16
class AuthorNameFormatter extends FormatterBase {
public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($items as $delta => $item) {
/** @var $comment DrupalcommentCommentInterface */
$comment = $item->getEntity();
$account = $comment->getOwner();
$elements[$delta] = array(
'#theme' => 'username',
'#account' => $account,
'#cache' => array(
'tags' => $account->getCacheTags() + $comment->getCacheTags(),
),
);
} Cache tags for this render element.
Cache should respond to changes in
account or comment.
Field formatter for comment author name.
17. #cache contexts, etc.
17
class CommentForm extends ContentEntityForm {
public function form(array $form, FormStateInterface $form_state) {
/** @var DrupalcommentCommentInterface $comment */
$comment = $this->entity;
$entity = $this->entityManager->getStorage($comment->getCommentedEntityTypeId())
$field_name = $comment->getFieldName();
$field_definition = $this->entityManager->getFieldDefinitions($entity
$config = $this->config('user.settings');
// In several places within this function, we vary $form on:
// - The current user's permissions.
// - Whether the current user is authenticated or anonymous.
// - The 'user.settings' configuration.
// - The comment field's definition.
$form['#cache']['contexts'][] = 'user.permissions';
$form['#cache']['contexts'][] = 'user.roles:authenticated';
$this->renderer->addCacheableDependency($form, $config);
$this->renderer->addCacheableDependency($form, $field_definition->getConfig($entity-
>bundle()));
Add cache contexts for users
The comment form depends on:
- comment field settings
- user settings
Add cache tags and contexts from the
configuration this form depends on.
18. #cache: keys
‣ #cache ‘keys’ are used for the database row ID.
‣ Most tags are created from ‘contexts’.
‣ Some tags are added.
18
entity_view:block:bartik_main_menu:[languages:language_interface]=en:
[languages:language_url]=en:[route.menu_active_trails:main]=menu_trail.main|:
[theme]=bartik:
[user.permissions]=120570f7a1954c488d57d2aac1dbc0a48b5072160533431c4ec9f19aa7602e86
#cache contexts:
• languages:language_interface
• languages:language_url
• route.menu_active_trails:main
• theme
• user.permissions
Added keys:
• entity_view
• block
• bartik_main_menu
Main menu block ‘cid’
taken from table cache_render
19. #cache bubbling
‣ Cacheability bubbles up during rendering.
‣ Child’s #cache properties are combined into
the parent’s #cache properties.
‣ Helper functions in Renderer class.
19
20. Thought process
20
Am I rendering
something?
yes ['#cache']['contexts']
Does the output
become outdated
after a certain time?
Does the output
vary by
something?
Does the output
depend on
something?
['#cache']['tags']
['#cache']['max-age']
yes
yes
yes
22. Demo: Recent content
A simple list of links to recent content.
Repo: https://github.com/wizzlern/cacheit
22
23. Provided #cache data
‣ Content entities, configuration, block-,
context-, condition-plugins, etc.
‣ Any objects that implement:
CacheableDependencyInterface
23
interface CacheableDependencyInterface {
public function getCacheContexts();
public function getCacheTags();
public function getCacheMaxAge();
}
Renderer::addCacheableDependency($build, $dependency)
24. Demo: Dynamic Ad
An advertisement block with dynamic content.
Repo: https://github.com/wizzlern/cacheit
24
25. Placeholders
‣ Placeholders allow separation of cachable (‘static’)
and non-cacheable (‘dynamic’) content.
‣ Lazy builder is a service.
‣ Builder arguments only integer, float, string, boolean.
25
if ($display->getComponent('links')) {
$build[$id]['links'] = array(
'#lazy_builder' => ['comment.lazy_builders:renderLinks', [
$entity->id(),
$view_mode,
$entity->language()->getId(),
!empty($entity->in_preview),
]],
'#create_placeholder' => TRUE,
);
}
26. Placeholders
‣ Other placeholder data:
‣ css, js header, js footer
‣ Auto placeholdering
‣ When #cache properties are below a threshold
‣ default: max-age: 0, contexts: ['session', ‘user']
‣ Configuration:
renderer.config:auto_placeholder_conditions
26
27. Invalidation process
‣ Cache invalidation per tag
‣ +1 ‘invalidation’ in cachetags table
‣ Calculate the sum of invalidation of all tags.
‣ Compare with ‘checksum’ value in cache_* table.
‣ Stale data remains in the cache_* table until
overwritten by new data.
‣ See DatabaseCacheTagsChecksum class
27
28. Debugging
‣ Check cache metadata at page level:
‣ http.response.debug_cacheability_headers : true
(in sites/*/services.yml)
‣ Check cache metadata at any level:
‣ Renderviz module (still needs love)
‣ Database table cache_render
‣ Test with different user roles !!
28