agentsclimarketplace

Drupal caching

Skill gkastanis/drupal-workflow/skills/drupal-caching

Cache bins, tags, contexts, invalidation strategies, and external caching for Drupal 10/11. Use when implementing caching, optimizing performance, or configuring cache backends.From its SKILL.md

Install
npx -y skills add gkastanis/drupal-workflow --skill drupal-caching

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 15 stars15 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

4.8 KB, as published. Nobody here has run it

Drupal Caching

Always use cache metadata on render arrays. Never return a render array without #cache keys. Check for proper cache tag, cache context, and max-age on every render array you create.

Cache Metadata (Render Arrays)

Every render array should include cache metadata:

$build = [
  '#theme' => 'my_template',
  '#data' => $data,
  '#cache' => [
    'keys' => ['my_module', 'block', $id],
    'contexts' => ['user', 'url.query_args'],
    'tags' => ['node:' . $nid, 'node_list'],
    'max-age' => 3600,
  ],
];

Cache Contexts

ContextVaries ByUse When
userCurrent user IDContent per user
user.rolesUser rolesContent per role
user.permissionsPermissionsAccess-dependent
urlFull URLPage-specific
url.pathURL path onlyPath-dependent
url.query_argsQuery parametersFiltered content
languagesCurrent languageMultilingual
themeActive themeTheme-specific
sessionSession IDSession-dependent

Cache Tags

// Entity-based tags (auto-invalidated).
'tags' => ['node:42']           // Specific node.
'tags' => ['node_list']         // Any node list.
'tags' => ['taxonomy_term:5']   // Specific term.
'tags' => ['config:system.site'] // Config object.

// Custom tags.
'tags' => ['my_module:feature_x']

CacheableMetadata (Object-Oriented API)

Use CacheableMetadata to merge cache metadata from multiple sources. This is cleaner than manually assembling #cache arrays, especially in complex render pipelines.

use Drupal\Core\Cache\CacheableMetadata;

// Build cache metadata from multiple sources.
$cache = new CacheableMetadata();
$cache->addCacheableDependency($node);
$cache->addCacheableDependency($user);
$cache->addCacheContexts(['url.query_args']);
$cache->addCacheTags(['my_module:feature_x']);
$cache->setCacheMaxAge(3600);

// Apply to a render array.
$cache->applyTo($build);

// Merge metadata from an access result into the render array.
$access = $entity->access('view', $account, TRUE);
$cache->addCacheableDependency($access);

Lazy Builders

Use #lazy_builder for fragments that vary per user inside otherwise cacheable pages. Lazy builders defer rendering until after the page cache is resolved, so the rest of the page can still be cached.

$build['user_greeting'] = [
  '#lazy_builder' => [
    'my_module.greeting_builder:build', // Service::method
    [$user_id],                         // Arguments (scalars only)
  ],
  '#create_placeholder' => TRUE,
];

The service must implement a build() method returning a render array:

final class GreetingBuilder {
  public function build(int $user_id): array {
    return ['#markup' => 'Hello, ' . $this->loadUserName($user_id)];
  }
}

Cache Invalidation

In services, inject CacheTagsInvalidatorInterface — never use \Drupal::service() calls.

// In a service with injected $cacheInvalidator:
$this->cacheInvalidator->invalidateTags(['node:42', 'my_module:feature_x']);

Cache Bins

BinPurpose
defaultGeneral-purpose
renderRender arrays
pageFull page cache
dynamic_page_cacheAuthenticated pages
discoveryPlugin discovery
dataData processing
configConfiguration
menuMenu trees

External Cache Backends

Redis Configuration

// settings.php
$settings['redis.connection']['interface'] = 'PhpRedis';
$settings['redis.connection']['host'] = '127.0.0.1';
$settings['cache']['default'] = 'cache.backend.redis';

Varnish Headers

// Ensure proper cache headers.
$response->headers->set('Cache-Control', 'public, max-age=3600');
$response->headers->set('Surrogate-Control', 'max-age=86400');

Caching Layers

  1. Internal Page Cache - Anonymous users (built-in).
  2. Dynamic Page Cache - Authenticated users (built-in).
  3. Render Cache - Render arrays (#cache).
  4. Cache Backends - Redis, Memcached.
  5. Reverse Proxy - Varnish, CDN.

Avoiding N+1 Queries

// Bad: N queries.
foreach ($nids as $nid) {
  $node = Node::load($nid);
}

// Good: 1 query.
$nodes = Node::loadMultiple($nids);

Performance Checklist

  • CSS/JS aggregation enabled in production.
  • Redis/Memcache configured for cache backend.
  • No N+1 query problems.
  • Image optimization enabled (WebP, lazy loading).
  • BigPipe enabled for authenticated users.
  • Entity queries use accessCheck() explicitly.
  • Views use caching with appropriate tags.

Keep looking

Skills are one crate of 626,194. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.