Cache objet WordPress : pourquoi je garde Redis Object Cache plutôt que celui de FlyingPress

Depuis la version 5.6, FlyingPress sait piloter un cache objet Redis depuis son propre tableau de bord. Un plugin de moins, une interface de moins, des métriques au même endroit que la purge des pages : l’argument est réel, et je l’entends. Pourtant, sur l’ensemble des sites que j’héberge et que j’administre, je continue de préconiser un plugin dédié, Redis Object Cache ou Object Cache Pro. Ce n’est pas une question de défiance envers FlyingPress, que j’utilise et que j’apprécie. C’est une question de maturité, de configuration et surtout de couplage. Le jour où la fonctionnalité aura fait ses preuves, l’argument du plugin unique deviendra probablement prépondérant. Ce jour n’est pas encore arrivé, et voici pourquoi, dans le détail.

Ce qu’est vraiment un cache objet

Avant de comparer deux implémentations, il faut rappeler ce que l’on remplace, parce que c’est précisément là que se joue tout le débat.

WordPress dispose nativement d’une classe WP_Object_Cache et d’une API de fonctions wp_cache_get(), wp_cache_set(), wp_cache_delete() et consorts. Par défaut, ce cache vit dans un simple tableau PHP : il naît au début de la requête et meurt à la fin. Il évite de relire dix fois la même option pendant un affichage, mais il ne survit pas d’une page à l’autre.

Un cache objet persistant remplace cette classe par une autre, qui stocke les données dans un service externe, ici Redis. Ce remplacement passe par un fichier très particulier, le drop-in wp-content/object-cache.php. Quand il est présent, wp-settings.php le charge via wp_start_object_cache(), très tôt dans le démarrage, avant même les mu-plugins et les plugins. Ce n’est donc pas une fonctionnalité de plugin au sens habituel : c’est un composant d’infrastructure, chargé avant tout le reste, dont dépend chaque lecture de données de chaque requête.

Les conséquences sont plus profondes qu’on ne le croit :

  • Les transients changent de domicile. Dès que wp_using_ext_object_cache() renvoie vrai, set_transient() n’écrit plus dans wp_options mais dans le cache, sous le groupe transient. Les anciens transients restent en base comme des fossiles.
  • alloptions devient une clé Redis. Le gros tableau sérialisé de toutes les options en autoload, celui que j’ai disséqué dans mon archéologie de wp_options, est désormais lu et réécrit dans Redis. Sa taille conditionne directement la mémoire consommée et la latence de chaque requête.
  • Les métadonnées et les requêtes passent par des lectures groupées. update_meta_cache(), _prime_post_caches() et leurs cousins s’appuient sur wp_cache_get_multiple(). Selon que le drop-in implémente ces fonctions nativement ou non, on parle d’un seul aller-retour réseau ou de plusieurs dizaines.

Le cache de pages et le cache objet sont deux couches distinctes. FlyingPress l’écrit lui-même dans sa documentation : l’un stocke du HTML final, l’autre des données. Pour un site vitrine dont toutes les pages sont servies en statique, le cache objet change peu de chose côté visiteur. Pour un WooCommerce, un espace membre, un forum ou une administration chargée, il change tout. Et c’est précisément sur ces sites-là que l’on ne peut pas se permettre d’approximation.

L’argument du plugin unique, honnêtement

Commençons par reconnaître ce que l’intégration de FlyingPress apporte, parce que ce n’est pas rien.

  • Un plugin de moins, donc une mise à jour de moins, une surface d’attaque de moins, une ligne de moins dans la liste des extensions que le client regarde avec méfiance.
  • Une interface unique : le statut de connexion, le taux de succès et la mémoire consommée sur la dernière heure apparaissent dans le même tableau de bord que la purge des pages.
  • Une mise en route quasi automatique quand Redis tourne en local avec l’extension phpredis.
  • Des constantes compatibles. FlyingPress a eu l’intelligence de reprendre la nomenclature de Redis Object Cache : WP_REDIS_HOST, WP_REDIS_PORT, WP_REDIS_DATABASE, WP_REDIS_USERNAME, WP_REDIS_PASSWORD, WP_REDIS_SCHEME, WP_REDIS_PATH, WP_REDIS_PREFIX, WP_REDIS_TIMEOUT. Le jour où l’on bascule, le wp-config.php est déjà presque prêt.

