Drupal 8: perf & ops

Cache tags & pluggable asset optimization

wimleers.com
@wimleers
Senior Software Engineer
Office of the CTO, Acquia

This talk is about

  1. Cache tags
  2. Pluggable asset optimization

Drupal 8 ⟹ you cannot apply this today!

Cache tags

Render cache ALL the entities!

“There are only two hard problems in Computer Science: cache invalidation and naming things.”
  • Caching is easy
  • Cache invalidation is hard
  • Poor cache invalidation

    poor performance

    more servers

1. Cache invalidation is hard

Screenshot of page caching being enabled in Drupal 7.

… clears the entire page cache on node save!

Cache clearing in Drupal 7

  1. Clear a specific cache entry:
    cache_clear_all('foo:content:id', $bin);
  2. Clear a coarse set of cache entries:
    cache_clear_all('foo:content:', $bin, TRUE);
  3. Clear all entries:
    cache_clear_all('*', $bin, TRUE);

“How to clear all entries containing node 42?”

Impossible!

Cache tags in Drupal 8

cache($bin)->set($cid, $value, CACHE_PERMANENT, $tags);
  • Something in page cache:
    $tags = array('content' => TRUE);
  • HTML containing title of node 42:
    $tags = array(
      'node' => array(42)
    );
  • Node with two Taxonomy terms:
    $tags = array(
      'node' => array(42),
      'user' => array(314),
      'taxonomy_term' => array(1337, 9001),
    );

Using cache tags in render arrays

$element['#cache'] = array(
  'keys' => array('entity_view', $entity_type, $entity_id, $view_mode),
  'granularity' => DRUPAL_CACHE_PER_ROLE,
  'tags' => array(
    $entity_type . '_view' => TRUE,
    $entity_type => array($entity_id),
  ),
);

+

/**
 * Collects cache tags for an element and its children into 1 array.
 *
 * […] This allows items to be invalidated based on all tags attached
 * to the content they're constituted from.
 */
function drupal_render_collect_cache_tags($element) { … }

Clear caches:

  • deleteTags():    prevents stale content
  • invalidateTags():    allows stale content

2. Caching is easy

Contextual links in Drupal 7

Screenshot of contextual links in Drupal 7.

Contextual links: HTML in Drupal 7



						
  • Embedded in node's HTML
  • Adapt to user's permissions + page

⟹ breaks render cache

Contextual links: HTML in Drupal 8


or


  • Embedded in node's HTML
  • Invisible placeholder!

⟹ compatible with render cache

Contextual links: logic in Drupal 8

Contextual IDs in HTML


+

contextual.js
(If Use contextual links permission.)

+

POST contextual IDs to contextual/render

=

Same HTML for all users!
(Personalization applied later)

Conclusion (cache tags)

Use cache tags precisely (cache invalidation)
  • Tag render arrays
  • Delete tags: prevent stale content
  • Invalidate tags: allow stale content
Personalize via JS, cache client-side (cache filling)
  • Cacheable render arrays
  • Universal truths: data- attributes
  • Personalization: JS + localStorage

Pluggable asset optimization

CSS/JS aggregation in Drupal 7

  • JS minification: none
  • Changing CSS/JS minifier: impossible
  • Asset grouping: dumb
  • Script loader: impossible

Painful mess.

CSS/JS aggregation in Drupal 7

“API”:

  • #aggregate_callback
  • #group_callback

Override the whole, or nothing … or hacks

Incompatibility hell: Omega theme, CDN module …

CSS/JS aggregation in Drupal 8

API:

  • More explicit
  • More building blocks

Identical logic, but restructured

(See d.o/node/2034675)

  1. AssetCollectionOptimizerInterface
    1. AssetCollectionGrouperInterface
    2. AssetOptimizerInterface
    3. AssetDumperInterface
  2. AssetCollectionRendererInterface

Asset services

Override at will!

asset.css.collection_renderer:
  class: Drupal\Core\Asset\CssCollectionRenderer
asset.css.collection_optimizer:
  class: Drupal\Core\Asset\CssCollectionOptimizer
  arguments: [ '@asset.css.collection_grouper', '@asset.css.optimizer', '@asset.css.dumper', '@state' ]
asset.css.optimizer:
  class: Drupal\Core\Asset\CssOptimizer
asset.css.collection_grouper:
  class: Drupal\Core\Asset\CssCollectionGrouper
asset.css.dumper:
  class: Drupal\Core\Asset\AssetDumper
asset.js.collection_renderer:
  …
asset.js.collection_optimizer:
  …
asset.js.optimizer:
  …
asset.js.collection_grouper:
  …
asset.js.dumper:
  …

Asset services: possibilities

  1. Asset dumper: external server (CDN, S3 …)
  2. JS optimizer: UglifyJS
  3. JS collection renderer: LabJS script loader
  4. Groupers: 3rd party data mining ⇒ globally optimal groups
  5. Collection optimizer:
    • new aggregate ⟺ changed mtime
    • use custom architecture entirely!

Conclusion (asset handling)

Override individual asset services
  • Improve individual services (use existing architecture)
  • Or apply your own architecture!

Hope you liked it.

Questions?

wimleers.com/talk/drupal-8-perf-ops

More details & depth:
wimleers.com/talk/really-fast-drupal-8