# wp_schedule_single_event empilé par erreur : une file qui ne se vide jamais

> Chaque envoi de formulaire ajoutait un événement wp-cron sans jamais vérifier s'il existait déjà. Résultat : une file qui grossit indéfiniment.

- Auteur : Clément Hadrot
- Publié le : 2025-02-04
- Mis à jour le : 2025-02-04
- Catégorie : Performance
- URL : https://wpmoderne.dev.wordpress-developpement.fr/performance/wp-schedule-single-event-file-qui-ne-vide-jamais/

## L’essentiel

- Aucune vérification avant la planification
- La table wp_options gonflait à chaque soumission
- wp_next_scheduled() a suffi à stopper l'hémorragie

`wp cron event list --format=count` renvoyait 14 212. Sur un site associatif qui ne recevait pourtant qu'une trentaine de formulaires par jour, ce chiffre n'avait aucun sens tant qu'on n'avait pas ouvert le détail des événements planifiés un par un.

Le mécanisme de wp-cron de WordPress ne s'exécute pas en tâche de fond au sens strict : il se déclenche à chaque visite du site, via une requête discrète vers `wp-cron.php`, dès qu'un événement planifié est arrivé à échéance. Plus la file d'événements en attente est longue, plus WordPress passe de temps à la parcourir à chaque chargement de page, ce qui explique pourquoi un temps de génération qui se dégrade lentement, semaine après semaine, mérite qu'on aille y regarder de près.

## Le symptôme : un temps de génération qui grimpe sans cause apparente

Rien dans les journaux d'erreurs ne signalait de problème. Le temps de génération de la page d'accueil, suivi via un en-tête `Server-Timing` personnalisé, était passé de 180 à 340 millisecondes en trois semaines, sans qu'aucune modification de code n'ait été déployée sur cette période. Le suspect naturel — une extension de sécurité récemment installée — a été écarté après désactivation temporaire : le ralentissement persistait.

## Le diagnostic : une planification sans garde-fou

> L'essentiel à retenir : Aucune vérification avant la planification ; La table wp_options gonflait à chaque soumission ; wp_next_scheduled() a suffi à stopper l'hémorragie

La commande `wp cron event list` a révélé des milliers d'événements portant le même nom, `wpm_relance_formulaire`, chacun programmé quelques minutes après une soumission de formulaire de contact. Le code responsable, ajouté par un développeur pour envoyer un rappel automatique en cas d'inscription incomplète, ressemblait à ceci :

```
add_action( 'wpm_formulaire_soumis', function ( $email ) {
    wp_schedule_single_event(
        time() + 3 * DAY_IN_SECONDS,
        'wpm_relance_formulaire',
        array( $email )
    );
} );
```

Le problème saute aux yeux une fois isolé : rien n'empêchait qu'un même visiteur soumette le formulaire plusieurs fois — par une double validation accidentelle, ou en corrigeant une erreur de saisie — et chaque soumission ajoutait un nouvel événement, sans jamais vérifier qu'une relance était déjà programmée pour la même adresse. Les événements passés s'exécutaient bien, mais leur volume avait fini par alourdir sérieusement la table `wp_options`, où wp-cron stocke sa liste sous la clé `cron` sous forme de tableau sérialisé.

## Le correctif : vérifier avant de planifier

La fonction `wp_next_scheduled()` permet précisément de vérifier qu'un événement portant les mêmes arguments n'est pas déjà en attente avant d'en ajouter un nouveau :

```
add_action( 'wpm_formulaire_soumis', function ( $email ) {
    if ( ! wp_next_scheduled( 'wpm_relance_formulaire', array( $email ) ) ) {
        wp_schedule_single_event(
            time() + 3 * DAY_IN_SECONDS,
            'wpm_relance_formulaire',
            array( $email )
        );
    }
} );
```

Ce contrôle, ajouté en une ligne, a immédiatement stoppé l'accumulation. Restait à purger les événements déjà empilés, ce que la commande `wp cron event delete` a permis de faire en ciblant le hook concerné, événement par événement, avant qu'un script ponctuel ne vienne nettoyer les doublons restants directement via `_get_cron_array()` et `_set_cron_array()`.

## Ce que la mesure a confirmé après nettoyage

- Le temps de génération de la page d'accueil est revenu à 175 millisecondes, une valeur cohérente avec la mesure d'origine.
- La taille de l'entrée `cron` dans `wp_options` est passée de plus de 900 kilo-octets à moins de 4 kilo-octets.
- Le déclenchement de `wp-cron.php` à chaque visite, jusque-là ralenti par la désérialisation d'un tableau volumineux, s'exécute désormais quasi instantanément.

## Prévenir plutôt que guérir

Au-delà du correctif ponctuel, l'équipe a ajouté une vérification périodique : un événement planifié une fois par semaine compte désormais le nombre total d'entrées dans la file wp-cron et envoie une alerte si ce nombre dépasse un seuil raisonnable. Un simple garde-fou qui aurait permis de détecter le problème bien avant qu'il n'affecte la page d'accueil.

> Planifier un événement sans vérifier son existence préalable revient à empiler des rappels sans jamais les compter : la file grossit en silence jusqu'à peser sur chaque page du site.

## En résumé

Un simple oubli — l'absence de vérification avant planification — a transformé une fonctionnalité de relance en fuite silencieuse qui a fini par dégrader le temps de génération de l'ensemble du site. La correction tient en une condition, mais le diagnostic a demandé de remonter jusqu'au contenu réel de la table `wp_options`, là où wp-cron range sa file d'attente.
