vendredi 25 septembre 2026

À propos

Contact

Performance

admin-ajax.php saturé par un plugin de sondage en direct : profiler et corriger

Un plugin de sondage en direct multiplie les appels admin-ajax.php sur un site d'actualité. Profilage avec Query Monitor et correctif par polling espacé.

Par Clément Hadrot • 2 juillet 2020 • 5 min de lecture • Aucun commentaire
admin-ajax.php saturé par un plugin de sondage en direct : profiler et corriger

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.

L'essentiel à retenir : Un appel AJAX toutes les 2 secondes par visiteur sature le serveur ; Query Monitor isole l'action fautive en quelques clics ; Un polling espacé de 20 à 30 secondes suffit dans 90 % des cas

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.

Partager :

À propos de l'auteur

Clément Hadrot

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

Voir tous ses articles

Dans la même veine

À lire aussi