Le site d’un client dans le secteur de l’actualité sportive commence à recevoir des alertes d’hébergeur : charge CPU anormalement élevée, y compris la nuit. Rien n’a changé côté contenu, aucune campagne publicitaire n’explique le pic. Le coupable finit par être trouvé du côté d’une extension a priori anodine : un widget de sondage en direct affiché sur toutes les pages d’articles, censé mettre à jour un pourcentage de votes en temps réel.
Ce cas illustre un piège classique de WordPress : admin-ajax.php n’a jamais été pensé pour du temps réel à grande échelle, et une extension mal conçue peut transformer un simple compteur en génératrice de charge continue. Voici comment le problème a été isolé, puis corrigé sans toucher au cœur de WordPress.
Le symptôme observé
Le tableau de bord d’hébergement montrait une activité PHP-FPM constante, même en dehors des heures de forte affluence. Le fichier de logs d’accès confirmait la piste : des milliers de lignes par heure pointant vers /wp-admin/admin-ajax.php, avec un paramètre action=poll_status revenant en boucle depuis les mêmes adresses IP, à intervalles très réguliers.
Un simple grep sur les logs suffisait à quantifier l’ampleur du problème :
grep "action=poll_status" access.log | wc -l
Le résultat, rapporté au nombre de visiteurs uniques actifs sur la page du sondage, donnait une fréquence proche d’un appel toutes les deux secondes par onglet ouvert. Sur un pic de fréquentation, cela représentait plusieurs centaines de requêtes PHP simultanées, chacune ouvrant une connexion à la base de données pour lire et parfois écrire un compteur de votes.
Le diagnostic avec Query Monitor
Plutôt que de deviner, l’extension Query Monitor a permis de confirmer précisément ce qui se passait côté serveur pendant un appel AJAX. En filtrant sur les hooks wp_ajax_poll_status et wp_ajax_nopriv_poll_status, l’onglet « Hooks & Actions » de Query Monitor affichait le callback exact enregistré par l’extension, et l’onglet « Requêtes » montrait deux requêtes SQL exécutées à chaque appel : une lecture du total de votes, une écriture systématique d’une ligne de log dans une table personnalisée, même quand l’utilisateur n’avait pas voté.
Ce deuxième point était la vraie cause du problème : l’extension journalisait chaque rafraîchissement, votant ou non, dans une table sans index sur la colonne de date. Sur une table qui grossissait de plusieurs milliers de lignes par jour, les requêtes de lecture ralentissaient elles-mêmes de façon croissante.

Le correctif appliqué
Trois changements ont suffi à ramener la charge à un niveau normal, sans désactiver la fonctionnalité pour les visiteurs :
- Espacement du polling côté JavaScript, porté de 2 secondes à 25 secondes, avec un léger facteur aléatoire pour éviter que tous les clients ne rafraîchissent en même temps.
- Suppression de l’écriture de log à chaque appel : seule une variation réelle du total de votes déclenche désormais une écriture, via une comparaison simple avant l’
INSERT. - Mise en cache du résultat lu pendant 10 secondes avec
wp_cache_set()et un groupe de cache dédié, pour que deux visiteurs proches dans le temps ne déclenchent pas deux lectures identiques en base.
Le correctif côté PHP ressemblait à ceci, simplifié pour l’exemple :
add_action( 'wp_ajax_poll_status', 'sondage_get_status' );
add_action( 'wp_ajax_nopriv_poll_status', 'sondage_get_status' );
function sondage_get_status() {
$cache_key = 'sondage_status_' . absint( $_GET['poll_id'] );
$status = wp_cache_get( $cache_key, 'sondage' );
if ( false === $status ) {
$status = sondage_read_from_db( absint( $_GET['poll_id'] ) );
wp_cache_set( $cache_key, $status, 'sondage', 10 );
}
wp_send_json_success( $status );
}
Cette version évite la double requête à chaque appel dès qu’un autre visiteur a interrogé le même sondage dans les dix secondes précédentes, tout en gardant une fraîcheur des données largement suffisante pour un affichage de pourcentages.
La prévention pour la suite
Au-delà du correctif immédiat, l’équipe a ajouté une surveillance légère : une alerte se déclenche désormais si le nombre d’appels vers une action AJAX donnée dépasse un seuil sur une fenêtre de cinq minutes, via un comptage simple dans les journaux d’accès agrégés par l’hébergeur. Cela permet de repérer un futur plugin mal conçu avant qu’il ne provoque un incident, plutôt qu’après.
Il est aussi utile, avant d’installer une extension qui promet du « temps réel », de vérifier dans son code source la fréquence de polling par défaut et la présence ou non d’un mécanisme de cache. Beaucoup d’extensions de sondage, de compteur de visiteurs ou de chat en direct partagent ce même défaut de conception.
En résumé
Un plugin de sondage en direct mal optimisé peut transformer admin-ajax.php en point de saturation, même sur un site à trafic modéré. Query Monitor permet d’identifier rapidement l’action et les requêtes SQL en cause. Le correctif ne nécessite ni Redis, ni Heartbeat API, ni changement d’hébergement : un polling plus espacé côté client et un cache court côté serveur suffisent la plupart du temps à retrouver une charge stable.