veza/veza-backend-api/docs/CIRCUIT_BREAKERS.md
2025-12-16 11:23:49 -05:00

6 KiB

Circuit Breakers — Documentation

Date: 2025-01-27
Status: IMPLEMENTED - MOD-P2-007


Vue d'ensemble

Les circuit breakers protègent l'application contre les dépendances externes lentes ou indisponibles en interrompant automatiquement les appels après un seuil d'échecs.

Implémentation

  • Bibliothèque: github.com/sony/gobreaker
  • Wrapper: internal/services/circuit_breaker.go
  • Métriques: internal/metrics/circuit_breaker.go

Configuration

Paramètres par défaut

MaxRequests: 3          // Requêtes simultanées max
Interval: 60s          // Réinitialisation des compteurs
Timeout: 30s           // Délai avant half-open
ReadyToTrip: 5 échecs  // Seuil pour ouvrir le circuit

États du Circuit Breaker

  1. Closed (Fermé): État normal, toutes les requêtes passent
  2. Open (Ouvert): Circuit ouvert après 5 échecs consécutifs, requêtes rejetées
  3. Half-Open (Demi-ouvert): Après 30s, permet quelques requêtes de test

Utilisation

Création d'un client avec circuit breaker

import (
    "veza-backend-api/internal/services"
    "go.uber.org/zap"
)

logger := zap.NewNop()
httpClient := &http.Client{Timeout: 10 * time.Second}
cbClient := services.NewCircuitBreakerHTTPClient(
    httpClient,
    "my-service",  // Nom du circuit breaker (pour métriques)
    logger,
)

Exécution d'une requête

req, _ := http.NewRequest("GET", "https://api.example.com/data", nil)
resp, err := cbClient.Do(req)
if err != nil {
    // Gérer l'erreur (circuit ouvert, timeout, 5xx, etc.)
    return err
}
defer resp.Body.Close()

Avec contexte (timeout/cancellation)

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

req, _ := http.NewRequest("GET", "https://api.example.com/data", nil)
resp, err := cbClient.DoWithContext(ctx, req)

Services Intégrés

1. Stream Service

Fichier: internal/services/stream_service.go

circuitBreaker: NewCircuitBreakerHTTPClient(
    httpClient,
    "stream-service",
    logger,
)

Utilisation: Appels HTTP vers le serveur de streaming pour transcodage.

2. OAuth Service

Fichier: internal/services/oauth_service.go

circuitBreaker: NewCircuitBreakerHTTPClient(
    httpClient,
    "oauth-service",
    logger,
)

Utilisation: Appels HTTP vers les providers OAuth (Google, GitHub, Discord).


Métriques Prometheus

Les métriques suivantes sont exposées automatiquement:

veza_circuit_breaker_state

Type: Gauge
Labels: circuit_breaker_name
Valeurs:

  • 0 = Closed
  • 1 = Half-Open
  • 2 = Open

Exemple:

veza_circuit_breaker_state{circuit_breaker_name="stream-service"} 0

veza_circuit_breaker_requests_total

Type: Counter
Labels: circuit_breaker_name, result (success|failure|rejected)

Exemple:

veza_circuit_breaker_requests_total{circuit_breaker_name="stream-service",result="success"} 150
veza_circuit_breaker_requests_total{circuit_breaker_name="stream-service",result="failure"} 5
veza_circuit_breaker_requests_total{circuit_breaker_name="stream-service",result="rejected"} 2

veza_circuit_breaker_failures_total

Type: Counter
Labels: circuit_breaker_name

Exemple:

veza_circuit_breaker_failures_total{circuit_breaker_name="stream-service"} 5

veza_circuit_breaker_consecutive_failures

Type: Gauge
Labels: circuit_breaker_name

Exemple:

veza_circuit_breaker_consecutive_failures{circuit_breaker_name="stream-service"} 3

Comportement sur Erreurs

Codes HTTP 5xx

Les codes HTTP 5xx (500, 502, 503, etc.) sont considérés comme des échecs et comptent pour le circuit breaker:

if resp.StatusCode >= 500 {
    resp.Body.Close()
    return nil, fmt.Errorf("server error: %d", resp.StatusCode)
}

Circuit Ouvert

Quand le circuit est ouvert, les requêtes sont rejetées immédiatement sans appel HTTP:

if err == gobreaker.ErrOpenState {
    return nil, fmt.Errorf("circuit breaker is open: service unavailable")
}

Tests

Tests unitaires

go test ./internal/services -v -run TestCircuitBreaker

Tests inclus:

  • Création du client
  • Requêtes réussies
  • Gestion des erreurs 5xx
  • Ouverture du circuit après seuil
  • Rejet de requêtes quand circuit ouvert
  • Support du contexte (timeout/cancellation)

Test d'intégration (mock server)

Un test simule un serveur qui retourne 5xx pour déclencher l'ouverture du circuit:

go test ./internal/services -v -run TestCircuitBreakerHTTPClient_Do_ServerError

Variables d'Environnement

Aucune variable d'environnement requise. La configuration est codée en dur dans le wrapper pour simplifier.

Pour personnaliser (si nécessaire):

  • Modifier internal/services/circuit_breaker.go
  • Ajuster MaxRequests, Interval, Timeout, ReadyToTrip

Monitoring et Alertes

Alertes recommandées

  1. Circuit ouvert trop souvent:

    veza_circuit_breaker_state{circuit_breaker_name="stream-service"} == 2
    
  2. Taux d'échec élevé:

    rate(veza_circuit_breaker_requests_total{result="failure"}[5m]) > 0.1
    
  3. Échecs consécutifs:

    veza_circuit_breaker_consecutive_failures > 3
    

Dépannage

Circuit reste ouvert

Cause: Service externe toujours en erreur
Solution: Vérifier la santé du service externe, attendre 30s (Timeout) pour half-open

Trop de rejets

Cause: Seuil trop bas (5 échecs)
Solution: Augmenter ReadyToTrip dans circuit_breaker.go

Métriques manquantes

Cause: Métriques non initialisées
Solution: Vérifier que internal/metrics/circuit_breaker.go est importé


Références


Dernière mise à jour: 2025-01-27
Maintenu par: Veza Backend Team