# Un pic de commandes qui fait tomber le serveur un jour de solde

> Le catalogue tenait la charge sans problème, mais le serveur s'est effondré dès que les commandes ont afflué sur un seul produit vedette. Le coupable : une réduction de stock déclenchée deux fois.

- Auteur : Clément Hadrot
- Publié le : 2023-09-08
- Mis à jour le : 2023-09-08
- Catégorie : E-commerce
- URL : https://wpmoderne.dev.wordpress-developpement.fr/ecommerce/debogueur-pic-commandes-serveur-jour-solde/

## L’essentiel

- Un verrou de ligne se forme quand toutes les commandes touchent le même produit
- Un hook redondant doublait la réduction de stock à chaque commande
- wc_maybe_reduce_stock_levels évite justement ce doublon

La boutique de sneakers avait bien préparé sa vente flash : pages catalogue mises en cache, CDN pour les images, hébergement dimensionné pour le trafic attendu. Tout tenait parfaitement la charge jusqu'à l'ouverture des ventes à 10 heures pile. Puis, en quelques minutes, le site a commencé à renvoyer des erreurs serveur par intermittence, précisément au moment où les clients validaient leur commande, jamais en simple navigation sur le catalogue.

Ce décalage entre un catalogue qui tient parfaitement et un tunnel de commande qui s'effondre pointe presque toujours vers un goulot d'étranglement en écriture en base de données, différent par nature des problèmes de cache qui affectent surtout la lecture.

## Symptôme : des erreurs concentrées sur la validation de commande

Le tableau de bord d'hébergement montrait un nombre de processus PHP-FPM actifs saturé, tous bloqués en attente, avec une durée d'exécution anormalement longue pour des requêtes de validation de commande qui, en temps normal, se terminaient en quelques centaines de millisecondes. Le journal des requêtes lentes MySQL confirmait l'hypothèse : de nombreuses requêtes `UPDATE` en attente de verrou sur la même ligne de la table `wp_postmeta`, correspondant toutes à la métadonnée `_stock` d'un seul et même produit, la paire de sneakers en tête d'affiche de la vente flash.

## Diagnostic : un verrou de ligne inévitable, mais doublé par erreur

> L'essentiel à retenir : Un verrou de ligne se forme quand toutes les commandes touchent le même produit ; Un hook redondant doublait la réduction de stock à chaque commande ; wc_maybe_reduce_stock_levels évite justement ce doublon

Un verrou de ligne lors de la réduction de stock d'un produit très demandé n'a en soi rien d'anormal : MySQL doit nécessairement sérialiser les écritures concurrentes sur une même ligne pour garantir que le stock ne descend jamais sous zéro. Ce coût existe par construction dès qu'un grand nombre de clients achètent simultanément la même référence.

Ce qui n'était pas normal, en revanche, c'est la durée de ce goulot d'étranglement, largement supérieure à ce qu'expliquait le seul volume de commandes. L'enquête a fini par révéler qu'une extension de programme de fidélité, installée l'année précédente, appelait elle-même `wc_reduce_stock_levels( $order_id )` depuis un hook personnalisé accroché sur `woocommerce_payment_complete`, en plus de l'appel déjà effectué nativement par WooCommerce au moment du passage de la commande au statut « Traitement ». Chaque commande déclenchait ainsi deux réductions de stock distinctes pour le même produit, doublant sans le savoir le nombre d'écritures en attente sur cette ligne critique.

## Correctif : s'appuyer sur le garde-fou déjà prévu par WooCommerce

WooCommerce protège justement contre ce type de doublon via la fonction `wc_maybe_reduce_stock_levels( $order_id )`, qui vérifie la métadonnée de commande `_reduced_stock` avant d'agir, plutôt que `wc_reduce_stock_levels()` directement, qui n'effectue aucune vérification de ce type et réduit le stock à chaque appel, quel que soit son historique :

```
// Fautif : réduit le stock sans vérifier si ce n'est pas déjà fait
add_action( 'woocommerce_payment_complete', function ( $order_id ) {
    wc_reduce_stock_levels( $order_id );
} );

// Correctif : vérifie la métadonnée _reduced_stock avant d'agir
add_action( 'woocommerce_payment_complete', function ( $order_id ) {
    wc_maybe_reduce_stock_levels( $order_id );
} );
```

Une fois ce correctif déployé sur l'extension fautive, en accord avec son éditeur, la durée moyenne des requêtes de validation de commande est retombée à un niveau comparable à celui observé en dehors des périodes de forte affluence, malgré un volume de commandes resté similaire sur la même référence vedette.

## Prévention : traquer les appels redondants avant l'événement critique

Ce type de doublon reste difficile à détecter en conditions normales de trafic, la durée du verrou de ligne restant négligeable tant que peu de commandes concurrentes touchent le même produit. Il ne devient visible que sous une charge concentrée sur une référence unique, exactement le scénario d'une vente flash.

- Rechercher, dans le code de toutes les extensions actives, les appels directs à `wc_reduce_stock_levels()`, en vérifiant qu'ils passent bien par `wc_maybe_reduce_stock_levels()` ou une vérification équivalente de la métadonnée `_reduced_stock`.
- Reproduire, avant un événement commercial connu à l'avance, un scénario de test concentré sur une seule référence plutôt qu'une charge répartie sur l'ensemble du catalogue, qui masquerait ce type de contention.
- Surveiller, le jour de l'événement, le journal des requêtes lentes en temps réel plutôt que seulement les métriques serveur globales, ces dernières ne révélant pas toujours qu'un verrou de ligne précis est en cause.

> Un problème de performance qui n'apparaît que sur un seul produit, jamais sur l'ensemble du catalogue, mérite d'être cherché du côté des écritures concurrentes sur cette référence précise, pas du côté d'une explication générale sur la charge du serveur.

## En résumé

Le serveur n'a jamais été sous-dimensionné pour l'événement : c'est un doublon d'appel, invisible en usage courant, qui a multiplié artificiellement la contention inévitable sur le stock du produit vedette. La leçon la plus utile de cet incident tient dans la méthode de diagnostic plus que dans le correctif lui-même : partir du symptôme précis, ici une erreur concentrée sur la validation de commande, plutôt que d'une hypothèse générale sur un manque de ressources serveur.