Pour un site unique, sur un hébergement mutualisé qui fournit Redis, sans trafic connecté significatif, c’est une option tout à fait défendable dès aujourd’hui. Mon propos concerne les autres cas, c’est-à-dire ceux qui m’occupent au quotidien.

Douze ans de bugs corrigés : la maturité ne se décrète pas

Redis Object Cache existe depuis 2014. Il compte plus de 400 000 installations actives, en est à sa version 2.8, et son journal des modifications se lit comme un registre de tous les pièges qu’un cache objet peut rencontrer. Quelques exemples, glanés au fil des versions :

  • une condition de concurrence dans add_or_replace(), qui permettait d’écrire une clé qu’une autre requête venait de créer ;
  • des incréments et décréments corrompus lorsque le sérialiseur igbinary était actif, corrigés en plusieurs temps, puis la préservation du TTL lors de ces mêmes opérations ;
  • des résultats faux de wp_cache_get_multiple() avec certains clients, retouchés sur une demi-douzaine de versions ;
  • la guerre des drop-ins : il a fallu empêcher W3 Total Cache, LiteSpeed Cache et le plugin Performance Lab d’écraser object-cache.php ;
  • le refus d’activer le cache quand Redis est injoignable, la purge automatique au moment de l’activation et de la désactivation, puis, dans la dernière version, la suppression des transients orphelins restés en base ;
  • l’implémentation successive de toutes les extensions de l’API apparues dans le cœur : wp_cache_get_multiple() (WordPress 5.5), wp_cache_add_multiple(), wp_cache_delete_multiple(), wp_cache_flush_runtime(), puis wp_cache_supports() et wp_cache_flush_group() avec WordPress 6.1.

Chacune de ces corrections correspond à un incident réel, rencontré sur un site réel, par quelqu’un qui a pris le temps de le signaler. Une implémentation neuve ne bénéficie pas de cet historique. Elle n’est pas nécessairement mauvaise, mais elle va redécouvrir, une par une, une partie de ces arêtes. La question n’est pas de savoir si l’équipe de FlyingPress est compétente, elle l’est. La question est de savoir sur quels sites on accepte de servir de terrain de découverte.

Vérifier ce que le drop-in sait faire

Il existe un test simple et objectif. Depuis WordPress 6.1, un drop-in peut déclarer les fonctionnalités qu’il implémente nativement via wp_cache_supports(). Si une fonction n’est pas fournie, le cœur charge les replis de cache-compat.php, qui bouclent sur les appels unitaires : c’est fonctionnellement correct, mais chaque get_multiple devient une rafale de GET successifs au lieu d’un unique MGET.

wp eval 'foreach ( [ "add_multiple", "set_multiple", "get_multiple", "delete_multiple", "flush_runtime", "flush_group" ] as $f ) { printf( "%-16s %s\n", $f, wp_cache_supports( $f ) ? "natif" : "repli" ); }'
Langage du code : JavaScript (javascript)

Lancez cette commande avec chaque drop-in, puis ouvrez Query Monitor sur une fiche produit WooCommerce avec variations. Il n’est pas rare d’y compter 200 à 400 accès au cache pour une seule page : options, métadonnées du produit et de chaque variation, termes, attributs, prix, stocks. Une partie de ces lectures arrive groupée, par exemple lorsque WooCommerce amorce les métadonnées de toutes les variations d’un coup.

Faisons un calcul d’ordre de grandeur. Sur un socket Unix local, un aller-retour Redis coûte quelques dizaines de microsecondes. Sur un Redis distant, même dans le même centre de données, on passe plutôt à 0,3 à 1 milliseconde. Prenons 300 lectures à 0,5 milliseconde :

  • en repli, chaque lecture est un GET séparé, soit environ 150 millisecondes passées à attendre le réseau ;
  • en natif, les lectures groupées partent en un seul MGET, soit environ une milliseconde pour le lot.

