Carnet Wiki

Version 56 — il y a 1 semaine JLuc

Ce plugin fournit le moyen d’invalider des caches ciblés ou de leur appliquer d’autres actions. Le ciblage se fait par une condition qui porte sur le chemin du squelette et sur le contexte (environnement) ayant généré ce cache.

Cache Lab doit être utilisé avec le plugin memoization et avec le cache APC activé. Il y a toutefois une fonction controler_invalideur qui peut être utilisée sans APC cache et même sans mémoization.

Motivation  : Chaque fois qu’un utilisateur crée ou modifie un objet (article, forum, favori, etc), SPIP invalide tout le cache. Tous les caches en mémoire devront donc ensuite être calculés de nouveau, au lieu d’être utilisés... même s’ils sont encore valables. Parfois, c’est rageant de ne plus pouvoir bénéficier d’aucun de ces caches, alors qu’on vient juste d’ajouter une virgule dans un texte... ou qu’un internaute vient de poster un forum dans un coin lointain du site, ou juste un « J’aime »...

Ce plugin met à disposition 2 fonctions fonctions API : controler_invalideur($action, $objets_invalidants) et cachelab_filtre($action, $conditions, $options) que le code de votre site peut utiliser pour invalider uniquement les caches qui doivent être invalidés.

controler_invalideur

Dans SPIP, la fonction qui invalide le cache est suivre_invalideur.
Cette fonction invalide TOUT le cache. Il est toutefois possible de la désactiver totalement ou de la désactiver pour certains types d’objets. C’est ce que facilite la fonction controler_invalideur.

Les arguments sont :

action
-  stop : arrêter le fonctionnement par défaut d’invalidation des caches par suivre_invalideur.
-  go : reprendre le fonctionnement par défaut des invalidations.
-  select : spécifier les types d’objet dont la modification est invalidante (les autres n’invalident pas).

objets_invalidants : non spécifié, ou liste des objets invalidants. Cet argument sert uniquement pour l’action select : c’est la liste (array) des types d’objets qui continuent à invalider globalement le cache.

cachelab_filtre

Les arguments en sont :

action : l’action à réaliser sur les caches filtrés
-  del : détruire les caches ciblés. En effet, SPIP 3 ne peut pas « invalider » spécifiquement un cache : pour s’en débarrasser, il faut le supprimer.
-  mark : ajoute une entrée mark dans les métadonnées du cache ciblé. Pour l’instant ça ne sert à rien.
-  list : renvoie la liste des caches ciblés.
-  clean : parcourir les caches et supprimer les caches périmés (pour SPIP). Toute action, sauf ’pass’ le fait déjà, sauf option contraire.
-  echo_cache : afficher à l’écran le contenu des caches (html et métadonnées)
-  echo_html : afficher à l’écran le contenu HTML des caches (contenu mis en cache à proprement parler)
-  pass : parcourir les caches mais ne rien faire, pas même la suppression nettoyage des caches périmés (pour SPIP) qui est faite par défaut pour les autres actions.

conditions : l’énoncé des conditions que doit satisfaire un cache. cachelab_filtre applique l’action indiquée à tous les caches qui satisfont à la fois la condition sur la session (si présente) ET sur le chemin du texte (s’il y en a une) ET sur le contexte (s’il y en a une). La condition sur le chemin n’est évaluée que si la condition sur la session est satisfaite (si présente).La condition sur l’objet n’est évaluée que si la condition sur la session et sur le chemin sont satisfaits (si présents).

