Étiquettes de cache
Cache tags = dépendances de données
Les étiquettes de cache décrivent les dépendances des données gérées par Drupal
Pourquoi ?
Les étiquettes de cache fournissent un moyen déclaratif de suivre quels éléments de cache dépendent de certaines données gérées par Drupal.
Cela est important pour un système de gestion de contenu / framework comme Drupal, car un même contenu peut être réutilisé de différentes manières. En d'autres termes : il est impossible de savoir à l'avance où un contenu sera utilisé. Partout où le contenu est utilisé, il peut être mis en cache. Cela signifie qu’un même contenu peut être mis en cache à des dizaines d’endroits. Ce qui nous amène à une citation célèbre. En informatique, il n’y a que deux vrais problèmes : l’invalidation du cache et la nomination. — c’est-à-dire, comment allez-vous invalider tous les éléments de cache où un contenu est utilisé ?
Note : Drupal 7 propose 3 manières d’invalider les éléments de cache : invalider un CID spécifique, invalider en utilisant un préfixe CID, ou invalider tout le contenu du panier de cache. Aucun de ces trois méthodes ne permet d’invalider les éléments de cache qui contiennent une entité modifiée, car il était impossible de le savoir !
Quelle forme ?
Une étiquette de cache est une chaîne de caractères.
Les étiquettes de cache sont passées sous forme d’ensembles (l’ordre n’a pas d’importance) de chaînes, donc elles sont exprimées en string[]. Ce sont des ensembles car un élément de cache peut dépendre de plusieurs étiquettes de cache.
Syntaxe
Par convention, elles ont la forme chose:identifiant — et lorsqu’il n’y a pas de notion de multiples instances d’une chose, elle prend la forme chose. La seule règle est qu’elles ne peuvent pas contenir d’espaces.
Il n’y a pas de syntaxe stricte.
Exemples :
- node:5 — étiquette de cache pour le nœud Node 5 (invalidée chaque fois qu’il est modifié)
- user:3 — étiquette de cache pour l’utilisateur User 3 (invalidée à chaque modification)
- node_list — liste d’étiquettes de cache pour les objets Node (invalidée chaque fois qu’un objet Node est mis à jour, supprimé ou créé, c’est-à-dire lorsqu’il peut être nécessaire de modifier la liste des nœuds). Applicable à tout type d’entité selon le format suivant : {entity_type}_list.
- config:system.performance — étiquette de cache pour la configuration system.performance
- library_info — étiquette de cache pour les bibliothèques d’actifs
Étiquettes de cache courantes
Les données gérées par Drupal se divisent en 3 catégories :
- entités — elles ont des étiquettes de cache de la forme <entity type ID>:<entity ID>, ainsi que <entity type ID>_list et <entity type ID>_list:<bundle> pour l’invalidation des listes d’entités. Les types d’objets de configuration utilisent l’étiquette de cache de l’objet de configuration de base.
- configuration — elles ont des étiquettes de cache sous la forme config:<nom de configuration>
- personnalisé "custom" (par exemple, library_info)
Drupal fournit automatiquement des étiquettes de cache pour les entités et la configuration — voir la classe de base Entity et la classe de base ConfigBase. (Tous les types concrets d’entités et d’objets de configuration héritent de ces classes.)
Bien que de nombreux types d’objets suivent un format d’étiquette de cache prévisible <entity type ID>:<entity ID>, le code tiers ne doit pas s’y fier. Il doit plutôt obtenir les étiquettes de cache à invalider pour un objet unique en utilisant la méthode ::getCacheTags(), par exemple $node->getCacheTags(), $user->getCacheTags(), $view->getCacheTags(), etc.
De plus, il peut être nécessaire d’invalider les caches basés sur des listes dépendantes des données de l’entité en question (par exemple, mettre à jour le HTML rendu pour une liste lors de la création d’une nouvelle entité pour celle-ci) : cela peut être fait avec EntityTypeInterface::getListCacheTags(), qui invalide toutes les étiquettes retournées par cette méthode avec les propres étiquettes de l’objet. Depuis Drupal 8.9 (avis de changement), les entités groupées ont aussi automatiquement une étiquette de cache plus spécifique incluant leur groupe, pour une invalidation plus ciblée des listes.
Il est également possible de définir des étiquettes de cache personnalisées plus spécifiques basées sur les valeurs des objets, par exemple un champ de référence vers un terme pour des listes qui affichent les objets ayant un terme donné. L’invalidation de ces étiquettes peut être placée dans des hooks personnalisés de pré-sauvegarde / suppression d’objet :
function yourmodule_node_presave(NodeInterface $node) { $tags = []; if ($node->hasField('field_category')) { foreach ($node->get('field_category') as $item) { $tags[] = 'mysite:node:category:' . $item->target_id; } } if ($tags) { Cache::invalidateTags($tags); } }
Ces étiquettes peuvent être utilisées dans le code et dans Views via le module fourni Views Custom Cache Tag.
Note : Il n’existe actuellement pas d’API pour obtenir les groupes individuels et les étiquettes de cache plus spécifiques à partir d’un objet ou autre. Cela s’explique par le fait que ce n’est pas l’objet qui décide quelles étiquettes de cache de liste sont pertinentes pour une liste / requête donnée, mais la requête elle-même. Les futures versions du noyau Drupal amélioreront probablement le support intégré des étiquettes de cache pour chaque groupe et les intégreront, par exemple, dans le générateur de requêtes d’entité et Views.
Comment
Configuration
Tout serveur de cache doit implémenter CacheBackendInterface, donc lors de la mise en cache d’un élément avec la méthode ::set(), fournissez le troisième et quatrième argument, par exemple :
$cache_backend->set( $cid, $data, Cache::PERMANENT, ['node:5', 'user:7'] );
Cela stocke l’élément de cache avec l’identifiant $cid de manière permanente (c’est-à-dire stocké indéfiniment), mais le rend sujet à l’invalidation via les étiquettes de cache node:5 ou user:7.
Invalidation
Les éléments de cache basés sur des étiquettes sont invalidés via leurs étiquettes en utilisant cache_tags.invalidator:invalidateTags() (ou, lorsque vous ne pouvez pas injecter le service cache_tags.invalidator : Cache::invalidateTags()), qui prend un ensemble d’étiquettes de cache (string[]),
Note : cela invalide les éléments marqués avec ces étiquettes dans toutes les cellules de cache. Cela est dû au fait qu’il n’a pas de sens d’invalider les étiquettes de cache uniquement dans certains bins, car les données modifiées dont les étiquettes sont invalides peuvent dépendre d’éléments de cache dans d’autres bins.
Débogage
Toutes les informations ci-dessus sont utiles pour déboguer ce qui est mis en cache. Mais il y a une autre chose : supposons qu’un élément soit mis en cache avec des étiquettes de cache ['foo', 'bar']. Alors l’élément de cache correspondant aura une colonne d’étiquettes (en supposant pour un instant un cache de base de données) avec la valeur suivante :
bar foo
En d’autres termes :
- les étiquettes de cache sont séparées par un espace
- les étiquettes de cache sont triées par ordre alphabétique
Cela facilite l’analyse et le débogage des caches !
En-têtes (débogage)
Enfin : il est facile de voir de quelles étiquettes de cache dépend une réponse donnée (et donc quand elle est invalidée) : il suffit de regarder l’en-tête X-Drupal-Cache-Tags !
(C’est pourquoi les espaces sont interdits : car l’en-tête X-Drupal-Cache-Tags, comme beaucoup d’en-têtes HTTP, utilise les espaces pour séparer les valeurs.)
Note : si vous ne voyez pas ces en-têtes, vous devez configurer votre instance Drupal pour le mode développement.
Intégration avec les reverse proxy
Au lieu de mettre en cache les réponses dans Drupal et de les invalider avec des étiquettes de cache, vous pouvez aussi mettre en cache les réponses dans un reverse proxy (Varnish, CDN ...) puis invalider les réponses mises en cache via les étiquettes de cache associées. Pour que ces reverse proxy sachent quelles étiquettes sont associées à chaque réponse, vous pouvez envoyer les étiquettes de cache dans un en-tête.
Tout comme Drupal 8 peut envoyer l’en-tête X-Drupal-Cache-Tags pour le débogage, il peut aussi envoyer un en-tête Surrogate-Keys avec des valeurs séparées par des espaces, comme attendu par certains CDN, ou un en-tête Cache-Tag avec des valeurs séparées par des virgules, comme attendu par d’autres CDN. Et cela peut aussi être un reverse proxy que vous gérez vous-même, pas seulement un service CDN commercial.
En général, il est recommandé que votre serveur web et votre reverse proxy supportent des en-têtes de réponse jusqu’à 16 Ko de taille.
1. HTTP est textuel. Les étiquettes de cache sont donc aussi basées sur du texte. Les reverse proxy peuvent librement représenter les étiquettes dans une autre structure de données. La limite de 16 Ko a été choisie selon deux critères : A) pour garantir que ça marche dans 99% des cas, B) que c’est réalisable pratiquement. Les serveurs web typiques (Apache) et les CDN typiques (Fastly) supportent des en-têtes de réponse jusqu’à 16 Ko. Cela représente environ 1000 étiquettes de cache, ce qui suffit pour 99% des cas.
2. Le nombre d’étiquettes de cache varie beaucoup selon le site et la réponse. Si la réponse dépend de beaucoup d’autres éléments, il y aura beaucoup d’étiquettes. Plus de 1000 étiquettes de cache dans une réponse seront rares.
3. Mais bien sûr, ce guide (~1000 étiquettes / réponse) peut évoluer avec le temps, car A) on voit comment il est utilisé par de plus en plus d’applications réelles, B) on voit que des systèmes l’utilisent ou construisent dessus.
Enfin, tout ce qui dépasse 1000 étiquettes de cache indique probablement un problème plus profond : la réponse est trop complexe et devrait être divisée. Rien ne vous empêche de dépasser ce nombre dans Drupal, mais cela peut demander une configuration manuelle. Ce qui est acceptable pour des cas d’usage extrêmement complexes. Cela peut même se produire pour bien moins de 1000 étiquettes de cache.
Lisez la documentation sur l’utilisation de Varnish avec les étiquettes de cache.
Il est connu que certains CDN supportent l’invalidation / purge basée sur les étiquettes :
CloudFlare
Fastly
KeyCDN
Akamai
Cache interne de page
L’utilisation complète des étiquettes de cache dans Drupal 8 permet à Drupal 8 de fournir un cache interne de page activé par défaut. C’est essentiellement un reverse proxy intégré.