Circuit Breaker Pattern - C-Suite
Este documento descreve a implementação do padrão Circuit Breaker para proteger chamadas entre serviços e prevenir falhas em cascata.
Visão Geral
O módulo common/common_circuit_breaker.py fornece uma implementação completa do padrão Circuit Breaker com suporte a retry com backoff exponencial.
Características
- ✅ Estados: CLOSED, OPEN, HALF_OPEN
- ✅ Thresholds configuráveis (falhas/sucessos)
- ✅ Timeout configurável antes de tentar reset
- ✅ Métricas Prometheus integradas
- ✅ Retry com backoff exponencial
- ✅ Thread-safe
Estados do Circuit Breaker
- CLOSED: Operação normal, todas as chamadas passam
- OPEN: Circuito aberto, todas as chamadas são rejeitadas imediatamente
- HALF_OPEN: Estado de teste, permite algumas chamadas para verificar se serviço recuperou
Configuração
Variáveis de Ambiente
# Configurações padrão (podem ser sobrescritas por circuito)
CIRCUIT_BREAKER_FAILURE_THRESHOLD=5
CIRCUIT_BREAKER_SUCCESS_THRESHOLD=2
CIRCUIT_BREAKER_TIMEOUT=60.0
Uso
Decorator Básico
from common.common_circuit_breaker import circuit_breaker
@circuit_breaker(name="external_api", failure_threshold=3)
def call_external_api():
response = httpx.get("http://external-service/api")
response.raise_for_status()
return response.json()
Com Retry e Backoff
from common.common_circuit_breaker import circuit_breaker, with_retry
@circuit_breaker(name="external_api", failure_threshold=3)
@with_retry(max_attempts=3, backoff_factor=2.0)
def call_external_api():
response = httpx.get("http://external-service/api")
response.raise_for_status()
return response.json()
Uso Manual
from common.common_circuit_breaker import get_circuit_breaker
breaker = get_circuit_breaker(
name="external_api",
failure_threshold=5,
success_threshold=2,
timeout=60.0
)
def call_api():
return breaker.call(httpx.get, "http://external-service/api")
Em FastAPI
from fastapi import FastAPI, HTTPException
from common.common_circuit_breaker import circuit_breaker
import httpx
app = FastAPI()
@circuit_breaker(name="4c_decision_api", failure_threshold=5)
def call_4c_decision_api(org_id: int):
response = httpx.post(
"http://decision-api:8080/decide",
json={"org_id": org_id}
)
response.raise_for_status()
return response.json()
@app.post("/decide/{org_id}")
async def decide(org_id: int):
try:
result = call_4c_decision_api(org_id)
return result
except Exception as e:
raise HTTPException(status_code=503, detail=f"Service unavailable: {str(e)}")
Configuração Avançada
Exception Específica
from common.common_circuit_breaker import circuit_breaker
import httpx
@circuit_breaker(
name="external_api",
failure_threshold=3,
expected_exception=httpx.HTTPError
)
def call_api():
response = httpx.get("http://external-service/api")
response.raise_for_status()
return response.json()
Timeout Customizado
@circuit_breaker(
name="slow_service",
failure_threshold=3,
timeout=120.0 # 2 minutos antes de tentar reset
)
def call_slow_service():
# Operação que pode demorar
pass
Estatísticas
Obter Stats
from common.common_circuit_breaker import get_circuit_breaker
breaker = get_circuit_breaker("external_api")
stats = breaker.get_stats()
print(f"State: {stats['state']}")
print(f"Failures: {stats['failures']}")
print(f"Successes: {stats['successes']}")
print(f"Total requests: {stats['total_requests']}")
print(f"Rejected requests: {stats['rejected_requests']}")
Reset Manual
breaker = get_circuit_breaker("external_api")
breaker.reset() # Força reset para CLOSED
Métricas Prometheus
O módulo expõe as seguintes métricas:
circuit_breaker_failures_total: Total de falhascircuit_breaker_successes_total: Total de sucessoscircuit_breaker_rejected_total: Total de requisições rejeitadascircuit_breaker_state: Estado atual (0=closed, 1=half_open, 2=open)
Retry com Backoff Exponencial
from common.common_circuit_breaker import with_retry
@with_retry(
max_attempts=3,
backoff_factor=2.0, # Delay = 2.0 * (2 ^ attempt)
exceptions=(httpx.HTTPError,)
)
def unreliable_function():
response = httpx.get("http://unreliable-service/api")
response.raise_for_status()
return response.json()
Delay Calculation
- Attempt 1: delay = 2.0 * (2^0) = 2.0s
- Attempt 2: delay = 2.0 * (2^1) = 4.0s
- Attempt 3: delay = 2.0 * (2^2) = 8.0s
Exemplos de Uso
Proteger Chamada HTTP
from common.common_circuit_breaker import circuit_breaker, with_retry
import httpx
@circuit_breaker(name="feature_service", failure_threshold=5)
@with_retry(max_attempts=3, backoff_factor=1.5)
def get_features(org_id: int):
response = httpx.get(
f"http://feature-service:8081/features/{org_id}",
timeout=5.0
)
response.raise_for_status()
return response.json()
Proteger Query de Banco
from common.common_circuit_breaker import circuit_breaker
from common.common_db_pool import get_db_session
@circuit_breaker(name="database_query", failure_threshold=10)
def get_policies(org_id: int):
session = get_db_session("csuite")
return session.query(Policy).filter_by(org_id=org_id).all()
Integração com Notificações
from common.common_circuit_breaker import get_circuit_breaker
from common.common_notifications import send_alert
breaker = get_circuit_breaker("external_api")
def call_api():
try:
return breaker.call(httpx.get, "http://external-service/api")
except Exception as e:
stats = breaker.get_stats()
if stats['state'] == 'open':
send_alert(
title="Circuit Breaker Opened",
message=f"Circuit breaker for external_api is OPEN",
metadata={"circuit": "external_api", "failures": stats['failures']}
)
raise
Best Practices
- Configure thresholds apropriados: Muito baixo = muito sensível, muito alto = demora para detectar problemas
- Use timeouts: Configure timeouts nas chamadas HTTP para evitar espera indefinida
- Monitor métricas: Configure alertas baseados nas métricas do circuit breaker
- Teste fallbacks: Sempre tenha um fallback quando o circuito está aberto
- Log adequado: Use logging para rastrear transições de estado
Troubleshooting
Circuit sempre aberto
- Verifique se o serviço realmente está funcionando
- Aumente
success_thresholdse necessário - Verifique logs para erros específicos
Circuit não abre quando deveria
- Diminua
failure_threshold - Verifique se exceptions estão sendo capturadas corretamente
Performance
- Circuit breaker adiciona overhead mínimo
- Use métricas para monitorar impacto