Jouez à cache-cache avec Drupal

Jouez à cache-cache avec Drupal

08 Jun 2010 |  Drupal

Qui n’a jamais eu besoin d’améliorer les performances de son site pour booster Drupal?

Effectivement, lorsque l’on travaille sur des gros sites il faut se poser des questions sur les performances de Drupal, parce qu’on le sait tous, Drupal est gourmand. L’affichage d’une simple page peut parfois engendrer l’exécution de 50 voir 150 requêtes.

Imaginez vous cette même page appelée par plusieurs internautes en même temps. On obtient alors des centaines de requêtes et informations recalculées inutilement qui vont solliciter les serveurs et vont ainsi consommer du CPU et de la RAM alors qu’elles auraient pu tout aussi bien être sauvegardées.

Pour sauvegarder ces informations il faut donc mettre en place un système de caching qui va mémoriser pour un temps donné des informations afin de ne pas les recalculer.

Le caching de page va avoir plusieurs impacts sur notre site :

  • Baisse du temps de chargement de la page
  • Diminution de l’utilisation des serveurs qui pourront faire autre chose à la place.
  • Améliorer votre référencement auprès de Google, puisqu’il prend maintenant en compte la vitesse de chargement des pages.

La solution de caching n’est pas l’arme ultime pour améliorer les performances de votre site mais elle permet d’alléger la charge de travail du serveur et de le rendre plus disponible.

Premiers pas avec le caching

La solution la plus simple à mettre en place est d’activer le cache de page depuis l’administration de Drupal [admin/settings/performance]. C’est un cache simple mais efficace qui mémorise le résultat de la page. Malheureusement cette solution n’est disponible que pour la naviguation des utilisateurs anonymes, mais n’ayez crainte il existe belle et bien une solution pour les visiteurs authentifiés :)

Cette solution consiste à effectuer soi-même la mise en cache et l’affichage en utilisant l’Api de Drupal, mais cela implique néanmoins de savoir ce que vous voulez sauvegarder et afficher.

L’API de cache de Drupal

Celle-ci est composée de trois fonctions :

cache_set pour sauvegarder

cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL)

$cid : C’est la clé permettant l’identification de ce que vous voulez sauvegarder. Celle-ci doit être unique, utilisez quelque chose qui ne pourra pas être créé par un autre module. Le plus simple est de préfixer votre clé avec le nom de votre module.
$data : Ce sont les données à sauvegarder, ne vous inquiétez pas du typage et de la structure de vos données celles-ci sont sérialisées lors de l’enregistrement.
$table : Par défaut vous enregistrerez dans la table nommé ‘cache’ mais vous pouvez tout aussi bien spécifier une autre table.
$expire : Si vous souhaitez rafraîchir les informations stockées en cache vous allez devoir définir une période de validité pour celles-ci.
$headers : Cette variable est utile si vous souhaitez passer des informations d’en-tête HTTP aux pages mises en cache.

cache_get pour récupérer les informations

cache_get($cid, $table = 'cache')

cache_clear_all pour nettoyer ce qui a été enregistré

cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE)

$wildcard : Par défaut à False, cette variable peut, si elle est passée à True, sélectionner toutes les occurrences retrouvées commençant par le $cid renseigné.

Mise en place du cache

function my_module_function() {
  // Récupération du cache s'il existe
  $cache = cache_get('my_module_data');
  if (is_object($cache) && !empty($cache->data)) {
    $my_data = $cache->data;
  }
  else {

    // fonction de génération de votre contenu
   
    // Sauvegarde des informations calculées.
    cache_set('my_module_data', $my_data);
  }
  return $my_data;
}

Analysons le code ci-dessus simplifié pour l’exemple. Dans un premier temps la fonction cache_get() à été utilisée pour savoir si oui ou non, nous avions des données enregistrées a retourner directement à l’utilisateur. Dans le cas contraire les informations ont été générées puis mise dans la table de cache via la fonction cache_set().

Dans cette table vous pouvez stocker tout ce que vous voulez, un rendu html ou encore le calcul d’une lourde opération mais dites vous bien que les informations dans cette table ne sont qu’éphémères et peuvent à tout moment disparaître.

Nettoyage du cache

Il peut vous arriver de vouloir vider vos données sauvegardées tout simplement pour que celles-ci soient régénérées ou pour faire un export complet de votre base (ne prenez pas les données contenues dans les tables de cache, elle ne font que grossir la taille de votre export sans réel besoin).

