Optimisation Drupal : Tuning Drupal

Si la mise en place d’un pooler de connexions peut se faire sans aucune modification côté Drupal il n’en va pas de même pour toutes les optimisations. En particulier la mise en place de mécanismes de cache peut nécessiter des adaptations voire des évolutions de code au niveau de l’application.

Nous avons vu en introduction que dans le cadre des RSE, qui est le plus complexe, les utilisateurs sont authentifiés et le contenu de ces pages dépend de l’authentification, et ce à plusieurs niveaux.

Dans le cas d’un Drupal standard les utilisateurs sont associés à des rôles. Ces rôles donnent accès à des fonctionnalités différentes ou à des types de contenus différents.

C’est un premier niveau de différenciation qui impacte le contenu des pages.

Mais dans les approches RSE Drupal est enrichi à l’aide d’Organic Groups et de ses sous-modules.

Ces modules ajoutent une notion de groupes d’utilisateurs pour lesquels des sous-ensembles de contenus d’un type donné sont accessibles en lecture et/ou en écriture.

Pour un utilisateur donné les pages sont donc conditionnées par les rôles ET les groupes auxquels il appartient.

Au résultat les pages « de synthèse » (pages d’entrée des groupes, tableaux de bord, vues de remontées d’actualités…) sont les plus compliquées, constituées de blocs d’affichages dont les contenus varient en fonction du contexte.

Sans dispositif complémentaire Drupal ne cache pas ces contenus pour éviter qu’un utilisateur ne récupère un cache généré par et pour un autre.

Au résultat ces blocs d’affichage ne sont pas cachés du tout ce qui :

  • limite fortement l’efficacité du cache
  • occasionne des charges plus importantes au niveau du CPU sur les frontaux web et CPU+I/O sur les bases de données.

Des travaux ont été faits par la communauté Drupal pour répondre à cette problématique.

Module « Per user cache views »

Ce module « PUC » (Per user cache views) ajoute une amélioration aux mécanismes de cache du module Views en déclinant les caches par utilisateur au lieu de les gérer globalement pour « tous les utilisateurs ».

Cela permet de cacher des vues même si leurs résultats varient en fonction des droits d’accès aux différents nœuds ou en fonction d’autres informations spécifiques aux utilisateurs.

En déclinant les caches « par utilisateur » la génération des caches se fait par utilisateur « pour lui même ».

Les faiblesses de cette approche sont liées à la duplication de l’information :

  1. les blocs d’informations partagées sont dupliquées ce qui occasionne une augmentation de la volumétrie nécessaire au niveau des données cachées
  2. chaque utilisateur générant son propre cache les temps de primo-chargement des pages seront importants pour tous les utilisateurs.

L’impact en termes de ressenti utilisateur se sentira au niveau de la fluidité en cas d’utilisation importante. Mais les premières pages chargées seront toujours lentes et ce pour tous les utilisateurs (on pense en particulier à la connexion initiale le matin).

La page du module PUC : https://www.drupal.org/project/views_puc

Module « Authcache »

Ce module Authcache tente de gérer les caches en les associant à des profils de droits.

C’est un module qui nécessite en général des précautions et des adaptations à cause de la complexité de l’approche.

index
Le principe est de cacher le rendu final (la page servie) en fonction du profil de droits.

Dans cette logique, la complexité se situe à deux niveaux :

  • La gestion des « profils de droits »
  • L’invalidation des caches : En théorie l’évolution des données dans un des blocs constituant une page doit entraîner l’invalidation des caches de la page pour tous les profils. Et si un bloc sert dans plusieurs pages c’est un ensemble de couples page+profils qui doit être invalidé dans le cache.

A noter quece module ne relève pas du backend.Les formulaires d’édition backend et les formulaires d’administration ne bénéficieront pas d’un gain de performances grâce à ce module.

Authcache utilisera, par défaut, la table cache_page pour stocker les pages en cache. Pour éviter d’affecter la base de données, il convient de télécharger et d’installer un module de gestion de cache.

Il permet de gérer nativement la mise en cache en tenant compte de la présence des modules suivants (depuis la version 7.x-2.x) :

ModuleApport
BlockChargement des blocs en utilisant Ajax ou ESI (Edge Side Includes)
CommentGère les nouveaux commentaires et les liens d’édition (Pour les utilisateurs habilités à éditer leurs propres commentaires).

Récupère le nombre de nouveaux commentaires via JavaScript

FieldCharge le contenu de ces champs via Ajax ou ESI.

(Particulièrement utile avec des modules comme Fivestar).

FormCharge les éléments de formulaires (jetons et build-ids) via Ajax ou ESI.

(Pour les formulaires avec Ajax activé)

ForumCharge le nombre de nouveaux sujets sur la page d’accueil du forum via Ajax ou ESI
MenuCharge les onglets et les actions locales via Ajax ou ESI
PollCharge les sondages et/ou les résultats via Ajax ou ESI
UserRemplis les formulaires utilisateurs avec des données récupérées par Ajax ou ESI.
FlagCharge les marqueurs sur une page en une seule requête Ajax ou ESI
PanelsCharge les contenus des panels via Ajax ou ESI (tant qu’ils sont affichés via Page Manager)
ViewsCharge les vues vi Ajax ou ESI

Warning : Il est à noter que tel quel ce module ne tient pas compte de l’appartenance à des groupes au sens Organic Groups.

Dans le cadre d’un RSE il conviendra de l’adapter pour que les id de « profils » tiennent compte de ces appartenances.

Il est également à noter que les invalidations de caches sont uniquement temporelles par défaut… dans certains cas ça peut être acceptable. Dans d’autres ça peut devenir gênant.

