Une extension d’enrichissement de fiches produit interrogeait, sur chaque affichage de page, une API tierce de traduction automatique pour proposer une version localisée du descriptif. Le jour où ce service externe a connu une panne de plusieurs heures, chaque page produit du site client a mis jusqu’à huit secondes à s’afficher, le temps que la requête HTTP finisse par expirer avant de continuer le rendu normalement.
Le code en lui-même gérait correctement les erreurs, avec un bloc de traitement des échecs et un contenu de repli affiché en cas d’échec. Le vrai problème n’était pas l’absence de gestion d’erreur, mais sa répétition : chaque page continuait d’appeler le service en panne, d’attendre son délai d’expiration, avant d’abandonner. Un disjoncteur applicatif, ou circuit breaker, existe précisément pour éviter cette attente répétée.
Le principe du disjoncteur
Un disjoncteur applicatif se comporte comme son équivalent électrique : après un nombre défini d’échecs consécutifs, il coupe le circuit et refuse d’appeler le service concerné pendant une durée fixée, en renvoyant immédiatement une réponse de repli sans même tenter la requête réseau. Après cette durée, il autorise un nombre restreint de tentatives pour vérifier si le service a repris, avant de refermer complètement le circuit ou de le rouvrir si l’échec persiste.
Ce mécanisme repose sur trois états distincts : fermé, où les appels passent normalement ; ouvert, où les appels sont bloqués immédiatement ; semi-ouvert, où un nombre limité d’appels de test est autorisé pour évaluer la reprise du service.
Implémentation avec les transients
Sur un site WordPress, l’API des transients suffit à implémenter ce mécanisme sans dépendance externe, en s’appuyant sur leur expiration automatique pour matérialiser la durée d’ouverture du circuit :

class Disjoncteur_Traduction {
const SEUIL_ECHECS = 5;
const DUREE_OUVERTURE = 120; // secondes
public static function circuit_ouvert() {
return (bool) get_transient( 'traduction_circuit_ouvert' );
}
public static function signaler_echec() {
$compteur = (int) get_transient( 'traduction_compteur_echecs' );
$compteur++;
if ( $compteur >= self::SEUIL_ECHECS ) {
set_transient( 'traduction_circuit_ouvert', true, self::DUREE_OUVERTURE );
delete_transient( 'traduction_compteur_echecs' );
return;
}
set_transient( 'traduction_compteur_echecs', $compteur, 60 );
}
public static function signaler_succes() {
delete_transient( 'traduction_compteur_echecs' );
delete_transient( 'traduction_circuit_ouvert' );
}
}
L’appel réel à l’API tierce se protège désormais en vérifiant l’état du disjoncteur avant même de lancer la requête HTTP :
function obtenir_traduction_produit( $texte, $langue_cible ) {
if ( Disjoncteur_Traduction::circuit_ouvert() ) {
return $texte; // repli immédiat, aucun appel réseau tenté
}
$reponse = wp_remote_post( 'https://api-traduction.example.com/v1/translate', array(
'timeout' => 3,
'body' => array( 'text' => $texte, 'target' => $langue_cible ),
) );
if ( is_wp_error( $reponse ) || wp_remote_retrieve_response_code( $reponse ) >= 500 ) {
Disjoncteur_Traduction::signaler_echec();
return $texte;
}
Disjoncteur_Traduction::signaler_succes();
return wp_remote_retrieve_body( $reponse );
}
Une fois le circuit ouvert, chaque page cesse immédiatement de tenter l’appel réseau, remplaçant un délai d’expiration de plusieurs secondes par un retour instantané au contenu original non traduit, le temps que le service tiers se rétablisse.
Le piège de la réouverture en masse
Une implémentation naïve rouvrirait le circuit d’un coup après expiration du transient, ce qui recréerait immédiatement une vague de requêtes simultanées vers un service qui vient tout juste de reprendre, risquant de le refaire tomber. La version présentée ici s’appuie sur l’expiration naturelle du transient traduction_circuit_ouvert, ce qui autorise de nouveau les tentatives progressivement, page par page, plutôt que toutes en même temps, puisque chaque page vérifie l’état au moment de son propre chargement.
Ce que le disjoncteur ne remplace pas
- Un timeout HTTP correctement réglé reste indispensable : le disjoncteur limite la fréquence des tentatives, pas la durée de chacune d’elles.
- Une file de nouvelles tentatives différées, pour les opérations qui ne peuvent pas se contenter d’un simple contenu de repli, comme l’envoi d’une commande à synchroniser.
- Une alerte transmise à l’équipe technique quand le circuit s’ouvre, pour ne pas découvrir la panne uniquement par un client mécontent.
Un principe qui vaut pour toute intégration d’API tierce dans une extension livrée à un client : la panne du service externe doit dégrader l’expérience du site, jamais son temps de réponse au point de le rendre inutilisable.
En résumé
Sur le site client concerné, l’ajout de ce disjoncteur a ramené le temps de chargement des pages produit à leur valeur normale dès la deuxième requête suivant l’ouverture du circuit, contre plusieurs heures de lenteur généralisée auparavant. Un mécanisme de quelques dizaines de lignes, appuyé uniquement sur l’API des transients déjà native à WordPress, suffit à éviter qu’une panne externe ne devienne une panne du site tout entier.