Carnet Wiki

20. API CacheLab 1. Agir sur des caches ciblés

Version 131 — il y a 4 mois — 78.242.xx.xx

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 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 »...

CacheLab fournit une API permettant d’appliquer différentes actions sur des caches ciblés, au rang desquelles figure l’invalidation. Le ciblage se fait par une condition qui porte sur le chemin du squelette, sur la session ou sur le contexte ayant généré ce cache. Cela permet de décrire les caches ciblés et de leur appliquer l’action. Dans le cas d’une invalidation, seuls les caches ne nécessitant sont invalidés, et aucun autre.

Cette partie de CacheLab doit être utilisé avec le plugin memoization activé avec le cache APC ou APCu.

CacheLab n’installe aucune stratégie d’invalidation, car ces stratégies dépendent de chaque site, mais CacheLab donne le moyen de le faire au moyen des fonctions d’API :
-  API d’action locale : les fonctions controler_invalideur et cachelab_cibler que décrit cet article.
-  API d’action globale (dev en cours) : ces fonctions interceptent les signaux d’invalidations de SPIP et permettent de les interpréter de la manière désirée globalement pour le site.

Fonction 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.

Cette fonction peut être appelée dans le code php
-  soit dans le mes_options du plugin, pour totalement supprimer l’invalidation SPIP des caches (avec controler_invalideur('stop');) et ensuite cibler au cas par cas
-  soit au cas par cas. Par exemple, dans le traitement d’un formulaire, on pourra stoper l’invalidation juste avant d’appeler l’API SPIP d’enregistrement d’une modification, afin de préserver le cache, puis cibler précisément l’invalidation adéquate avec cachelab_cibler, puis restaurer le fonctionnement normal de l’invalidation avec (avec controler_invalideur('go');)

Fonction cachelab_cibler

Cette fonction permet de réaliser une action sur un certain nombre de caches, ciblés en fonctions de conditions qui portent sur leur chemin, sur l’environnement du squelette à l’origine de ce cacche (variables de contexte), sur le contenu du cache lui-même ou sur ses métadonnées.

C’est la fonction principale qui doit être appelée pour faire une invalidation ciblée, lorsqu’on a désactivé l’invalidation totale au moyen de controler_invalideur.

Les arguments en sont :

action : l’action à réaliser sur les caches filtrés. Les actions sont en anglais.
-  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.
-  list : renvoie la liste des clés des caches ciblés.
-  list_html : renvoie la liste des contenus HTML des caches ciblés, indexés par leur clé. Attention, cela peut être volumineux. On peut limiter le nombre de résultats en spécifiant $option['nb'].
-  clean : parcourir les caches et supprimer les caches périmés (pour SPIP). Toute action, sauf ’pass’ le fait déjà, sauf option contraire.
-  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.
-  get : renvoyer le premier cache ciblé (métadonnées et contenu).
-  get_html : renvoyer le contenu du premier cache ciblé (html généré à partir du squelette SPIP)

