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 foundassocié à des requêtesUPDATEouINSERT ... ON DUPLICATE KEY UPDATEsurwp_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_statssur la même période : un écart confirme des jobs de statistiques perdus, pas des commandes perdues.

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.