Circuit Breaker

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 do Circuit Breaker

  1. CLOSED: Operação normal, todas as chamadas passam
  2. OPEN: Circuito aberto, todas as chamadas são rejeitadas imediatamente
  3. 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:

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

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

  1. Configure thresholds apropriados: Muito baixo = muito sensível, muito alto = demora para detectar problemas
  2. Use timeouts: Configure timeouts nas chamadas HTTP para evitar espera indefinida
  3. Monitor métricas: Configure alertas baseados nas métricas do circuit breaker
  4. Teste fallbacks: Sempre tenha um fallback quando o circuito está aberto
  5. Log adequado: Use logging para rastrear transições de estado

Troubleshooting

Circuit sempre aberto

Circuit não abre quando deveria

Performance

Referências

🔊 Text-to-Speech

1.0x
1.0
Pronto para reproduzir