conditions : l’énoncé des conditions que doit satisfaire un cache. Les conditions sont en français.
cachelab_cibler applique l’action indiquée à tous les caches qui satisfont à la fois la condition sur la session (si cette condition est présente) ET sur le chemin du squelette (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’. Il peut y avoir des dossiers et sous-dossiers : liste/article

-  cle_objet : la clé primaire ciblée
-  id_objet : valeur ciblée de la clé primaire. Ces 2 ci vont nécessairement ensemble.
-  contexte : spécification d’un ensemble de couples (clé_objet,id_objet). Seuls les caches dont le contexte inclue l’ensemble de ces couples (clé,valeur) sont ciblés.
-  plus : 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['plus'] vaut monfiltrage, le webmestre doit définir une fonction cachelab_ciblercache_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_cibler
-  $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_ciblercache_contexte.

Rq 1 : Les conditions spécifiées se combinent. On peut, par exemple, spécifier un chemin, une valeur de contexte et un couple (cle_objet, id_objet) et seuls les caches vérifiant l’ensemble de ces critères seront ciblés.
Pour combiner des conditions de manière inclusive (chemin OU valeur de contexte OU id_objet précis) on peut faire des appels successifs à cachelab_cibler, ou bien, avec more, définir une nouvelle méthode de filtrage.

Rq 2 : contexte est implémenté par un plus. C’est un exemple de la manière d’utiliser le paramétrage plus. Mais de ce fait, il n’est pas possible de combiner contexte et plus qui fasse le test désiré en une seule passe.

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|liste.*admin. Dans ce dernier cas, les chemins de squelettes contenant ’inclure’ ou ’include’ seront ciblés, ainsi que tous les chemins de squelette dans un dossier ’liste’ et ayant ’admin’ dans le nom... ou ayant ’liste’ puis ’admin’ dans leur nom.

-  partie_chemin : permet de spécifier quelle partie du chemin est testée. Le chemin complet, c’est le chemin du squelette depuis le dossier de squelettes (comme pour un inclure_spip), avec en prime en suffixe l’identifiant de session (un hexa qui peut difficilement matcher avec un nom de squelette et donc qui ne gêne pas). Les différentes valeurs possibles sont : tout ou chemin : teste l’entièreté du chemin ;dossier : teste le répertoire du squelette seulement. Comme ça, des squelettes dans les sous-dossiers ne sont pas ciblés ; fichier : teste le nom de fichier du squelette.

-  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_cibler renvoie un tableau de statistiques :

  • nb_candidats : le nombre de caches candidats (corrects et valides)
  • nb_cible : le nombre de caches ciblés par les conditions spécifiées
  • nb_clean : le nombre de caches vidés car périmés par APC ou invalidés car trop vieux vu la durée du cache SPIP
  • chrono : le temps total du filtrage, en millisecondes.
  • l_cible : liste des clés des caches ciblés. Pour obtenir ces sorties, le tableau d’options passé à la fonction doit contenir une entrée list vraie.

Exemple de résultats renvoyé :

Filtrage du chemin par ’strpos’ :
[nb_cible] => 8
[nb_clean] => 0
[nb_candidats] => 2325
[chrono] => 10.415 ms
Filtrage du chemin par ’regexp’ :
[nb_cible] => 29
[nb_clean] => 0
[nb_candidats] => 1546
[chrono] => 81.342 ms

Extension : définir ses propres actions

Pour définir une nouvelle action nouvelleaction, il faudra définir une fonction cachelab_appliquer_nouvelleaction($action, $cle, $data=null, $options='', &$return=null) sur le modèle de la fonction cachelab_appliquer livrée dans le plugin.
Cette fonction sera appelée tour à tour par cachelab_cibler pour chacun des caches ciblés.
-  Elle devra appliquer l’action nouvelleaction sur le cache dont la clé est reçue en paramètre $cle et dont le contenu est $data s’il est déjà connu (dans le cas où le filtre ne porte pas seulement sur le chemin mais aussi sur le contexte).
-  Les options de ciblage sont $options.
-  Elle renverra true si elle a pu appliquer l’action et false sinon.
-  La fonction pourra éventuellement ajouter un résultat au tableau $return passé en référence. Ce tableau pourra être récupéré dans le champ ’return’ de la valeur de retour de cachelab_cibler.

Efficacité et coût pour le CPU

Sur un hébergement où chaque site dispose de son propre espace pour les caches APC, les durées de filtrage ne sont pas pénalisantes. Surtout qu’elles ne se font que lorsqu’il y a une invalidation. Le coût du ciblage de l’invalidation est compensé par le bien meilleur taux de cache : en pratique on atteint un taux de cache de 95% à 100%. SPIP n’est sollicité pour l’évaluation d’un squelette que lorsqu’il en a réellement besoin.

-  Le filtrage par regexp est évidemment plus long que par strpos. Et l’interrogation du contexte est un peu plus lente puisqu’il faut accéder au contenu des caches.

-  Le filtrage par le contenu du contexte est plus coûteux si les données du cache sont codées par mémoization, comme c’est nécessaire lorsque le cache APCu est partagé entre utilisateurs. Si les données sont codées, le commit 111634 indique qu’il faut 1 ou 2µs pour chaque décodage. Du coup il faudrait entre 10 et 20ms supplémentaire pour parcourir 10000 caches. Cela reste acceptable.

Lorsque les caches APC de plusieurs sites indépendants sont mutualisés et qu’ils sont tous accessibles à chaque site, alors 1) ils doivent être cryptés par Memoization 2) leur nombre peut être beaucoup plus grand. Dans ce cas, le parcours va prendre plus de temps. S’il y a 10 sites qui mutualisent leur cache APC et qu’il y a 10 fois plus de caches, alors au lieu de 50 ms ce sera 500 ms ce qui devient significatif (voir todo).

Questions et réponses

  • « Dans des inclures imbriqués, est-il possible d’invalider certains inclures de la branche sans forcement invalider tout les inclures enfants ? Par exemple en zcore il y a content > liste Si j’invalide content, liste sera-t-il invalidé ? »

3 réponses :
1) Les options permettent de n’invalider que les squelettes d’un dossier ciblé : il suffit de passer 'partie_chemin' => 'dossier'

2) Une autre manière de faire serait de passer methode_chemin=regexp et donner comme chemin une expression régulière qui exclue les squelettes dans les sous dossiers. Par exemple : chemin=content\/[^\/]*$

3) Une autre manière de faire serait de créer une extension ’contentseul’ et passer plus=contentseul pour appeler une fonction cachelab_ciblercache_contentseul qui opérerait le filtrage, par exemple avec la même regexp que ci dessus ou bien ainsi :

function cachelab_ciblercache_contentseul($action, $conditions, $options, $cle, &$data, &$stats) {
   return (strpos($cle,'content') and (substr_count($cle, '/') < 2));
}
  • « Et memcache, XCache et redis ? »

Contrairement à XRay qui en son état actuel est trés lié à APC, CacheLab n’appelle qu’une seule fonction spécifique à APC : celle qui permet de lister l’ensemble des caches. Il serait possible d’étendre memoization pour disposer optionnellement d’une méthode iterate. Sur option, Memoïzation tiendrait à jour une liste des caches et alors cacheLab pourrait tout de suite fonctionner avec les autres systèmes de caches.

  • Quel est l’état de dev du plugin ?

L’API est intensément utilisée sur au moins un site avec APC cache. Les extensions de #CACHE et var_mode=cache ont été testées sur quelques sites.
Les éventuels bugs détectés et les développements futurs à court ou moyen terme sont suivis sur Compléments CacheLab et todo.

  • Quels sont les résultats obtenus ?

Sur le site de dev, 100% des invalidations du cache sont optimisées. Il n’y a donc plus d’invalidation totale du cache, sauf si on le demande explicitement. Le taux de hit du cache SPIP est passé de en moyenne 40% à plus de 98% après aprés quelques heures ou jours de fonctionnement (aprés le fix du bug de contamination du sessionnement).

Voir aussi


-  CacheLab étend #CACHE et INCLURE
-  API CacheLab 2. Actions globales par type d’invalidation
-  Compléments CacheLab et todo
-  XRay, un explorateur des caches SPIP
-  Plugin ’macrosession’ : usage optimisé et extension des données de session

Retour à la version courante

Toutes les versions