Invalider le cache des pages en fonction d'une durée avec Drupal 8

Un sablier

Pour certains besoins il peut être nécessaire de disposer de contenus, ou d'éléments de contenu, qui puissent varier selon une certaine durée de temps écoulée, et donc qui nécessitent une invalidation du cache en fonction d'une certaine durée. C'est par exemple le cas typique d'une liste des événements futurs et/ou passés, liste qui doit varier invariablement avec le temps.

Drupal 8 dispose de trois systèmes de cache :

  • Les cache tags : qui nous permettent d'invalider des pages, ou éléments de page, selon des tags spécifiques collectés et remontés automatiquement au niveau de chaque page
  • Les cache context : qui nous permettent de faire varier le cache selon différents contextes (par URL, par langue, par cookie, par permissions, etc.)
  • Le cache max-age : qui nous permet de spécifier une durée déterminée pour la validité du cache mis en place

Le cache max-age semble répondre en tous points à notre besoin, à savoir définir une durée déterminée au terme de laquelle le cache sera invalidé automatiquement. Malheureusement, ce système de cache ne fonctionne pas encore parfaitement dans toutes les situations, et certains caches de type max-age peuvent ne pas être collectés et remontés dans les entêtes de page selon certaines situations. Une issue est ouverte à ce sujet, Bubbling of elements' max-age to the page's headers and the page cache, et mentionne aussi l'utilisation d'un module contribué Cache control override permettant de régler ce problème pour certaines situations.

Néanmoins, en attendant la résolution de ces difficultés, nous pouvons disposer d'un solution déjà opérationnelle, et remarquablement efficace, à savoir les cache tags. En effet si Drupal fournit automatiquement pour toute entité, que ce soit de la configuration ou du contenu, leurs cache tags et se charge de les collecter et de les invalider automatiquement, rien ne nous empêche de fournir nous même nos propres caches tags, réutilisables à volonté.

Et si donc nous utilisions une solution alternative pour procéder à une invalidation de cache selon un critère de temps.

Ainsi, par exemple au lieu de spécifier un cache max-age de 3600 (pour stipuler une durée de validité du cache de 1h) pour les certains éléments de contenus ou pages de notre projet, nous pouvons très bien ajouter aux cache tags notre propre cache tag [time:hourly], ou encore [time:daily] ou encore [time:weekly]. Bref je pense que vous avez dû comprendre le concept. A charge bien entendu au développeur Drupal de positionner ces cache tags sur les éléments qui nécessitent une invalidation basée sur une durée, ou pourquoi un module à faire qui permettrait d'ajouter des caches tags depuis les pages d'édition des contenus.

Bien entendu l'ajout de ces cache tags personnalisés n'aura strictement aucun effet sur l'invalidation du cache de votre projet, tant que vous ne mettez pas en place la logique d'invalidation de ces cache tags. Ce qui peut être fait en quelques lignes en implémentant hook_cron().

 

use Drupal\Core\Cache\Cache;

/**
 * Implements hook_cron().
 */
function my_module_cron() {
  $request_time = \Drupal::time()->getCurrentTime();
  $state_service = \Drupal::state();
  $next_hourly_cron = $state_service->get('my_module.cache_cron_hourly', 0);
  if($next_hourly_cron < $request_time){
    $next_hour = strtotime(date('H') . ":59");
    $state_service->set('my_module.cache_cron_hourly',$next_hour);
    Cache::invalidateTags(['time:hourly']);
  }
  $next_daily_cron = $state_service->get('my_module.cache_cron_daily', 0);
  if ($next_daily_cron < $request_time) {
    $tomorrow = strtotime('tomorrow 0:59:00');
    $state_service->set('my_module.cache_cron_daily', $tomorrow);
    Cache::invalidateTags(['time:daily']);
  }
  $next_weekly_cron = $state_service->get('my_module.cache_cron_weekly', 0);
  if($next_weekly_cron < $request_time){
    $next_week = strtotime('next monday 0:59:00');
    $state_service->set('my_module.cache_cron_weekly',$next_week);
    Cache::invalidateTags(['time:weekly']);
  }
}

Et ainsi, bien sûr avec un tache planifiée (cron) que vous aurez préalablement configuré sur votre serveur (recommandé) ou au niveau de votre projet (avec l'aide d'un freelance Drupal bien sûr) pour être lancée toutes les heures (ou moins si nécessaire), vous disposez d'un système d'invalidation de cache, remarquablement efficace, basé sur une durée, que ce soit pour la cache statique des visiteurs anonymes ou le cache dynamique. Bien entendu, toutes les variations (sur les périodes de temps, ou tout autre critère finalement) peuvent être imaginées ici.

 

Commentaires

Soumis par Silvus (non vérifié) le 19/11/2019 à 16:39 - Permalien

Au premier lancement, $next_hourly_cron (et ses amis daily et weekly) va retourner null il me semble.
Dans ce contexte le test "if($next_hourly_cron < $request_time){" fonctionne comme attendu ?
Peut être serait-il plus fiable de transformer les test dans les ifs en "<=" et dedéfinir une valeur par défaut comme ceci :
$next_hourly_cron = $state_service->get('my_module.cache_cron_hourly', $request_time);

Soumis par fabrice le 19/11/2019 à 16:42 - Permalien

Oui cela fonctionne comme attendu car request_time sera supérieur à null. mais on peut tout à fait typer le retour de la valeur, ainsi le premier (et unique) NULL sera 0. Ce sera mieux. Ou avec une valeur par défaut tout à fait (j'ai mis à jour le snippet).

Ajouter un commentaire