Dans la réalité, toutes les lectures ne sont pas groupables, et le gain se situe entre ces deux bornes. Mais l’écart suffit à expliquer pourquoi deux drop-ins « Redis » peuvent afficher des temps de réponse très différents sur le même serveur. Avec Redis en local, la pénalité se réduit fortement ; avec un Redis managé ou mutualisé sur une autre machine, elle devient la première dépense de la page.

La configuration : là où se joue l’hébergement multi-sites

C’est le point le plus concret. Quand on héberge plusieurs dizaines de sites sur les mêmes serveurs, la question n’est pas « est-ce que le cache fonctionne ? » mais « est-ce que les caches des différents sites sont étanches, dimensionnés et purgeables séparément ? ».

Voici ce que permet chaque solution, d’après leur documentation respective en octobre 2026. Je note « non documenté » plutôt que « absent » : une fonctionnalité peut exister sans être décrite, mais on n’exploite pas en production ce qu’on ne sait pas décrire.

BesoinRedis Object CacheFlyingPress 5.6
Hôte, port, base, socket UnixOuiOui
Utilisateur et mot de passe (ACL Redis 6)OuiOui
Préfixe de clésWP_REDIS_PREFIXWP_REDIS_PREFIX, défaut flying-press_
Choix du client (phpredis, Predis, Relay)Ouiphpredis uniquement
Connexion TLSOui (WP_REDIS_SSL_CONTEXT)Non documenté
Délai de lecture et nouvelles tentativesOuiSeul le délai de connexion est documenté
Sérialiseur igbinaryWP_REDIS_IGBINARYNon documenté
TTL maximal forcéWP_REDIS_MAXTTLNon documenté
Groupes globaux et ignorésOuiNon documenté
Purge limitée au préfixeWP_REDIS_SELECTIVE_FLUSHNon documenté
Réplication, Sentinel, clusterOuiNon documenté
Commandes WP-CLIwp redis …Non documenté
Intégration Query MonitorOuiNon documenté
MétriquesTableau de bord et diagnosticsTaux de succès et mémoire, dernière heure

Trois lignes de ce tableau méritent qu’on s’y arrête.

Le préfixe et la collision silencieuse

Si deux sites partagent la même instance Redis et la même base numérotée, seule la clé distingue leurs données. Un préfixe fixe et identique d’un site à l’autre, comme un défaut à flying-press_, ferait lire au site B la clé alloptions du site A. Je n’ai pas vérifié si le drop-in de FlyingPress ajoute un discriminant automatique, comme le nom de la base ou le préfixe des tables, et c’est justement le problème : sur un serveur mutualisé, on ne parie pas là-dessus. Quelle que soit la solution retenue, la règle est la même : un préfixe unique et explicite par site, défini dans wp-config.php.

La purge qui vide le voisin

Avec Redis Object Cache, wp_cache_flush() exécute par défaut un FLUSHDB : toute la base numérotée est vidée, y compris les clés des autres sites qui la partageraient. Un avis récent sur le répertoire WordPress le rappelle d’ailleurs. Deux parades existent :

  1. WP_REDIS_SELECTIVE_FLUSH, qui ne supprime que les clés portant le préfixe du site au moyen d’un script Lua. Attention : un script Lua est atomique et bloque Redis pendant son exécution. Sur un gros espace de clés, c’est précisément pour cela que le plugin a ajouté WP_REDIS_FLUSH_TIMEOUT.
  2. Une base numérotée par site (Redis en propose 16 par défaut, paramètre databases), voire une instance par site sur son propre socket, avec sa propre limite mémoire. C’est ma configuration de référence.

FlyingPress ne documente pas la portée de sa purge. FLUSHDB ou suppression par préfixe ? Sur un serveur mono-site, peu importe. Sur un serveur partagé, c’est toute la différence entre un bouton anodin et un incident chez un autre client.

Le client unique

FlyingPress exige phpredis. C’est le meilleur choix par défaut, et je ne lui reproche rien sur ce point. Mais Redis Object Cache accepte aussi Relay, un client qui maintient une copie des clés chaudes dans la mémoire partagée de PHP-FPM et que Redis invalide par notification. Sur un WooCommerce chargé, supprimer l’aller-retour réseau pour les clés les plus lues n’est pas un raffinement : c’est souvent le principal gain restant une fois le reste optimisé.

