Carnet Wiki

cachelab

Laboratoire du cache

Cachelab permet d’expérimenter et tester diverses stratégies de cache lorsqu’on emploie mémoization et un cache en mémoire : XCache, APC, memcache, eaccelerator (et redis aussi peut être).

L’expérimentation porte surtout le ciblage de l’invalidation du cache : c’est une alternative au fonctionnement par défaut de SPIP, qui invalide *tous* les caches à chaque modification d’une donnée en BDD.

cachelab.php est un fichier du plugin xray qui implémente 2 fonctions utiles. Il est valable, pour l’instant, uniquement pour APC cache mais vise à passer par l’API de memoization et donc être disponible pour toutes les mémoizations en mémoire.

Les fonctions cachelab_filtre et cachelab_applique permettent d’invalider sélectivement une liste de caches selon plusieurs critères :
-  présence d’une ou plusieurs chaines dans le chemin du squelette.
-  présence d’une variable avec une certaine valeur dans l’environnement du cache.

Ces 2 fonctions sont utilisables dans un code php pour faire une invalidation sélective des caches.

cachelab.php les utilise pour permettre l’étude de ces filtrage et invalidations sélectives :
-  commande par les paramètres passés dans l’url
-  mesure des microtimes

Lors des tests les temps de filtrage sont de l’ordre de 0.1 à 0.5 secondes lorsqu’il y a 10000 à 20000 caches en mémoire. C’est supportable lorsque le filtrage et l’invalidation n’ont lieu qu’à la demande lorsqu’on crée ou modifie un contenu, mais il serait intéressant tout de même de limiter cet impact.

Limites de l’expérimentation

-  Si en moyenne 98% des squelettes sont en cache, alors il est peu utile de cibler précisément quels caches doivent être invalidés (cf explications de cerdic , et dans la discussion sur la zone).

Mais en pratique, xray montre des taux de rendement du cache bien moindres (rapport : nombre d’accés en cache / nombre d’accés total) sur un site où il y a des créations ou modifications de contenus (saisie de nouveaux articles ou commentaires, choix de favoris, administration du site via ecrire).

Le rendement du cache est d’autant plus faible que les internautes créent souvent des articles, ou déposent un commentaire... ou même ajoutent ou retirent un favori avec le plugin favoris, car tous les caches sont invalidés à chacune de ces opérations !

À part pour les fichiers js compressés et css, qui ne sont jamais modifiés, le taux n’atteint jamais 98%. Il oscille entre 0% (juste après une invalidation globale) et un taux maximal d’efficacité qui sera d’autant moindre que le site est actif (20%, 60%, 70%...). Les graphiques plus loin montrent un exemple d’évolution de cette efficacité sur un site en production.

-  Cerdic attire l’attention sur le coût de l’invalidation sélective. Il pointe aussi le plugin refresher comme exemple de complexité dissuasive.

Les premières lignes jetées pour cachelab sont pourtant simples et procurent déjà de bons résultats en situation d’étude. Il faudra veiller à ce que leur amélioration ne mène pas à une usine à gaz.

Outils actuellement proposés

Le prérequis est que memoization soit actif sur votre site avec APC Cache.

cachelab permet l’invalidation des caches satisfaisant 2 sortes de critères :
-  présence de certaines chaines dans le chemin du cache (y compris le nom du fichier). On peut au choix passer une regexp, ou une suite de chaines séparées par des | (ce qui est bien plus efficace). Exemple : admin|prive|liste
-  présence d’un objet particulier dans le contexte d’évaluation d’un cache. Exemple : id_article=18.

Ce n’est appelé nulle part dans le code livré avec SPIP. Pour mettre en oeuvre ces stratégies, il faut appeler la fonction cachelab_filtre dans votre code php, au lieu d’appeler suivre_invalideur, par exemple, dans le traiter d’un CVT.

Compléments de stratégies possibles

Toutes les stratégies évoquées ci après peuvent se combiner.

Garbage collector

En l’état actuel, 90% des caches sont périmés en général (xray donne ces stats). Il serait intéressant de les effacer et de ne retenir que les caches valides. On diviserait par 10 (en moyenne) les temps de recherche pour invalidation sélective.

Ce garbage collector peut se faire
-  soit au fur et à mesure qu’on découvre des caches invalides,
-  soit globalement par un cron.

Listes ciblées préfiltrées

Une autre possibilité serait de constituer des listes ciblées : sous-ensembles pré-filtrés de caches. Une telle liste de caches serait alimentée au moment de la création d’un cache, lorsque ce dernier satisfait les critères définissant la liste. L’invalidation sélective pourrait alors directement spécifier les caches de quelle liste doivent être invalidés, en paramètre de suivre_invalideur.
-  Il y aurait un surcoût lors de la création d’un cache, pour préfiltrer le cache. Mais ce surcoût semble très réduit.
-  Les temps d’invalidation seraient alors énormément réduits (divisés par un facteur 100 ou 1000 ?), puisqu’il n’y aurait aucun filtrage requis. Tout le parcours serait utile.

