Le WordPress d'aujourd'hui, décodé pour les développeurs

Éditeur de site (FSE)

Un thème bloc pour un média à 300 000 articles : purge et cache de patterns

Retour chiffré sur la purge de cache par catégorie et l'optimisation du rendu de patterns partagés sur un site de presse à fort volume.

Par WordPress Développement • 12 janvier 2023 • 5 min de lecture • Aucun commentaire
Un thème bloc pour un média à 300 000 articles : purge et cache de patterns

wp cache flush sur un site de 300 000 articles : cette commande, exécutée par réflexe après chaque publication, a mis un serveur à genoux pendant plus d’un an avant qu’on ne comprenne pourquoi. Le thème bloc livré à ce média régional générait ses pages d’archive à partir de patterns partagés entre des dizaines de catégories, et chaque purge globale forçait la reconstruction simultanée de milliers de fragments de cache.

Ce retour d’expérience décrit la stratégie de purge ciblée par catégorie mise en place, ainsi que l’optimisation du rendu des patterns partagés qui a suivi. La question du CDN d’images et celle de la monétisation publicitaire ne sont pas traitées ici : ce sont des chantiers séparés, menés par d’autres prestataires sur ce projet.

Le problème : une purge qui coûte plus cher que le gain

Le thème bloc utilisait un unique pattern archive-header partagé par les 40 catégories du site, incluant un bloc de requête affichant les articles les plus lus de la rubrique. Chaque nouvel article déclenchait, via un hook mal calibré sur save_post, un appel à wp_cache_flush() qui vidait l’intégralité de l’object cache Redis, y compris les objets sans rapport avec la publication en cours.

Sur 300 000 articles et un trafic soutenu, cette purge globale provoquait un pic de charge à chaque publication, aux heures où la rédaction publie le plus : entre 7 h et 9 h. Le serveur régénérait alors, en quelques secondes, l’ensemble des pages d’archive du site simultanément sollicitées par les visiteurs.

La solution : purger par groupe de cache

La première étape a consisté à remplacer le flush global par des groupes de cache dédiés, un par catégorie, en s’appuyant sur wp_cache_add_non_persistent_groups() et une clé de cache composée de l’identifiant de catégorie et d’un horodatage de dernière modification stocké en option.

L'essentiel à retenir : Purge par catégorie plutôt que purge globale du site ; Rendu de pattern mis en cache avec clé de version ; Temps de génération divisé sur les pages d'archive
function wpm_bump_category_cache_version( $post_id ) {
    $categories = get_the_category( $post_id );
    foreach ( $categories as $category ) {
        $key = 'wpm_cat_cache_version_' . $category->term_id;
        update_option( $key, time(), false );
    }
}
add_action( 'save_post_post', 'wpm_bump_category_cache_version' );

function wpm_get_category_cache_key( $term_id ) {
    $version = get_option( 'wpm_cat_cache_version_' . $term_id, '0' );
    return 'archive_header_' . $term_id . '_' . $version;
}

Cette clé versionnée est ensuite utilisée pour envelopper le rendu du pattern archive-header dans un appel à wp_cache_get() / wp_cache_set(), avec une expiration longue puisque l’invalidation se fait désormais par changement de version, non par durée.

Optimiser le rendu du pattern lui-même

La purge ciblée a réglé le pic de charge lié aux publications, mais le temps de génération d’une page d’archive restait élevé : le bloc de requête « articles les plus lus » exécutait une requête WP_Query triée par un champ de compteur de vues à chaque affichage, y compris en cas de cache manquant sur une seule catégorie parmi quarante.

  • Le compteur de vues, mis à jour à chaque visite, a été déplacé vers une agrégation asynchrone via une tâche wp_cron horaire plutôt qu’un UPDATE synchrone sur chaque page vue.
  • La requête de tri a été indexée en base via meta_query avec une clé numérique dédiée, évitant un tri en mémoire côté MySQL sur des dizaines de milliers de lignes.
  • Le rendu final du pattern a été mis en cache de fragment séparément de la requête, avec sa propre clé de version, pour permettre de rafraîchir les vues sans invalider tout l’en-tête d’archive.

Les chiffres après trois mois

IndicateurAvantAprès
Temps de rendu moyen d’une archive1,8 s0,3 s
Purges de cache par jour~450~40
Pic CPU aux heures de publication92 %34 %

Le gain le plus visible pour la rédaction : la publication d’un article n’entraîne plus de ralentissement perceptible sur le reste du site, ce qui a permis de supprimer une contrainte opérationnelle absurde consistant à éviter de publier plusieurs articles à la même minute.

Un piège évité de justesse

Le versionnement par option a failli créer un nouveau problème : sans limite, la table wp_options aurait accumulé une entrée orpheline par catégorie supprimée. Un nettoyage sur le hook delete_term a été ajouté pour éviter cette dérive, un détail facile à négliger dans ce genre de dispositif.

Notre verdict

La leçon principale de ce projet tient en une phrase : sur un site à fort volume, la granularité de l’invalidation de cache compte davantage que sa durée. Un cache généreux mais purgé finement bat systématiquement un cache court mais purgé globalement. La contrepartie est une complexité de code plus élevée, qu’il faut documenter soigneusement pour l’équipe de rédaction technique qui reprendra ce thème bloc dans quelques années.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi