Carnet Wiki

API CacheLab : cibler les caches invalidés

Version 100 — il y a 1 semaine JLuc

DOC BIEN AVANCÉE - MANQUE DESCRIPTION DU 3EME JEU D’API

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 et une surcharge de la balise #CACHE qui peuvent être utilisées 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 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

Pour les sites ayant activé Mémoization par APC :
-  2 fonctions fonctions API :

controler_invalideur($action, $objets_invalidants)

et

cachelab_cibler($action, $conditions, $options)

que le code de votre site peut utiliser pour invalider uniquement les caches qui doivent être invalidés.
-  la possibilité de définir des invalidations globale par type d’objet ou événement invalidant
-  des statistiques et logs de suivi des invalidations standard et redéfinies

Pour les sites ayant activé Mémoization avec toute méthode :
-  une surcharge de #CACHE permettant de paramétrer une durée dynamique et contextuelle de cache, de vérifier le bon état sessionné ou non d’un cache et de loger cet état, de loger des métadonnées du cache ou de son html compilé : Documentation.
-  un nouveau mode de débuging par l’url :  ?var_mode=cache et  ?var_cache=oui

Chaque jeu de squelette est différents, et la mise en oeuvre des API du Cachelab se fera donc de manière adaptée. Des développements ultérieurs permettraient tout de même d’apporter des stratégies standards pour cibler des jeux de squelettes spécifiques (Zpip, Escal, SC ou autre ...) ; ces stratégies prédéfinies seraient par exemple activables par un simple clic ou automatiquement par reconnaissance du jeu de squelette employé (plus d’infos : Compléments CacheLab et todo)

API : 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 et ensuite cibler au cas par cas
-  soit au cas par cas, juste avant de cibler une invalidation avec cachelab_cibler.

API : cachelab_cibler

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

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.

RQ : Définir les constantes php CACHELAB_CLEAN et CACHELAB_CHRONO permet de changer le fonctionnement par défaut de ces options.

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 globales sur le filtrage :

  • 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
  • nb_no_data : le nombre de caches sans données. 0 normalement.
  • nb_not_array : le nombre d’erreurs "data not array". 0 normalement.
  • 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.
  • 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_not_array] => 0
    [nb_no_data] => 0
    [nb_clean] => 0
    [nb_candidats] => 2325
    [nb_alien] => 0
    [chrono] => 10.415 ms
Filtrage du chemin par ’regexp’ :
    [nb_cible] => 29
    [nb_not_array] => 0
    [nb_no_data] => 0
    [nb_clean] => 0
    [nb_candidats] => 1546
    [nb_alien] => 0
    [chrono] => 81.342 ms
Filtrage du contexte
par ’id_truc=1234’
    [nb_cible] => 0
    [nb_not_array] => 0
    [nb_no_data] => 0
    [nb_clean] => 0
    [nb_candidats] => 2340
    [nb_alien] => 0
    [chrono] => 36.954 ms

Coût pour le CPU

Les durées de filtrage sont très raisonnables et ne sont pas pénalisantes, dans la mesure où elles ne se font que lorsqu’il y a une invalidation. Le filtrage par regexp est un peu plus long que par strpos. Et l’interrogation du contexte est un peu plus lente puisqu’il faut accéder aux données.

Le filtrage par le contenu du contexte serait plus coûteux si les données du cache étaient 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 très acceptable. Cependant, lorsque les caches APC de plusieurs sites indépendants sont mutualisés et qu’ils sont tous accessibles à chaque site (ainsi que ça semble le cas sur nursit par exemple), alors 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 81.342 ms ce sera 810 ms ce qui est significatif. Et pour 50 sites ce sera plus de 4s : ce n’est plus acceptable (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 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 disposer optionnellement d’une méthode get_list qui renvoie la liste de tous les caches. Il faudrait ajouter une option selon laquelle 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 ?

Ça marche. L’API est opérationnelle 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, toutes les invalidations du cache ont été ciblées. Le taux de hit du cache SPIP est passé de en moyenne 40% à 88% et même à 99% avec le fix du bug de contamination du sessionnement.

Voir aussi

-  Compléments CacheLab et todo
-  XRay, un explorateur de cache
-  CacheLab étend la balise #CACHE
-  Plugin ’macrosession’ : usage optimisé et extension des données de session

Retour à la version courante

Toutes les versions