Cache API

Modern websites are much more dynamic and interactive than 10 years ago, making it more difficult to build modern sites while also being fast

 

Dries Buytaert

Drupal founder and project lead,

President Drupal Association,

Acquia co-founder and CTO

The fastest Drupal ever is here !

The secret ?

Mostly the new caching strategy

How we use Cache API ?

Typically we return

  • render arrays
  • Response

cache tags

cache contexts

cache max-age

Cacheability metadata

Cache tags

Drupal 7

Clear cache bin

Clear cache item

Prefix based invalidation

Invalidate all data for user 200

Drupal 7:

 

Clear the entire cache bin.

Cache tags

Cache tags = data dependencies

Cache tags describe dependencies on data managed by Drupal

 There are only two hard things in Computer Science: cache invalidation and naming things.

 

Phil Karton

What is a cache tag ?

  • Type: string[]
  • Form: thing:identifier

Example

  • node:5 — cache tag for Node entity 5 (invalidated whenever it changes)
  • user:3 — cache tag for User entity 3 (invalidated whenever it changes)
  • node_list — list cache tag for Node entities (invalidated whenever any Node entity is updated, deleted or created, i.e. when a listing of nodes may need to change)
  • config:system.performance — cache tag for the system.performance configuration
  • library_info — cache tag for asset libraries

Drupal 8 cache tags

  • entities — these have cache tags of the form <entity type>:<entity ID>
  • configuration — these have cache tags of the form config:<configuration name>
  • custom (for example library_info)

Invalidating

cache_tags.invalidator service

Cache contexts

Cache contexts = (request) context dependencies

Cache contexts are analogous to HTTP's Vary header.

Why ?

Data is context-dependent variation.

 

  • Some expensive-to-calculate data depends on the active theme: different results for different themes. Then you'd vary by the theme cache context.
  • When creating a render array that shows a personalized message, the render array varies per user. Then you'd vary (the render array) by the user cache context.
  • Generally: when some expensive-to-calculate information varies by some environment context: vary by a cache context.

Example

What ?

A cache context is a string that refers to one of the available cache context services.

Cache contexts are hierarchical.

  • vary by user
  • vary by permission

How ?

Drupal 8 core ships with the following hierarchy of cache contexts:

cookies
  :name
headers
  :name
ip
languages
  :type
request_format
route
  .book_navigation
  .menu_active_trails
    :menu_name
  .name
session
  .exists
theme
timezone
url
  .host
  .query_args
    :key
    .pagers
      :pager_id
  .path
  .site
user
  .is_super_user
  .node_grants
    :operation
  .permissions
  .roles
    :role
  1. no ambiguity: it's clear what parent cache context is based on wherever it is used
  2. comparing (and folding) cache contexts becomes simpler: if both a.b.c and a.b are present, it's obvious that a.b encompasses a.b.c, and thus it's clear why the a.b.c can be omitted, why it can be "folded" into the parent
  3. no need to deal with ensuring each level in a tree is unique in the entire tree

Examples

  • theme (vary by negotiated theme)
  • user.roles (vary by the combination of roles)
  • user.roles:anonymous (vary by whether the current user has the 'anonymous' role or not, i.e. "is anonymous user")
  • languages (vary by all language types: interface, content …)
  • languages:language_interface (vary by interface language — LanguageInterface::TYPE_INTERFACE)
  • languages:language_content (vary by content language — LanguageInterface::TYPE_CONTENT)
  • url (vary by the entire URL)
  • url.query_args (vary by the entire given query string)
  • url.query_args:foo (vary by the ?foo query argument)

Optimizing cache contexts

  • a part of the page is varied by user
  • another part is varied by user.permissions

optimize([user, user.permissions]) = [user]

Results:

What happens when the permissions change ? 

Solution:

  • cache tags
  • max-age

Example:

user.node_grants

max-age = 0

optimize([user, user.node_grants]) =

[user, user.node_grants]

Example:

user.node_grants

max-age = 3600

optimize([user, user.node_grants]) =

[user]

How to discover and create ? 

 - cache.context tagged services

- implements \Drupal\Core\Cache\Context\CacheContextInterface 

or \Drupal\Core\Cache\Context\CalculatedCacheContextInterface

Example

cache_context.route.book_navigation:
    class: Drupal\book\Cache\BookNavigationCacheContext
    arguments: ['@request_stack']
    tags:
      - { name: cache.context}

cache_context (mandatory prefix)route (parents)book_navigation (this cache context's name)

Debugging

X-Drupal-Cache-Contexts header

Don't you see it ? 

Set http.response.debug_cacheability_headers to true in services.yml

Cache max-age

Cache max-age = time dependencies

Cache max-age is analogous to HTTP's Cache-Control header's max-age directive

 

Why ?

Cache max-age provides a declarative way to create time-dependent caches.

 

What ?

A cache max-age is a positive integer, expressing a number of seconds.

Cache max-ages are passed around as individual integers, because a given cache item can only logically have a single max-age.

Examples

  • 60 means cacheable for 60 seconds
  • 100 means cacheable for 100 seconds
  • 0 means cacheable for zero seconds, i.e. not cacheable
  • \Drupal\Core\Cache\Cache::PERMANENT means cacheable forever, i.e. this will only ever be invalidated due to cache tags. (In other words: ∞, or infinite seconds.)

Big Pipe

RefreshLess

Cache API

By Popdan Daniel

Cache API

  • 626