Le nettoyage des caches se fait avec la fonction cache_clear_all() qui permet de supprimer ce qui nous concerne sans toucher au reste. Par défaut celle-ci n’a besoin que d’une chaîne de texte ($cid) afin de trouver et effacer l’occurence dans la table de cache, mais si $wildcard est passé à True alors toutes les valeurs commençant par votre $cid seront effacées.

cache_clear_all('my_module_data', 'cache', TRUE);

Cet exemple supprime toutes les entrées de cache commencant par ‘my_module’.

Fraîcheur de l’information

Par défaut les informations que vous avez sauvegardées en cache sont gardées indéfiniment ou du moins jusqu’à ce que vous les ayez effacées avec la fonction cache_clear_all. La problématique sur la fraîcheur de données se pose alors car cette méthode n’est pas viable si vous souhaitez cacher des informations qui doivent être recalculées souvent.

Pour palier au cache permanent nous allons définir lors de l’enregistrement une date d’expiration aux données que l’on pourra comparer ensuite à la date en cours.

cache_set('my_module_data', $my_data, 'cache', time() + 300);

La date d’expiration doit être au format unix timestamp et la façon la plus simple pour définir cette valeur est d’ajouter une période en seconde au timestamp en cours. Cet exemple défini une validité de 5 minutes (60sec x 5).

Lorsque vous récupérez votre cache il ne vous reste plus qu’à vérifier qu’il est encore valide, sinon il faudra le régénérer et le mettre en cache.

function my_module_function() {
  // Récupération du cache s'il existe.
  $cache = cache_get('my_module_data');
  if (is_object($cache) && !empty($cache->data)) {
    // On vérifie que les données sont encore valide ou non.
    if ($cache->expire > time()) {
      return $cache->data;
    }
  }
  // fonction de génération de votre contenu.
   
  // Sauvegarde des informations calculées.
  cache_set('my_module_data', $my_data);  
  return $my_data;
}

Créer une table de cache dédiée

Nous venons de voir comment sauvegarder des informations dans la table de cache par défaut de Drupal, mais il est aussi possible de créer votre propre table de cache surtout si vous pensez avoir beaucoup d’informations à stocker. Ceci évitera d’avoir une grosse et unique table de cache.

Voici un petit exemple pour mieux comprendre l’interêt d’une table de cache dédiée. Imaginons que j’ai 200.000 urls raccourcies à mettre en cache. Par défaut cela aurait crée 200.000 nouveaux enregistrements dans la table de cache standard, ce qui aurait eu pour conséquence d’augmenter le temps d’exécution des requêtes sur la base de données parce que Mysql aurait été obligé de parcourir tous les enregistrements à chaque fois. En créant notre table dédiée on garde les performances de la table cache et on ne parcourt nos 200.000 valeurs qu’en cas de besoin.

Le plus simple à faire pour créer un autre table de cache est de faire une copie de la table standard ‘cache’ comme cela on est sûr d’avoir le même schéma de base de données. Pour cela la récupération du schema original et non modifié de la table ‘cache’ sera fait avec la fonction drupal_get_schema_unprocessed($module, $table = NULL).

/**
* Implementation of hook_schema().
*/
function my_module_schema(){
  $schema = array();
  $schema['cache_my_module'] = drupal_get_schema_unprocessed('system', 'cache');
  return $schema;
}

Par convention nommez votre table ‘cache_’ puis le nom de votre module ce qui dans l’exemple ci dessus donne ‘cache_my_module’.

Le schema de base de données est prêt, il faut maintenant écrire les fonctions permettant la création et la suppression de celui-ci (hook_install et hook_uninstall) dans le fichier .install du module.

A ce stade la mise en place de la table de cache dédiée est terminée, il ne reste plus qu’à activer le module et à vérifier que celle-ci à bien été ajoutée à la base de donnée.

Pensez aussi à modifier les fonctions drupal_cache_get(), drupal_cache_set() et cache_clear_all() pour qu’elles pointent vers la bonne table. :)

cache_set('my_module_data', $my_data, 'cache_my_module');
cache_get('my_module_data', 'cache_my_module');

Un dernière chose sur le stockage des informations en cache, il est tout à fait possible de les sauvegarder vers un serveur spécialisé type memcached, APC, ou autre.

Vous voilà maintenant prêt à améliorer votre code. Si vous voulez en lire plus voici la présentation officielle de l’api cache de Drupal ou sinon le guide Lullabot sur l’utilisation de l’api.

Crédits Photo

Julien Dubreuil

Vous avez une idée, un projet web à réaliser ?

Ensemble, mettons en oeuvre sa réussite. Je vous accompagne dans vos projets, depuis l'élaboration du cahier des charges jusqu'à la mise en production. Pour plus d'information n'hésitez pas à me contacter.

Contactez-moi