Optimisation Drupal : Backends de cache

Une fois optimisé ce qui peut l’être au niveau de la base de données et des accès aux fichiers partagés. Il faut se tourner vers l’application et sa capacité à réduire la charge via des mécanismes de cache pour éviter de solliciter de trop les bases de données.

Nota : Il peut être bon d’effectuer un audit systématique des requêtes lentes en particulier à la recherche de requêtes pour lesquelles il manque des index.

Sur les CMS modulaires comme Drupal la structure de la base de données évolue au fur et à mesure de la création de champs et les vues générées via l’interface se traduisent directement en requêtes SQL à jointures multiples : une fois l’application stabilisée il peut être intéressant d’auditer les requêtes reçues au niveau du serveur pour identifier celles qui filtrent ou trient ou joignent en utilisant des champs (ou combinaisons de champs) sans index.

En effet un champ utilisé selon l’une de ces méthodes : tri ou jointure, occasionnera le balayage « bas niveau » de toute la table à la recherche des valeurs. Ce balayage est infiniment plus lent et génère beaucoup plus de charges sur le serveur que le balayage d’un index prévu pour accélérer de telles opérations sans recours au contenu réel de la table.

Le cache de Drupal

Drupal 7 offre un mécanisme de cache doté d’une API assez complète. Le mécanisme de gestion du cache Drupal inclut un niveau d’abstraction qui permet de définir divers types de support pour le cache. On parlera de backends. Il peut s’agir en particulier de stockage dans des fichiers sur disque, en base de données dans des tables dédiées au cache ou en poussant les éléments à cacher vers un serveur memcache.

En parallèle une notion de conteneurs permet de stocker des éléments relevant d’une même catégorie dans un « backend » donné.

Les conteneurs utilisés à la base par Drupal sont : cache_class_cache_cache, cache_class_cache_bootstrap, cache_default_class, cache_class_cache_form, cache_class_block, cache_class_content, cache_class_filter, cache_class_form, cache_class_menu, cache_class_page, cache_class_pathdst, cache_class_pathsrc, cache_class_uc_price, cache_class_session, cache_class_update, cache_class_users, cache_class_views, cache_class_views_data

Chaque conteneur est utilisé par les appels au cache pour « choisir » le backend de destination.

La configuration se fait dans le fichier settings.php pour chaque site Drupal Il est bien entendu possible, par exemple lors du développement d’un module, de décider d’un nouveau conteneur pour permettre de choisir spécifiquement un backend de destination pour la gestion du cache.

Utilisation de Memcache

Le backend MemCache repose sur l’usage des librairies php-memcache et php-memcached. Par rapport au cache en base de données ou au cache par fichiers, la configuration de Memcache est declaré dans le fichier de settings.php du site.

Celle-ci définit le cache « MemCacheDrupal » comme cache par défaut (‘cache_default_class’ et ‘cache_class_cache’) ainsi que comme cache pour les objets du conteneur ‘form’ (‘class_cache_class_form’).

Des paramètres annexes définissent le comportement global en relation avec l’usage du cache. En particulier la désactivation des hooks lors du rendu de pages cachées peut réduire de façon très importante la charge. La déclaration du (ou des) serveur(s) MemCache permet de connecter Drupal au(x) serveur(s) MemCache. Lorsque plusieurs serveurs sont utilisés les conteneurs sont répartis sur les backends.

La déclaration de plusieurs serveurs MemCache apporte donc une haute disponibilité du service mais la persistance des données cachées n’est pas garantie et les données ne sont pas répliquées ce que n’offre pas la possibilité d’une répartition de cache pour un conteneur donné. L’absence de cache impliquerait une chute de la performance telle que l’information ne puisse être délivrée dans un temps acceptable.

Memcache(d) & Repcache

Pour améliorer la répartition de charge au niveau des caches il peut être intéressant de mettre en place une réplication des données entre les serveurs de cache. Bien entendu cette approche ne devra être envisagée que si l’infrastructure de cache sature et nécessite de grossir.

Repcached de ce point de vue est intéressant. C’est un patch sur le MemCached « vanille » qui permet la mise en place d’une réplication bi-directionnelle entre deux serveurs MemCached. Cette réplication bi-directionnelle assure un système de cache haute disponibilité.

Il convient de l’adosser à un mécanisme d’IP flottante ou de distribution round-robin (dans ce cas les nœuds Drupal ne « voient » plus qu’un seul serveur MemCached au niveau de sa configuration.

Nota : Cette configuration peut également être complétée par un mécanisme de haute disponibilité type pacemaker ou heartbeat

On trouve le patch Repcached sur GitHub : https://github.com/ignacykasperowicz/repcached

Ce patch nécessite de refaire le package memcached. Il ajoute la possibilité dans le fichier de configuration des 2 serveurs MemCached de déclarer le nœud memcache « compagnon ».

Nota : du point de vue infrastructure il faut s’assurer d’un débit réseau suffisant entre les deux serveurs MemCache (Mettre éventuellement un lien réseau dédié).

Au-delà, les besoins de montée en charge nécessitent de mettre en place des mécanismes de synchronisation en cascade cache-maitre → cache-esclaves (Infrastructure typique présentée par Facebook).

Nota : Drupal ne gére pas nativement ce type d’architecture sans un travail d’adaptation du mécanisme d’abstraction permettant de différencier les serveurs à utiliser pour le cache en lecture de ceux en écriture.

Une alternative : Redis

Il peut être envisageable en alternative à MemCache de mettre en place Redis. Il existe un module permettant d’ajouter Redis aux backends possibles pour Drupal. Les fonctionnalités de clustering et de scaling sont natives et permettent de monter au-delà de deux nœuds du point de vue infrastructure.

En conclusion, les backends de cache MemCache et Redis sont disponibles pour Drupal et permettent, moyennant configurations ou adaptations, de soutenir des charges importantes et de monter en charge.

Cependant l’usage qui est fait du cache dépend aussi de l’application et de ses modes de fonctionnement.