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

E-commerce

Deadlock MySQL sur wp_wc_order_stats en forte charge : diagnostic et correctif

« Deadlock found when trying to get lock » sur la table de statistiques de commandes en pleine période de forte charge : la cause n'est pas le volume brut, mais la concurrence d'écriture.

Par Clément Hadrot • 17 octobre 2025 • 4 min de lecture • Aucun commentaire
Deadlock MySQL sur wp_wc_order_stats en forte charge : diagnostic et correctif

Deadlock found when trying to get lock; try restarting transaction — ce message apparaît dans les journaux d’erreur MySQL d’une boutique à fort trafic, précisément pendant les pics de commandes, et jamais en dehors. Le réflexe naturel est de croire qu’une commande a été perdue. En réalité, dans l’immense majorité des cas observés, la commande elle-même est correctement enregistrée : c’est la mise à jour asynchrone des tables de statistiques WooCommerce qui échoue.

WooCommerce Analytics maintient des tables d’agrégation dédiées, notamment wp_wc_order_stats, mises à jour en tâche de fond via Action Scheduler à chaque changement de statut de commande. Sous forte concurrence — plusieurs dizaines de commandes traitées à la seconde — plusieurs jobs tentent de mettre à jour des lignes indexées proches (souvent liées à la même journée ou au même total agrégé), et InnoDB peut détecter un cycle de verrous croisés entre deux transactions concurrentes.

Pourquoi la commande passe mais la statistique échoue

Le traitement de la commande côté client (paiement, confirmation, e-mail de commande) suit un chemin transactionnel distinct de la mise à jour des tables de lookup analytiques. Ces dernières sont recalculées en arrière-plan via des actions planifiées, généralement après le hook woocommerce_order_status_changed. Si une transaction MySQL est choisie comme « victime » du deadlock par InnoDB, elle est annulée, l’action Action Scheduler correspondante repasse en échec, mais la commande elle-même n’est jamais impactée — d’où l’impression trompeuse de « commande perdue » alors qu’il s’agit d’un agrégat statistique manquant.

Où regarder pour confirmer le diagnostic

  • Dans les journaux MySQL, cherchez le motif Deadlock found associé à des requêtes UPDATE ou INSERT ... ON DUPLICATE KEY UPDATE sur wp_wc_order_stats.
  • Dans l’écran Action Scheduler (WooCommerce > Statut > Tâches planifiées), repérez les actions en statut failed autour de l’horodatage du pic de charge.
  • Comparez le nombre de commandes réellement enregistrées avec le nombre de lignes présentes dans wp_wc_order_stats sur la même période : un écart confirme des jobs de statistiques perdus, pas des commandes perdues.
L'essentiel à retenir : Le verrou survient sur les mises à jour concurrentes des agrégats de statistiques ; La commande elle-même passe, mais le job de statistiques échoue en silence ; Le traitement différé via une file dédiée absorbe la charge sans perte

Stratégie de correction : différer et regrouper plutôt que traiter en direct

La solution la plus robuste ne consiste pas à ajouter des tentatives de reprise (retry) sur chaque échec individuel — cela ne fait que déplacer la contention dans le temps — mais à regrouper les mises à jour de statistiques par lot, avec un intervalle de traitement légèrement décalé de la transaction de commande elle-même :

add_action( 'woocommerce_order_status_changed', function ( $order_id ) {
    if ( ! as_next_scheduled_action( 'boutique_stats_recalcul', array( $order_id ) ) ) {
        as_schedule_single_action(
            time() + 30,
            'boutique_stats_recalcul',
            array( $order_id ),
            'stats-differees'
        );
    }
} );

Ce délai de trente secondes suffit, sur les cas traités, à sortir la mise à jour des statistiques du pic de contention immédiat sans introduire de retard perceptible côté rapports.

Ajuster le niveau d’isolation en dernier recours

Sur les configurations où la contention persiste malgré le différé, un ajustement du niveau d’isolation transactionnel de MySQL, de REPEATABLE READ vers READ COMMITTED, réduit la portée des verrous de type gap lock posés par InnoDB sur les index concernés. Ce changement doit être testé en recette avant application, car il modifie le comportement transactionnel de l’ensemble de l’installation, pas seulement de WooCommerce.

Sur les boutiques à fort trafic que nous suivons, la file de retraitement différé des statistiques a fait disparaître les deadlocks sans qu’aucun ajustement du moteur de base de données n’ait été nécessaire.

Prévention pour les prochains pics

Avant un évènement commercial connu à l’avance (soldes, lancement produit), surveillez le volume de tâches en échec sur Action Scheduler en temps réel, pas seulement après coup. Un tableau de bord simple comptant les actions failed par tranche de dix minutes permet de détecter la contention avant qu’elle ne s’accumule sur plusieurs heures.

En résumé

Un deadlock MySQL sur wp_wc_order_stats en forte charge ne signifie presque jamais une commande perdue, mais un job de statistiques différées entré en collision avec un autre. Différer légèrement ces mises à jour via Action Scheduler absorbe la contention sans toucher au chemin critique de la commande elle-même.

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