Sérialisation, mémoire et éviction : les réglages qui comptent

Le choix du drop-in ne se limite pas à la connexion. Il conditionne la manière dont les données occupent la mémoire.

La sérialisation. Par défaut, les valeurs sont stockées avec serialize() de PHP, verbeux par nature. igbinary produit une représentation binaire nettement plus compacte sur les tableaux imbriqués, ce qui est exactement la forme d’alloptions, des métadonnées et des objets WP_Post. Moins d’octets, c’est moins de mémoire Redis, moins de bande passante et une désérialisation plus rapide. Redis Object Cache l’active avec WP_REDIS_IGBINARY. Object Cache Pro va plus loin avec la compression (lzf, lz4 ou zstd).

L’éviction. Voici le piège que je rencontre le plus souvent chez les sites que l’on me confie. WordPress écrit une majorité de clés sans expiration : wp_cache_set() a un TTL de zéro par défaut. Si Redis est configuré avec maxmemory-policy volatile-lru, il ne peut évincer que les clés munies d’un TTL. Une fois la mémoire pleine, il ne trouve rien à supprimer et refuse les écritures. Pour un Redis dédié au cache, la politique correcte est allkeys-lru (ou allkeys-lfu). En complément, WP_REDIS_MAXTTL permet d’imposer une durée de vie maximale à toutes les clés.

La persistance. Un cache n’a pas besoin de survivre à un redémarrage. Les sauvegardes RDB déclenchent un fork() dont la copie sur écriture peut provoquer des pics de latence sur une instance volumineuse. Pour une instance purement cache :

# /etc/redis/redis-cache.conf
maxmemory 512mb
maxmemory-policy allkeys-lru
save ""
appendonly no
unixsocket /run/redis-cache/redis.sock
unixsocketperm 770
Langage du code : PHP (php)

Et côté WordPress, avec Redis Object Cache :

define( 'WP_REDIS_SCHEME', 'unix' );
define( 'WP_REDIS_PATH', '/run/redis-cache/redis.sock' );
define( 'WP_REDIS_DATABASE', 3 );
define( 'WP_REDIS_PREFIX', 'client-boutique:' );
define( 'WP_REDIS_IGBINARY', true );
define( 'WP_REDIS_MAXTTL', 86400 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
Langage du code : JavaScript (javascript)

Rien de cela n’est exotique. Mais chaque ligne correspond à un comportement que je dois pouvoir garantir à un client, et la moitié de ces lignes n’ont aujourd’hui pas d’équivalent documenté côté FlyingPress.

Quand Redis tombe

Un cache objet se juge autant à son comportement en panne qu’en régime nominal. Trois questions se posent.

Que voit le visiteur ? Redis Object Cache affiche par défaut une page d’erreur explicite si Redis est injoignable, ou bascule en mode dégradé si l’on définit WP_REDIS_GRACEFUL. Les deux choix se défendent, et surtout ils sont documentés. FlyingPress indique qu’une erreur apparaît dans son tableau de bord, mais ne précise pas ce qui se passe en façade.

Combien de temps attend chaque requête ? Avec un hôte TCP qui ne répond plus, chaque requête PHP patiente jusqu’au délai de connexion, une seconde par défaut chez les deux. Sur un socket Unix absent, l’échec est immédiat. C’est une raison de plus de préférer le socket quand Redis est local.

Les données sont-elles encore justes au retour ? C’est le point que presque personne ne mentionne, et il ne dépend pas du plugin. En mode dégradé, WP_REDIS_GRACEFUL compris, WordPress continue d’écrire en base sans pouvoir invalider les clés correspondantes dans Redis. Si la panne était une coupure réseau ou une saturation plutôt qu’un redémarrage, Redis a conservé ses anciennes valeurs. Au retour du service, il les ressert : une commande au mauvais statut, un stock faux, une option ancienne, une session utilisateur incohérente.

Le mode dégradé de Redis Object Cache protège donc la disponibilité, pas la cohérence. Ce n’est pas un défaut de l’un ou de l’autre plugin : c’est la nature même d’un cache qu’on a cessé d’invalider. La procédure est identique avec Redis Object Cache, Object Cache Pro ou FlyingPress, et elle n’est pas facultative :

wp cache flush

immédiatement après le retour de Redis, idéalement déclenchée par la supervision plutôt que par un humain qui y pensera trop tard. La seule différence entre les solutions tient à la confiance que l’on peut avoir dans cette purge : avec Redis Object Cache, je sais exactement ce qu’elle supprime et ce qu’elle épargne. Avec FlyingPress, je dois encore le découvrir.

Observer et exploiter

Sur un parc de sites, je n’administre pas à la souris. Redis Object Cache fournit wp redis status, wp redis enable, wp redis disable et wp redis update-dropin, ce qui permet de scripter les déploiements, de vérifier l’état d’un serveur entier en une boucle et de corriger un drop-in obsolète après une mise à jour. Il s’intègre aussi à Query Monitor, qui affiche le taux de succès et le détail des appels au cache pour la requête en cours : c’est l’outil qui permet de repérer le plugin qui contourne le cache ou qui le sature.

Côté Redis lui-même, les outils sont les mêmes quel que soit le drop-in, et ils méritent d’être connus :

redis-cli -s /run/redis-cache/redis.sock INFO stats | grep keyspace_
redis-cli -s /run/redis-cache/redis.sock INFO memory | grep used_memory_human
redis-cli -s /run/redis-cache/redis.sock --memkeys
redis-cli -s /run/redis-cache/redis.sock SLOWLOG GET 10

keyspace_hits et keyspace_misses donnent le vrai taux de succès, --memkeys révèle la clé obèse (presque toujours alloptions ou un transient de plugin mal conçu), SLOWLOG capture les commandes lentes. En revanche, jamais de KEYS * ni de MONITOR sur une instance de production chargée : le premier bloque Redis, le second divise ses performances.

Le graphique d’une heure de FlyingPress est agréable pour rassurer un client. Il ne remplace pas un outillage scriptable.

Le vrai sujet : le couplage

Tout ce qui précède relève de la maturité, et la maturité s’acquiert avec le temps. L’argument suivant, lui, est structurel.

Un plugin de cache de pages est un composant de façade. Il évolue vite, il touche au CSS, au JavaScript, au lazy-loading, au préchargement, aux règles de réécriture. C’est le premier plugin que l’on désactive pour diagnostiquer un affichage cassé, et l’un de ceux que l’on remplace le plus volontiers quand un concurrent fait mieux. Un cache objet est un composant d’infrastructure. Il doit être stable, prévisible, et changer le moins souvent possible.

Mettre les deux dans le même plugin revient à lier leur cycle de vie. Concrètement :

  • Je désactive FlyingPress pour isoler un conflit JavaScript. Que devient le drop-in ? S’il est supprimé, je viens de modifier le profil de performance de tout le back-office pendant mon diagnostic, et je purge sans le vouloir. S’il est conservé, il continue de tourner sans le plugin censé le gérer. La documentation ne tranche pas.
  • Une mise à jour de FlyingPress corrige une régression d’optimisation des images. Elle embarque aussi une nouvelle version du drop-in. Je ne peux pas accepter l’une sans l’autre.
  • Je décide un jour de changer de cache de pages. Je perds mon cache objet au passage et je dois le reconstruire avec un autre outil.

C’est exactement le principe que je défends depuis longtemps à propos de la philosophie Unix : faire une chose et la faire bien. Un plugin de moins dans la liste, ce n’est pas une dépendance de moins. C’est la même dépendance, rangée dans une boîte qui en contient d’autres.

Et Object Cache Pro ?

Object Cache Pro est la réécriture commerciale de Redis Object Cache, par le même auteur. Ce qu’il ajoute est substantiel : compression, connexions TLS, préchargement des clés les plus demandées, statistiques détaillées, optimisations spécifiques à WooCommerce, et une intégration poussée avec Relay. Il revendique une couverture de tests unitaires complète, ce qui, pour un composant chargé avant tout le reste, n’est pas un argument marketing.

Il a un prix : de l’ordre de 95 dollars par mois et par site au tarif public, ce qui le réserve aux boutiques et plateformes où chaque milliseconde de page dynamique se traduit en chiffre d’affaires. Certains hébergeurs l’incluent dans leurs offres, Cloudways et GridPane notamment. Pour un WooCommerce à fort trafic connecté, un multisite conséquent ou une plateforme d’adhésion, c’est mon premier choix. Pour tout le reste, Redis Object Cache gratuit fait le travail, et le fait bien.

Ce qu’on m’opposera

« Un plugin de moins, c’est toujours mieux. » En nombre de lignes dans l’administration, oui. En nombre de composants à comprendre, non : le cache objet est là dans les deux cas. La seule différence est de savoir qui le maintient et à quel rythme.

« FlyingPress est réactif, ils corrigeront vite. » Je n’en doute pas. Mais corriger vite un bug suppose qu’il ait d’abord été rencontré. Je préfère que d’autres le rencontrent sur leurs sites avant les miens.

« Les métriques de FlyingPress suffisent. » Pour vérifier que le cache fonctionne, oui. Pour comprendre pourquoi une page met 800 millisecondes en administration, non.

« Mon site est petit, tout ça ne me concerne pas. » C’est exact, et je le disais plus haut : un site seul, sur un Redis dédié, sans trafic connecté, peut activer l’option de FlyingPress sans grand risque. Mon conseil vise les sites qui ont quelque chose à perdre.

Ce qui me fera changer d’avis

Je ne suis pas dogmatique, et je m’attends sincèrement à réviser cette position. Voici les critères que je surveillerai :

  1. Une documentation des comportements critiques : portée de la purge, conduite en cas de panne de Redis, sort du drop-in à la désactivation du plugin.
  2. wp_cache_supports() à « natif » sur l’ensemble des fonctions groupées.
  3. igbinary, TLS, délai de lecture et nouvelles tentatives configurables.
  4. Un préfixe par défaut propre à chaque site, ou une documentation qui affirme clairement le contraire et impose de le définir.
  5. Des commandes WP-CLI pour l’état, l’activation et la purge.
  6. Six à douze mois de journal des modifications sans correctif de régression sur la partie cache objet.

Le jour où ces cases seront cochées, la migration sera triviale, puisque les constantes sont déjà communes :

# Purge et retrait propre du drop-in actuel
wp cache flush
wp redis disable
wp plugin deactivate redis-cache

# Vérifier qu'aucun drop-in ne subsiste
ls -l wp-content/object-cache.php

# Activer l'option dans FlyingPress, puis contrôler
wp eval 'var_dump( wp_using_ext_object_cache() );'
Langage du code : PHP (php)

Pensez simplement à conserver votre WP_REDIS_PREFIX ou à purger après la bascule : un changement de préfixe laisse derrière lui toutes les anciennes clés, qui occuperont la mémoire jusqu’à leur éviction.

Mon avis

FlyingPress a fait le bon choix en intégrant un cache objet, et il l’a fait intelligemment en reprenant les constantes de l’outil de référence. Pour un site isolé, c’est déjà une option raisonnable. Mais un cache objet n’est pas une option d’optimisation parmi d’autres : c’est la couche qui se place entre WordPress et ses propres données, chargée avant tout le reste, sur chaque requête. Sur une boutique, un forum ou un parc de sites mutualisés, j’attends de cette couche une décennie de bugs corrigés, une configuration complète et un cycle de vie indépendant de celui de mon cache de pages.

Aujourd’hui, Redis Object Cache coche ces trois cases, Object Cache Pro les coche plus haut encore, et FlyingPress coche la première intention. Je reviendrai sur ce billet quand la fonctionnalité aura vieilli. D’ici là, deux plugins valent mieux qu’un.


Écrivez quelques éclats d'âme...

Dans l'ombre vacillante d'une chandelle, où les murmures du vent se mêlent aux secrets d'un vieux parchemin, je vous invite à tisser une toile de mots. Écrivez quelques éclats d'âme – rêve, étoile, abîme, étreinte, brume – et laissez-les danser sur la page, comme des lucioles dans une nuit d'encre. Que diriez-vous de les entrelacer dans une phrase, un souffle, une histoire ?

S’abonner
Notification pour
guest
0 Commentaires
Le plus ancien
Le plus récent Le plus populaire

Source : https://paul.argoud.net/cache-objet-wordpress-redis-object-cache-flyingpress/