ediverse Explorer la plateforme

À la une PEPPOL BIS Billing 3.0 L’obligation européenne d’e-invoicing arrive : France sept 2026, Belgique janv 2026, Allemagne 2025.

Circuit Breaker Half-Open

L'état test du Circuit Breaker — celui qui évite le yo-yo entre Open et Closed et qui rend le pattern utilisable en production.

Problème

Un Circuit Breaker basique a deux états : Closed (trafic passe) ou Open (trafic bloqué). Quand le partenaire AS2 Walmart est en erreur, le circuit s'ouvre après 5 échecs en 10s, blocage 30s, puis on retente. Si Walmart répond avec une erreur (encore en panne), le circuit se referme aussitôt — et 5 nouvelles requêtes essaient, échouent, le circuit ré-ouvre. Toutes les 30s, un burst de 5 requêtes traverse : on a inventé un système qui DDoS le partenaire dès qu'il revient en ligne. Pire : les transitions Open ↔ Closed flippent en boucle.

Forces

  • Tester avant de rouvrir est nécessaire. Sans sonde, on ne sait pas si le partenaire est revenu.
  • Le test ne doit pas être massif. Si on envoie tout le trafic d'un coup et que le partenaire est fragile, on le retombe.
  • Le test doit échantillonner sans bloquer. Le service applicatif ne doit pas être bloqué pendant le test.
  • Plusieurs tests peuvent être nécessaires. Une seule requête en succès n'est pas une preuve de stabilité.

Solution

Ajouter un état intermédiaire Half-Open au Circuit Breaker. Quand le timer d'Open expire, on passe à Half-Open. Dans cet état, on autorise au maximum N requêtes-test simultanées (typiquement 1 à 3). Les autres continuent à recevoir un fail-fast 503. Si les M premières requêtes-test (typiquement M=N=3) réussissent, on retourne à Closed : trafic plein. Si une requête échoue, on retombe immédiatement à Open et on relance un timer (souvent plus long, en backoff exponentiel). Resilience4j, Polly, Istio implémentent cet état nativement.

       [Closed]
       trafic OK
          │
   threshold dépassé
   (ex: 5 échecs/10s)
          │
          ▼
       [Open]                  ◀───── échec sur 1 requête-test
       trafic bloqué                       │
       fail-fast 503                       │
          │                                │
   timer reset (ex: 30s)                   │
          │                                │
          ▼                                │
   [Half-Open]                             │
   N requêtes-test  ─────────────────────  │
   (ex: 3 max simultanées)                 │
          │                                │
   M succès consécutifs                    │
          │                                │
          ▼                                │
       [Closed] ──── trafic plein ────────  │

Implémentation EDI

Cas concret : hub EDI Java avec Resilience4j. Configuration circuitBreaker.walmart-as2 : failureRateThreshold=50%, slidingWindowSize=20, waitDurationInOpenState=30s, permittedNumberOfCallsInHalfOpenState=3. À 11h Walmart commence à répondre HTTP 503. Vers 11h00:18, le circuit passe à Open. Tout AS2 vers Walmart répond immédiatement CircuitBreakerOpenException, le service applicatif redirige sur une queue de retry. À 11h00:48, le circuit passe à Half-Open. Les 3 premiers messages AS2 sont envoyés. Walmart répond 200 OK pour les 3 : circuit revient à Closed, trafic complet reprend. Si l'un des 3 avait échoué, Half-Open repasse à Open avec timer doublé à 60s. Sur la console ops, on voit le diagramme d'état en temps réel — métriques Prometheus resilience4j_circuitbreaker_state.

Anti-patterns

  • Pas de Half-Open. Circuit qui flippe entre Closed et Open en boucle dès que le partenaire est instable : bursts de retry, partenaire qui s'effondre dès qu'il rouvre.
  • Half-Open avec trop de requêtes-test. Si N=100, on a juste retardé le DDoS de 30s.
  • Half-Open sans backoff cumulatif. Si chaque retour à Open relance toujours 30s, et que le partenaire reste down 4h, on génère 480 bursts de test. Doubler le timer à chaque échec.
  • Half-Open en lecture seule. On teste avec un GET /health mais on bascule le trafic d'écriture automatiquement : le partenaire peut avoir une santé OK et un pipeline d'écriture cassé. Tester avec une requête représentative.

Patterns liés

  • Circuit Breaker — le pattern parent.
  • Retry + backoff — le backoff exponentiel s'applique au timer Open après chaque échec.
  • Health Check — la requête-test envoyée en Half-Open est parfois un health-check.
  • Bulkhead — un Circuit Breaker par compartiment.
  • Timeout — chaque requête-test a son propre timeout, distinct du circuit.

Sources