$condition est un tableau dont les entrées peuvent être :
-  session : 1 ou ’courante’ pour la session courante, ou un identifiant de session, tel qu’utilisé en suffixe des noms de cache. Seuls les squelettes sessionnés pour l’auteur courant (ou spéficié) sont ciblés.
-  chemin : spécification du chemin des squelettes ciblés. Par défaut, c’est une chaîne constituée des morceaux de chemins ciblés, séparés par un |, et que la fonction strpos cherchera. Exemple : admin|liste ciblera tous les caches des squelettes dont le chemin ou le nom contient ’admin’ ou ’liste’.
-  cle_objet : la clé primaire ciblée
-  id_objet : valeur ciblée de la clé primaire
-  contexte : spécification d’un ensemble de couples (clé_objet,id_objet). Seuls les caches dont le contexte inclue l’ensemble de ces valeurs sont ciblés.
-  more : spécification d’une méthode de filtrage autre que celles incorporées dans le plugin. Cela permet des extensions personnalisées pour des filtrages adaptés à un squelette particulier.
Lorsque $conditions['more'] vaut monfiltrage, le webmestre doit définir une fonction cachelab_filtrecache_monfiltrage(($action, $conditions, $options, $cle, &$data, &$stats) qui renvoie true ou false selon que le cache vérifie ou non le filtrage désiré.
Les arguments en sont :

<blockquote class="spip">

- $action, conditions, options : les paramètres reçus par cachelab_filtre
-  $cle : la clé du cache APC à tester
-  $data : la valeur du cache APC à tester, y compris les métadonnées. Cet argument est passé par référence ( pour efficacité et pour permettre de modifier le cache ou les métadonnées ).
.
- $stats : le tableau de stats en cours. Cet argument est passé par référence et peut être modifié par la nouvelle fonction de filtrage, pour incrémenter les statistiques, augmenter les listes de caches et éventuellement pour renvoyer des résultats complémentaires.

</blockquote>

Ce filtrage dédié est appelée après les filtrages par session, chemin et cle_objet s’ils sont spécifiés.
Le filtrage par contexte, proposé d’origine dans CacheLab est un exemple d’extension. En effet, cette méthode de filtrage est implémentée au moyen d’une fonction cachelab_filtrecache_contexte.

options : tableau facultatif d’options à passer à l’action ou au filtrage. Ces options permettent de modifier le filtrage ou d’enrichir les résultats renvoyés par la fonction.
-  methode_chemin : Par défaut, les chemins sont reconnus avec la fonction php strpos. Toutefois, si on passe $options['methode_chemin'] vaut regexp, alors c’est un test d’expression régulière qui est utilisé, et qui permet une plus grande finesse de ciblage. Exemple de valeur pour $condition['methode_chemin'] dans ce cas : inclu.e|admin_
-  list et chrono : permettent de récupérer les listes de caches et la durée du filtrage, pour aller plus loin et optimiser le ciblage.
-  clean : par défaut, cachelab détruit les caches SPIP périmés que APCCache garde encore en mémoire, dés qu’il en rencontre un. Ce comportement est désactivé lorsque $options a un index ’clean’ vide.

Préciser le diagnostic
La plupart du temps, il n’est pas utile d’utiliser le retour de la fonction, mais cela permet d’étudier le cache, connaître le coût de l’invalidation ciblée (en ms) et éventuellement d’aller plus loin.

La fonction cachelab_filtre renvoie un tableau de statistiques globales sur le filtrage :

  • caches : le nombre de caches examinés
  • matche_chemin : le nombre d’accès réussis au chemin
  • no_data : le nombre de caches sans données. (Les caches sans données sont soit un artefact de gestion des caches par APC, soit un artefact, volontaire ou indésirable, de la gestion des caches par SPIP.)
  • data_not_array : le nombre d’erreurs « data not array » (signalerait un bug)
  • cible : le nombre de caches ciblés (qui satisfont toutes les conditions)
  • chrono : le temps total du filtrage, en millisecondes. Pour obtenir cette sortie, le tableau d’options passé à la fonction doit contenir une entrée chrono vraie.
  • squelette et contexte : ces 2 sorties contiennent la liste des caches dont le chemin du squelette matche le chemin indiqué, et la liste des caches dont le contexte contient l’objet spécifié. Pour obtenir ces sorties, le tableau d’options passé à la fonction doit contenir une entrée listes vraie.

Exemple de résultat renvoyé :

   [caches] => 8341
   [session] = _
   [matche_chemin] => 445
   [no_data] => 8
   [data_not_array] => 0
   [cible] => 5
   [chrono] => 38.989 ms

Il y a 8341 caches, dont 445 sont préfiltrés par le chemin, parmi lesquels 5 sont ciblés par toutes les conditions. Parmi les caches qui matchent le chemin, 8 n’avaient en fait pas de données. Le filtrage a demandé 39 ms.

Lorsqu’il n’y a pas de préfiltrage par le chemin, l’interrogation du contexte est un peu plus lente puisqu’il faut accéder aux données, et ce d’autant plus si les données du cache sont codées par mémoization (lorsque le cache APCu est partagé entre utilisateurs).

Voici les résultats lorsque le chemin n’est pas préfiltré, avec des données non codées :

   [caches] => 10126
   [matche_chemin] => _
   [no_data] => 348
   [data_not_array] => 0
   [cible] => 0
   [chrono] => 154.242 ms

Si les données sont codées, il faudrait a priori 20ms supplémentaire si comme annoncé il faut 2µs pour chaque décodage, ce qui reste acceptable.

Exemple d’usage

Le site où ce plugin est développé et mis en œuvre gère des objets « truc » dont l’internaute peut constituer une sélection (des favoris), et dispose d’une partie protégée servant pour l’administration.

Voici 2 modifications réalisées pour cibler les invalidations :

  • Dans l’administration (squelettes dont l’accés est restreint), les modifications de contenu portant sur des « trucs » sont parfois fréquentes, voire continues. Le souhait est que ces modifications ne vident pas tout le cache.
    De manière très classique, un truc est affiché (que ce soit dans la partie publique ou dans l’admin) soit de manière résumée dans des listes (le titre, éventuellement le logo, le sous titre ou une autre caractéristique), soit sur des pages qui lui sont dédiées. Dans tous les cas, des noisettes sont utilisées et en pratique, ces noisettes reçoivent toujours l’identifiant du truc dans leur environnement. Il a donc été très facile de cibler les caches sans devoir réécrire le squelette. Il a suffit de modifier le début et la fin de la fonction ’traiter’ du formulaire #FORMULAIRE_EDITER_ADMIN_TRUC :
    -  un appel à controler_invalideur('stop') en début de fonction, et un appel à controler_invalideur('go') en fin de fonction permettent d’éviter toute invalidation « par défaut » pendant le traitement.
    -  Juste avant le retour du traiter, on invalide tous les caches contenant le truc modifié avec cachelab_filtre('del', array('cle_objet'=>'id_truc', 'id_objet'=>$id_truc))
  1. function formulaires_editer_truc_admin_traiter_dist($id_truc) {
  2. // On commence par désactiver l'invalidation des caches par SPIP
  3.     include_spip('inc/cachelab');
  4.     controler_invalideur('stop');
  5.  
  6.  
  7. // ... code du traitement
  8.  
  9.  
  10. // Quand le traitement est fait, on invalide les caches le nécessitant  
  11.     cachelab_filtre('del', array('cle_objet'=>'id_truc', 'id_objet'=>$id_truc));
  12. // et on restaure le mécanisme d'invalidation habituel :
  13.     controler_invalideur('go');
  14.     return $res;
  15. }

Télécharger

  • Pour ce site, l’internaute logé peut se constituer une sélection de trucs. Pour cela, un formulaire #FORMULAIRE_SELECTION lui permet d’ajouter un truc à sa sélection, ou de le retirer s’il y est déjà. Il utilise pour cela la structure de donnée et l’API fournie par le plugin mesfavoris. La fonction de traitement du CVT de ce formulaire désactive l’invalidation SPIP des caches et provoque l’invalidation des seuls caches périmés :
  1. function formulaires_selection_traiter_dist($id_truc) {
  2.     include_spip('inc/cachelab');
  3.     controler_invalideur('stop');
  4.  
  5.  
  6. $res = array('message_ok'=>' ');
  7.     $id_favori = ;
  8.    
  9. // ... traitement du formulaire ...
  10.  
  11.  
  12. // $id_favori vaut maintenant l'identifiant du favori créé ou supprimé
  13. // on invalide tous les caches concernés et on restaure le fonctionnement normal du cache
  14.     cachelab_filtre( 'del',
  15.                               array(    'chemin'=>'selection',
  16.                                         'cle_objet'=>'id_favori',
  17.                                         'id_objet'=>$id_favori )
  18.                             );
  19.     controler_invalideur('go');
  20.     return $res;
  21. }

Télécharger

Ici, la présélection par le chemin permet de ne pas interroger le contenu de tous les caches qui n’ont pas de rapport avec les sélections.

Un 3e usage est fait dans l’action instituer_truc.

Un 4e usage est fait dans ...

Au final il ne reste plus que les invalidations SPIP suivantes et dans les 2 ou 3 cas elles sont faciles à éviter :
-  pour la création d’un point GIS lors de la création d’un nouveau ’truc’ dans le public
-  lors du cron mailsubscriber pour la mise à jour des listes de diffusion dynamiques, au milieu de la nuit. C’est pas très gênant.
-  à vérifier : pour la création d’un point GIS lors de la modification d’un truc dans l’admin.

Stratégie de mise en oeuvre sur votre site

L’usage dépend de chaque site. Voici une marche à suivre pour concevoir comment utiliser CacheLab sur un site existant :

-  Déterminer quelles sont les circonstances d’invalidation par défaut. En général, c’est un formulaire qui modifie la base de donnée. Pour s’assurer que le site soit mis à jour, le traitement des formulaires appelle la fonction suivre_invalideur du noyau SPIP. Cet appel a lieu soit directement dans la fonction ’traiter’ du CVT, soit dans l’une des fonctions d’API SPIP appelées à l’intérieur de ce ’traiter’.

-  Déterminer quelles sont les parties du site dont il est critique qu’elles soient immédiatement mises à jour, et inversement, quelles parties peuvent supporter un délai normal d’invalidation du cache avant d’être mises à jour. La nature du site, les circonstances de son utilisation, la fréquence des pics de consultation ou de création de contenu, la mesure des temps de calculs des caches fournie par var_mode=profile, l’usage de la partie privée ou d’une partie d’administration, ... sont des paramètres à prendre en compte.

-  En conséquence, choisir quels caches doivent être invalidés dans cette circonstance, et lesquels il est inutile d’invalider.

-  Si nécessaire, restructurer l’arborescence des squelettes de manière à pouvoir spécifier plus facilement, au moyen de l’argument $chemin ou $id_objet, quels sont les caches qui doivent être invalidés dans ce cas. Certaines bases de squelette standard contiennent déjà des sous-répertoires liste ou admin, qu’il est déjà possible d’utiliser, mais vous pouvez créer à volonté les chemins utiles pour la stratégie définie, quitte à renommer un dossier ou un squelette pour plus facilement le cibler. Il est aussi possible de passer un environnement spécifique à un squelette {marqueur_cachelab=iciprecis} pour le cibler précisément.

-  Dans tous les cas il faut privilégier le filtrage par chemins, qui est beaucoup plus efficace que le filtrage par contenu du contexte. En effet, le filtrage par chemins ne nécessite pas de récupérer la valeur du cache : il suffit d’en tester la clé. Lorsqu’on veut tout de même filtrer en fonction de la valeur d’un identifiant d’objet, on aura donc intérêt à faire un pré-filtrage par le chemin dans le squelette.

-  L’onglet « Cache Lab » de XRay permet de tester les conditions envisagées.

-  Réécrire la fonction traiter de ce formulaire afin de ne pas appeler suivre_invalideur, mais labcache_filtre('del', ...) avec les conditions que vous avez défini.

Ce travail étant fait, le plugin XRay permet de vérifier que le formulaire corrigé n’invalide plus la totalité du cache, mais seulement les caches ciblés.

Usage pour objet standard

Pour un objet déjà existant dans SPIP (par exemple article, forum, etc), la modification d’une valeur se fait via le formulaire d’édition livré avec SPIP, et appelle les pipeline pre_edition et post_edition : il sera possible de définir ces pipelines de manière à désactiver l’invalidation des caches, puis appliquer l’invalidation sélective désirée, et enfin réactiver l’invalidation normale des caches.

Et memcache, XCache et redis ?

Contrairement à XRay qui est trés lié à APC, CacheLab n’appelle qu’une seule fonction spécifique à APC : celle qui permet de lister l’ensemble des caches. Cette fonction n’existe pas, à ma connaissance, pour les autres systèmes de caches. Il serait néanmoins possible d’étendre memoization pour y ajouter une configuration selon laquelle Memoïzation tiendrait à jour une liste des caches. Et alors cacheLab fonctionnerait tout de suite avec les autres Si cette fonction est disponible ou peut être créée pour d’autres systèmes de cachesmémoire , alors CacheLab pourrait fonctionner aussi .

État des devs

Ça marche, et c’est opérationnel sur un site hébergé par Gandi Simple Hosting, avec APC cache.

Ça ne devrait plus beaucoup évoluer pour une première version, mais il y a encore des trucs à vérifier.

Voici les développements envisagés :

Chauds
-  ANALYSER : la détection des caches périmés et le clean fonctionnent ils bien ? Vérifier le retour de ->del et ajouter un log dans cachelab_filtre comme dans _appliquer
-  NOMMAGE : Infinitif dans les noms de fonction + cohérence du nommage des index des arguments tableaux et des noms de fonctions (anglais, français...).
-  CLEAN : pas recueillir l_not_array et l_no_data. nb_not_array et nb_no_data, plus un spip_log pour l_not_array, suffisent.

Tièdes
-  TODO : confirmer l’exemple théorique d’emploi sur des objets standards, sans redéfinir les fonctions CVT, mais avec les pipelines pre_edition et post_edition + donner un exemple dans la doc.
-  DEV : embarquer (de manière optionnellement activable ou désactivable) l’implémentation de pipelines destinés à des formulaires externes pré-existants, issus de plugins (ou du noyau si c’est possible). Pour cela, il faut que l’implémentation du formulaire permette sans ambiguïté de cibler les caches, quel que soit le site et son squelette. (Par exemple pour le formulaire plugin mesfavoris ?)
-  ANALYSER : permettre d’appeler en éditant un squelette, via une ou plusieurs balises SPIP ?

Froid
-  ajouter actions ’get’ à ’applique_filtre’ pour recevoir le contenu d’un cache (avec ou sans metadonnées selon option) et ’set’ aussi. BOF au besoin ça peut se faire avec une extension ’more’.
-  rendre ’mark’ utilisable : pour créer des méta-entrées dans les caches et filtrer selon présence et valeur de ces entrées (index ’mark’ ou ’meta’ de $condition ou libre). BOF. Au besoin ça peut se faire avec une extension ’more’. Faut plutôt supprimer cette action actuelle, ou la dé-documenter.

Voir aussi

-  Compléments CacheLab
-  XRay, un explorateur de cache

Retour à la version courante

Toutes les versions