# Vider le cache des blocs Query Loop après une mise à jour en masse

> Une mise à jour en masse de 5 000 articles via WP-CLI laisse un cache de blocs Query Loop obsolète pendant des heures. Recette pour invalider de façon ciblée sans purge totale.

- Auteur : Clément Hadrot
- Publié le : 2025-12-14
- Mis à jour le : 2025-12-14
- Catégorie : Performance
- URL : https://wpmoderne.dev.wordpress-developpement.fr/performance/vider-cache-query-loop-mise-a-jour-masse/

## L’essentiel

- Le cache de blocs a son propre cycle de vie, distinct du cache de page HTML
- Une mise à jour en masse via WP-CLI ne déclenche pas toujours les bons hooks
- Invalider par requête plutôt que par groupe complet évite une purge disproportionnée

Le problème : un site d'actualités locales republiait chaque nuit environ 5 000 articles pour corriger une métadonnée de catégorie mal renseignée lors d'une migration antérieure, via un script WP-CLI exécuté en tâche planifiée. Après chaque exécution, les blocs Query Loop affichant les « derniers articles par catégorie » sur la page d'accueil et les pages de rubrique continuaient à afficher les anciennes catégories pendant plusieurs heures, alors que la base de données, elle, contenait déjà la correction.

## Pourquoi le cache de blocs ne se comporte pas comme le cache de page

Cette catégorie de problème ne concerne pas le cache de page HTML classique (FastCGI, Varnish), qui expirait normalement selon sa propre configuration et affichait bien la page mise à jour dès sa prochaine régénération. Le problème se situait dans un cache de blocs plus fin, mis en place via l'API de cache persistant de WordPress spécifiquement pour le rendu du bloc Query Loop, afin d'éviter de relancer la requête de sélection d'articles à chaque affichage. Ce cache de blocs a son propre cycle de vie et ses propres clés, indépendantes du cache de page.

> L'essentiel à retenir : Le cache de blocs a son propre cycle de vie, distinct du cache de page HTML ; Une mise à jour en masse via WP-CLI ne déclenche pas toujours les bons hooks ; Invalider par requête plutôt que par groupe complet évite une purge disproportionnée

## Pourquoi la mise à jour en masse ne déclenchait pas l'invalidation attendue

Le script WP-CLI mettait à jour les articles via `wp post update` en boucle, ce qui déclenche normalement le hook `save_post`, censé invalider le cache concerné. Le problème venait d'un détail technique précis : pour accélérer le traitement des 5 000 articles, le script utilisait l'option `--skip-cache-flush` ainsi qu'un appel direct à `$wpdb->update()` pour la métadonnée de catégorie spécifique, en contournant volontairement les hooks WordPress standards par souci de rapidité d'exécution sur un aussi grand volume. Ce contournement, raisonnable pour la performance du script lui-même, avait pour effet secondaire de ne jamais déclencher l'invalidation du cache de blocs qui dépend justement de ces hooks.

```
# Extrait du script original, qui contourne les hooks pour la vitesse
foreach ( $ids_articles as $id ) {
    $wpdb->update(
        $wpdb->postmeta,
        [ 'meta_value' => $nouvelle_categorie ],
        [ 'post_id' => $id, 'meta_key' => 'categorie_legacy' ]
    );
}
// Aucun hook save_post déclenché ici : le cache de blocs ne le sait jamais.
```

## La recette d'invalidation ciblée

Plutôt que de restaurer les hooks standards (ce qui aurait ralenti significativement le traitement des 5 000 articles, l'objectif initial du contournement), le choix a été d'ajouter une étape d'invalidation ciblée en fin de script, qui vide uniquement les clés de cache correspondant aux catégories effectivement modifiées, sans toucher au reste du cache de blocs qui reste valide.

```
$categories_modifiees = array_unique( $categories_touchees_par_le_script );

foreach ( $categories_modifiees as $slug_categorie ) {
    wp_cache_delete( 'query_loop_categorie_' . $slug_categorie, 'blocs_rendus' );
}

WP_CLI::log( sprintf(
    'Cache de blocs invalidé pour %d catégorie(s) : %s',
    count( $categories_modifiees ),
    implode( ', ', $categories_modifiees )
) );
```

Cette approche suppose que le code responsable du rendu du bloc Query Loop utilise bien une clé de cache prévisible par catégorie, ce qui était le cas ici grâce à un filtre personnalisé déjà en place sur `render_block` pour ce type de bloc.

```
add_filter( 'render_block', function ( $contenu, $bloc ) {
    if ( 'core/query' !== $bloc['blockName'] ) {
        return $contenu;
    }

    $slug_categorie = $bloc['attrs']['query']['taxQuery']['category'][0] ?? null;
    if ( ! $slug_categorie ) {
        return $contenu;
    }

    $cle = 'query_loop_categorie_' . $slug_categorie;
    $cache = wp_cache_get( $cle, 'blocs_rendus' );
    if ( false !== $cache ) {
        return $cache;
    }

    wp_cache_set( $cle, $contenu, 'blocs_rendus', HOUR_IN_SECONDS );
    return $contenu;
}, 10, 2 );
```

## Pourquoi ne pas simplement tout purger

Une purge totale du groupe de cache `blocs_rendus` après chaque exécution du script aurait été plus simple à écrire, mais aurait forcé la régénération de toutes les pages de rubrique du site, y compris celles dont aucun article n'avait changé de catégorie, avec un pic de charge inutile sur la base de données au moment précis où le trafic nocturne, bien que faible, existe encore. L'invalidation ciblée par catégorie modifiée évite ce pic disproportionné par rapport à l'ampleur réelle du changement.

- Le cache de blocs Query Loop suit un cycle de vie distinct du cache de page HTML classique.
- Un script qui contourne les hooks WordPress pour la vitesse doit prévoir sa propre invalidation.
- Invalider par clé ciblée plutôt que par groupe complet limite le pic de charge après une mise à jour massive.

> Contourner les hooks WordPress pour aller plus vite est un choix parfaitement défendable, à condition de ne jamais oublier ce que ces hooks faisaient réellement avant de les court-circuiter.

## Hors périmètre

Cette recette ne concerne pas le cache de page HTML classique, qui suivait ici son propre cycle sans lien avec le problème rencontré, et qui aurait de toute façon fini par expirer et régénérer la page correctement, contrairement au cache de blocs qui n'avait aucune raison de se renouveler de lui-même sans invalidation explicite.