La page du module Authcache https://www.drupal.org/project/authcache

Module « Expire, Rules et Cache actions »

Ces trois modules permettent de gérer des règles d’expiration de cache.

Rules étant le module de gestion de règles qui permettent de « déclencher » des actions, Expire et Cache actions étant des modules qui ajoutent des actions, en particulier d’invalidation, pour les caches.

Lorsque les caches sont essentiellement utilisés pour « absorber la charge » (c’est à dire permettre de tenir des fortes sollicitations sans trop de pertes de performances) ces modules perdent de leur intérêt.

Mais si il s’agit d’améliorer à plus bas régime les performances de l’application en augmentant les durées de vie des caches alors mettre en place des mécanismes d’invalidation « active » des caches devenus obsolètes devient très pertinent.

Il est à noter que pour Drupal 7 le module Cache actions est encore en phase stabilisation (version alpha5 publiée).

L’emploi de ces modules nécessite un travail amont d’identification du besoin de fraîcheur pour les diverses pages et des attendus fonctionnels (Quand j’ajoute XXX les pages YYY et ZZZ doivent être rafraîchies pour tous les utilisateurs ou seulement les utilisateurs des groupes AAA et BBB). Ensuite ça nécessite le développement de modules de « surcharge » pour compléter ce qu’apportent les modules de base.

Les points clés pour ces développements sont :

  • les règles de nommage des id de caches pour pouvoir les retrouver facilement par profil ou par objet caché
  • les règles d’invalidation fonctionnelles pour éviter de devoir supporter le coût d’une approche exhaustive (par exemple la remontée d’une actualité brûlante pourra nécessiter des règles d’invalidation quand les liens de bas de page pourront tolérer la durée de vie standard des caches)

Synthèse sur les modules proposé par Drupal

Des modules Drupal doivent être utilisés pour enrichir le comportement du système de cache et prendre en compte le mode « connecté ».

L’approche la plus rapide sera de mettre en place un cache par utilisateur qui déportera beaucoup de charge sur les backends.

Elle est néanmoins un peu « rustique » et l’on gagnera sur le long terme à employer des approches « par profil de droits » qui nécessitent à l’heure actuelle de surcharger les modules contributeurs par des enrichissements spécifiques (typiquement pour prendre en compte la mise en œuvre de groupes d’utilisateurs).

L’allongement de la durée de vie des caches peut également être envisagée mais nécessite la mise en place de stratégies pertinentes d’invalidation de caches pour s’assurer de la pertinence de ce qui remonte des caches indépendamment des durées de vie paramétrées).

L’utilisation des API en complément

Au delà de ce qu’offrent les modules Drupal la sous-couche de gestion des caches peut être utilisée sur les développements métiers ou servir à surcharger les comportements par défaut éventuellement avec de nouvelles approches.

La difficulté dans le cadre d’un RSE étant de pouvoir cibler les éléments cachés par « catégories » pour les invalider, par exemple :

  • « tous les éléments cachés pour les utilisateurs appartenant au groupe XXX » en cas de changement majeur,
  • ou « toutes les versions cachées du bloc YYY ou de la page ZZZ »…
  • ou en allant plus loin « tout ce qui est à rafraîchir suite à la (dé)publication de l’article AAA »

Ces difficultés avec le cache, communes à tous les RSE opensource ou non, ont fait l’objet de publications par les « grands » représentants de ce type d’activités (Exemple Facebook : https://www.usenix.org/sites/default/files/conference/protected-files/nishtala_nsdi13_slides.pdf )

En complément il peut être envisageable de mettre en place au niveau du noyau Drupal un niveau de cache pour stocker des résultats de requêtes SQL et soulager ainsi directement les serveurs de bases de données.

Ce serait un patch du cœur nécessitant un effort de tests et de qualification important mais qui permettrait à Drupal de monter en charge bien plus facilement sans impact majeur sur le stockage de données en backend.

En complément ou en soutien des démarches de mise en œuvre de modules Drupal (développement de modules de sur-chargement) l’utilisation de l’API dédiée au cache Drupal permet d’envisager des réponses « sur mesure » soit pour des fonctionnalités « métier » soit pour des fonctionnalités si lourdes (cas typique des pages « portail » dans les RSE) qu’un sur-chargement ponctuel de ce qui est fait par la communauté peut être la meilleure solution.

Et en dernier ressort la mise en place au cœur de Drupal d’un niveau de cache spécifique pour les requêtes SQL peut être envisagée. Ça serait une opération lourde techniquement nécessitant ses propres mécanismes d’invalidation. Cela est en particulier dû au fait que ce niveau de cache est prise en charge par la base de données.

En conclusion…

Cette problématique de performances et de gestion de montées de charge de Drupal dans un contexte « RSE » (avec des pages difficiles à cacher) est complexe.

Ce domaine a malheureusement été moins travaillé par la communauté Drupal jusqu’à présent (Parce que le besoin était jusqu’à présent plutôt marginalement représenté).

Cependant les pistes de réponse existent et permettent d’améliorer nettement les temps de réponse et la tenue de charge.

Cela peut nécessiter des efforts importants sur l’architecture pour éviter les goulets d’étranglement et les points de congestion.

Cela peut nécessiter aussi des adaptations applicatives pour une réponse technique satisfaisante.

Et cela peut nécessiter aussi, une fois l’architecture apte à le faire, un grossissement horizontal pour absorber la charge en fonction du nombre d’utilisateurs connectés.

Mais il est tout à fait possible d’améliorer les performances de Drupal comme d’autres RSE dans ce contexte exigeant.


Références

  • Exemple d’alternative tiré de la documentation officielle PHP sur l’usage de flocck()