Cette possibilité semble intéressante même pour filecache, lorsque le cache ne se fait pas en mémoire mais sur le disque.

TODO : Ces listes sont en fait une extension de ce que propose le plugin microcache.
Il serait probablement possible de l’implémenter en étendant microcode (en ajoutant un nouvel argument à la fonction : le nom de la liste de caches ciblés (qui décrit aussi le type de filtrage qui génère cette liste).

Ajouter un ou des pipelines

S’ils étaient implémentés dans le core, dans les fonctions de gestion du cache, des pipelines permettraient d’implémenter des stratégies alternatives de fonctionnement du cache, sans surcharger tout le fichier dans un plugin.
-  suivre_invalideur
-  la fonction qui vérifie si un cache existe et est à jour
-  un cron : revenir sur la suppression du genie invalideur

Cachecool

On pourrait au contraire souhaiter garder les caches invalides et les utiliser à la demande, comme fait cachecool. Cela n’atténue pas la charge sur le serveur, mais ça donne l’impression d’un service plus rapide.

On peut combiner :
-  les caches périmés sont supprimé par cron toutes les X minutes
-  dans l’intervalle, les caches périmés sont quand même servis. L’utilisateur doit être averti qu’il doit parfois attendre un peu pour que les affichages soient mis à jour ou disposer d’un bouton pour forcer la mise à jour de la noisette qui l’intéresse !

Selon les sites et les objets

Les stratégies d’implémentation dépendent des sites et des objets utilisés.

Il semble possible de déployer une stratégie optimisée dans le code php qui implémente un objet lorsque ce code est conçu « sur mesure » pour un site donné.

Ça semble plus difficile pour des objets génériques qui ne préjugent rien du contexte d’utilisation. La stratégie par défaut de SPIP à l’heure actuelle reste peut être bien la meilleure dans ces cas là.

Un entre-deux semble envisageable pour les squelettes généralistes de SPIP : dist, spipr, escale, SC, sarka... Tous ne se prêtent pas aussi bien à l’élaboration d’une stratégie optimisée.

Enfin, on pourrait définir un ensemble de convention de nommage des dossiers et fichiers permettant d’implémenter des régles standard de filtrage et invalidation ciblée du cache. Sur la base de ces conventions, on pourrait définir un plugin standardisé auquel auraient recours les squelettes se conformant aux conventions et souhaitant bénéficier de l’invalidation sélective du cache.

Exemple de règles :
-  nom de dossiers contenant ’liste’, ou plus précisément ’liste_monobjet’, pour les noisettes qui présentent des listes de monobjet et qui doivent être invalidées lorsqu’on crée, supprime ou modifie un monobjet
-  noms de squelettes contenant ’monobjet’ lorsqu’il faut filtrer pour les invalider sélectivement lorsqu’on crée, supprime ou modifie un monobjet avec un id_monobjet particulier

Tests et statistiques

Sur un site j’ai mesuré
-  la durée entre 2 invalidations (en secondes)
-  le ratio d’efficacité du cache au moment de l’invalidation (nb hits /nb accés)
-  le rendement du cache au moment de l’invalidation (hits×taillemémoire / accés×taillemémoire)

Rq : le ratios et le rendement sont calculs hors javascript et hors css. Pour les fichiers javascript et css, les taux sont bien plus élevés.

Les graphes suivants présentent les résultats pour 3 jours comprenant weekend (du 2018-03-09 17:00 au 2018-03-12 16:34) et présentent 80 mesures.

Au cours de ce test, il y a eu sur le site
-  700 visites par jour,
-  15 créations d’objets éditoriaux principaux
-  60 créations de forum
-  1 seule suppression de favori
-  aucune édition par l’admin (c’est le weekend !)

On voit
-  que le rendement est toujours légèrement inférieur au ratio : les gros caches sont moins souvent réutilisés. Mais globalement, la différence n’est pas significative.
-  c’est le matin, après une nuit sans création ou modification de contenu, que le cache dépasse tout juste 80%
-  la moyenne des ratios est de 52 et la médiane est de 56

ratio d’efficacité et de rendement mémoire du cache selon la durée avant invalidation

En excluant les « premières invalidations du matin » on peut zoomer :

Le calcul indique que la moyenne des ratios est de 50 et la médiane est de 55. La moitié des ratios est inférieur à 55%

Sans faire de calculs précis on voit qu’il faut en moyenne 15mn sans invalidation pour atteindre 55% d’efficacité et environ 1h30 sans invalidation pour atteindre 70%, ce qui n’arrive que dans moins de 15% des cas.

JLuc - Mise à jour :16 avril 2018 à 19h30min