Implémentation de la gestion du cache dans Drupal : une approche terre-à-terre (2/4)
Comprendre l’implémentation des services de stockage du cache.

Après avoir cherché dans la première partie de cette série à comprendre le fonctionnement des différentes approches de la gestion du cache de Drupal, je me suis intéressé à où celui-ci était stocké.
Lorsque j’ai examiné les services qui ont la responsabilité de retourner une réponse en cache, j’ai constaté qu’ils font appel à une dépendance déclarée à travers l’interface CacheBackendInterface. Il s’agit de l’interface qui est implémentée par tous les services de stockage de cache.
Il existe plusieurs implémentations pour la CacheBackendInterface qui sont livrées avec Drupal. Cela veut dire que les données à mettre en cache peuvent être stockées dans différents systèmes de stockage que nous pouvons choisir selon les finalités. Nous allons donc explorer ici les CacheBackendInterface disponibles avec Drupal 10, sans ajout de modules, que j’ai séparés en trois types distincts : les stockages simples, qui sont une implémentation pour un système de stockage donné, les stockages chainés qui sont des wrappers qui peuvent contenir plusieurs stockages simples et les stockages de développement qui ne sont donc pas utilisables en production.
Les stockages simples
- ApcuBackend : C’est l’implémentation pour un système de stockage de cache en mémoire de type clé/valeur stocké directement sous forme de tableaux PHP. S’il n’est pas déjà activé de base sur le serveur, il est disponible via l’activation d’une extension PECL (https://pecl.php.net/package/apcu). C’est un système de stockage considéré comme rapide mais non consistant ou instable car il est limité par la mémoire assignée. Les entrées en mémoire s’effacent au fur et à mesure lorsqu’il sature. Autre point important, il n’est pas partagé avec d’autres utilisateurs machine et il peut, dans certains contextes, ne pas être disponible entre processus PHP sur un même serveur (avec un processus CLI ou entre processus PHP sous Windows, en savoir plus).
- DatabaseBackend : C’est l’implémentation du stockage pour une base de données SQL. Relativement moins performant, il est toutefois plus stable et robuste, il est partagé entre processus et peut même être distribué entre serveurs.
- PhpBackend : C’est une implémentation promettant un cache rapide, car il peut profiter de la puissance de l’OPCache, le cache de l’OPCode, le code précompilé des scripts PHP. On peut le considérer comme stable, car le cache est sauvegardé dans un fichier PHP. C’est ce dernier qui pourra être compilé en OPCode. Le cache est partagé entre processus. Cependant, à l’instar de APCu, il n’est pas distribuable entre différentes machines.
Les stockages chaînés
Le principe des stockages chaînés est de permettre d’utiliser un ou plusieurs stockages de substitution pour pallier les manquements du stockage par défaut.
On peut penser que l’utilisation de ce type de stockage a un coût à la génération du cache (transmis à plusieurs stockages).
- ChainedFastBackend : Ce “Cache Backend” est pensé pour envelopper deux stockages de cache : il appelle en premier un stockage rapide dit non-consistant, pour la performance, puis un stockage consistant si le premier échoue à trouver un cache pour la requête. L’intérêt de cette approche et de fournir un stockage de cache le plus véloce possible mais de contrebalancer l’instabilité (dans la conservation du cache) du premier en permettant à un second cache plus robuste, mais moins performant, de prendre le relai en cas de perte de cache.
- BackendChain : Ce cache peut sembler à première vue semblable au ChainedFastBackend mais il propose quelques nuances par rapport à celui-ci. Il est prévu pour chaîner plusieurs stockages “consistants”, sans limite, afin de bénéficier d’un système de secours ou bien pour utiliser au besoin un cache distribué sur plusieurs machines (ou plusieurs applications) si le cache par défaut ne le permet pas.
On pourrait, comme exemple, envisager sa mise en place dans le cas de l’utilisation d’un load-balancer, qui va redistribuer une charge supplémentaire de requêtes vers un autre serveur. En paramétrant un premier stockage de cache local (par exemple un PhpBackend) puis un second stockage distribué (et pourquoi pas un troisième stockage distribué de secours). Cela permet ainsi de récupérer le cache sur un autre serveur.
Les stockages d’environnement de développement et de testing :
Ces stockages sont prévus pour être utilisés dans un environnement de développement ou de testing.
- MemoryBackend : C’est un stockage en mémoire RAM, utile pour réaliser des tests automatisés.
- MemoryCounterBackend : Semblable au précédent avec la différence qu’il permet de garder des traces sur l’accès au cache.
- NullBackend : Un stockage dummy à utiliser comme stub dans les tests ou pour désactiver le cache en dev en l’activant dans son settings.local.php.
Que peut-on stocker en cache ?
Le contenu peut varier : il s’agit généralement de tableaux de rendu (Render Array API), des objets de type Response, mais cela peut aussi être toute autre chose que l’on décide de stocker manuellement dans notre application pour éviter de recalculer le résultat (requêtes SQL, réponses web API tierces, etc…).
Pour information, avec un DatabaseBackend et un PhpBackend le cache est sérialisé, et peut être stocké en chaîne de caractères ou en donnée binaire. Dans ApcuBackend, les données de cache sont stockées en brut (dans un tableau) directement dans la mémoire.
Pourquoi la base de données est le stockage de cache par défaut de Drupal ?
C’est une question que je me suis posée car je trouvais ce support moins performant que d’autres pour du cache. J’ai pu déduire la réponse en consultant les commentaires disponibles dans les classes implémentant la CacheBackendInterface (qui sont repris dans la documentation en ligne).
De ce que j’ai compris, la réponse est simplement que le stockage en base de données peut être considéré comme un système de stockage plus polyvalent, notamment parce qu’il permet de distribuer le cache entre différents “nœuds web”. Ce qui n’est pas possible avec la plupart des autres systèmes de stockage, considérés comme plus performants et qui sont disponibles dans le core Drupal.
En bref, le cache en base de données est le stockage qui va fonctionner pour la plus grande partie des cas d’usage sans avoir à rien configurer une fois Drupal installé.
A noter que l’implémentation du DatabaseBackend est clairement prévue pour des bases de données relationnelles de type SQL comme MySQL ou PostgreSQL mais si vous vous posez la question, il est tout à fait possible d’adapter cette implémentation pour utiliser des bases de données orientées document ou de type clé/valeur (des modules de contribution le font déjà).
Chez ekino nous recommandons, si possible, l’utilisation de Redis pour le stockage du cache dans Drupal. Cela pour des raisons de performances. Pour vous en convaincre vous pouvez visionner la conférence de Nicolas Perussel (aka @mamoot), CTO PHP à l’agence ekino Bordeaux, “Drupal Performances” présentée lors des DrupalCon 2022 à Prague (31').
Configurer le stockage du cache
Si on souhaite configurer le stockage du cache par défaut il suffit de le déclarer dans le fichier de configuration Drupal (settings.php) :
$settings['cache']['default'] = 'cache.backend.<MY_BACKEND>';
Si la configuration ne vise qu’un type de cache particulier (un cache bin, voire partie suivante), elle ressemblera à :
$settings['cache']['bins']['<CACHE_BIN_NAME>'] = 'cache.backend.<MY_BACKEND>';
Si on souhaite créer un stockage de cache personnalisé, on peut procéder en créant un nouveau fichier de déclaration de services. On l’inclura ensuite à la liste des fichiers de services grâce à la variable $settings du fichier de configuration :
// settings.php, settings.local.php ...
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/my_app.services.yml';
Puis, dans ce fichier, vous pourrez déclarer vos services selon votre convenance. Par exemple, si, pour une raison farfelue, je désire stocker mon cache dans une autre base de données, je déclare un service alternatif :
database_alt:
class: DrupalCoreDatabaseConnection
factory: DrupalCoreDatabaseDatabase::getConnection
arguments: [ 'db_cache', 'custom' ]
cache.backend.custom_database:
class: DrupalCoreCacheDatabaseBackendFactory
arguments: ['@database_alt', '@cache_tags.invalidator.checksum', '@settings', '@serialization.phpserialize', '@datetime.time']
Pour l’exemple, on peut imaginer déclarer la configuration de base de données dans le fichier settings.php avec quelque chose du genre :
$databases['custom']['db_cache'] = [/* db config... */];
Pour faire quelque chose de plus utile, on pourrait décider de déclarer dans ce même fichier un BackendChain :
cache.backend.custom_chain:
class: DrupalCoreCacheBackendChain
calls:
- [appendBackend, ['@cache.backend.php']]
- [appendBackend, ['@cache.backend.custom_database']]
Ou bien encore, si on souhaite utiliser un ChainedFastBackend personnalisé (par défaut cette classe utilise DatabaseBackend comme stockage ‘consistant’ et ApcuBackend comme stockage rapide) :
cache.backend.custom_chainedfast:
class: DrupalCoreCacheChainedFastBackendFactory
arguments: ['@settings', 'cache.backend.custom_database', 'cache.backend.php']
calls:
- [setContainer, ['@service_container']]
Après avoir creusé l’implémentation et le fonctionnement du “cache backend” de Drupal, j’appréhende mieux la façon dont est stocké le cache. Une fois cela en tête, je me suis arrêté sur les bases du fonctionnement de la gestion du cache avec Drupal.
Implémentation de la gestion du cache dans Drupal: une approche terre-à-terre (2/4) was originally published in ekino-france on Medium, where people are continuing the conversation by highlighting and responding to this story.