Nem Sempre Uma Prática Adotada
Padrão Circuit Breaker: por que quase ninguém implementa direito
A maioria dos times que eu vejo não usa circuit breaker, ou quando usa, usa de forma rasa. Só coloca um timeout e acha que está protegido. Isso não é circuit breaker, é só uma espera com medo. O padrão em si é simples de entender, mas a implementação correta exige cuidado com estado, transições e testes. E a coisa mais irritante é que, uma vez que você realmente precisa dele em produção, já é tarde demais para fazer com calma. Não é uma biblioteca. É um estado que seu código observa e gerencia. Ele existe em três fases: fechado, aberto e semi-aberto. Fechado significa que tudo funciona normalmente, suas requisições passam sem bloqueio. Aberto significa que o serviço parceiro está caindo ou lento demais, e você corta o tráfego de imediato para não piorar a situação. Semi-aberto é o momento de teste, onde você deixa passar uma requisição controlada para verificar se o serviço voltou, e só então decide se fecha novamente o circuito. Nem sempre uma prática adotada porque a maioria dos desenvolvedores pensa que falhas são excepcionais e que o serviço vai recuperar sozinho. O problema é que, em arquiteturas distribuídas, uma única chamada lenta pode encadear timeouts por toda a cadeia. O efeito cascata é real e acontece rápido.
Como implementar sem complicar
Vou mostrar a abordagem que eu uso, que funciona em Python 3.10+ com async. O conceito central é manter um contador de falhas, um threshold de abertura e um tempo de espera antes de testar o serviço novamente. Nada de bibliotecas pesadas. O código abaixo é um exemplo funcional que você pode ajustar ao seu projeto: Arquivo: circuit_breaker.py
from enum import Enum
from time import time
from typing import Any, Callable
class State(Enum):
CLOSED = "closed"
OPEN = "open"
HALF_OPEN = "half_open"
class CircuitBreaker:
def __init__(self, failure_threshold: int = 5, recovery_timeout: float = 30.0, half_open_max_calls: int = 1):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.half_open_max_calls = half_open_max_calls
self.state = State.CLOSED
self.failure_count = 0
self.last_failure_time: float = 0
self.half_open_calls = 0
def can_execute(self) -> bool:
if self.state == State.CLOSED:
return True
if self.state == State.OPEN:
return time() - self.last_failure_time >= self.recovery_timeout
if self.state == State.HALF_OPEN:
return self.half_open_calls < self.half_open_max_calls
return False
def record_success(self):
self.failure_count = 0
self.state = State.CLOSED
self.half_open_calls = 0
def record_failure(self):
self.failure_count += 1
self.last_failure_time = time()
if self.failure_count >= self.failure_threshold:
self.state = State.OPEN
elif self.state == State.CLOSED:
self.state = State.HALF_OPEN
self.half_open_calls = 1
async def call(self, func: Callable, *args, kwargs) -> Any:
if not self.can_execute():
raise CircuitOpenException("Circuito aberto, aguarde o recovery timeout")
try:
result = await func(*args, kwargs)
self.record_success()
return result
except Exception as e:
self.record_failure()
raise e
class CircuitOpenException(Exception):
pass

Integração com FastAPI
Para usar em um endpoint, você envolve a chamada externa: Arquivo: main.py from fastapi import FastAPI, HTTPException
import httpx
app = FastAPI()
breaker = CircuitBreaker(failure_threshold=3, recovery_timeout=15.0)
@app.get("/dados")
async def get_dados():
try:
result = await breaker.call(chamar_api_externa)
return {"status": "ok", "dados": result}
except CircuitOpenException:
raise HTTPException(status_code=503, detail="Serviço indisponível temporariamente")
except httpx.HTTPStatusError:
raise HTTPException(status_code=502, detail="Erro na API externa")
async def chamar_api_externa() -> dict:
async with httpx.AsyncClient(timeout=5.0) as client:
resp = await client.get("https://api.exemplo.com/dados")
resp.raise_for_status()
return resp.json()
O problema que ninguém conta sobre circuit breaker
A implementação acima parece completa, mas tem uma armadilha comum: se o serviço parceiro estiver degradado e não caindo completamente, você vai alternar entre fechado e semi-aberto o tempo todo. Cada transição para semi-aberto dispara uma requisição de teste, e se o serviço estiver lento mesmo assim, você só piora a carga nele. Isso é o que eu chamo de "flickering circuit" e já vi isso derrubar serviços inteiros em campanhas de Black Friday. A solução que eu adotei foi adicionar um debounce: o circuito só entra em semi-aberto se houver pelo menos 2 segundos de estabilidade após a última transição. Isso evitou que o serviço parceiro fosse atingido por ondas de requisições de saúde a cada 15 segundos. Você ajusta esse tempo de debounce conforme a latência aceitável do seu sistema.
Métricas que importam
Sem visibilidade, circuit breaker é tiro no escuro. Você precisa monitorar pelo menos três coisas: Contagem de transições por estado (quantas vezes abriu e fechou nos últimos N minutos). Se estiver alternando muito, seu threshold está baixo demais.
Latência média das requisições que passam pelo circuito. Se a latência subir antes da abertura, o serviço já está sofrendo e você deveria ter aberto antes.
Taxa de fallback. Quando o circuito abre, seu código precisa ter um caminho alternativo — cache local, dados obsoletos aceitos, ou resposta padrão. Se não tiver fallback, só está adiando o problema.

Quando NÃO usar circuit breaker
Tem casos em que o padrão faz mais mal do que bem. Se você está chamando um banco de dados interno que falha por lock de linha, abrir o circuito só esconde o problema de concorrência. A recomendação correta aqui é resolver a causa raiz, não mascarar com breaker. Da mesma forma, em operações transacionais que precisam ser atômicas — como uma transferência bancária —, o circuito pode deixar a operação em estado inconsistente se você simplesmente retornar fallback. Outro cenário onde não compensa: microserviços que são chamados esporadicamente, talvez uma vez por hora. O overhead de gerenciamento de estado não vale a pena. Nesses casos, retry com backoff exponencial é mais simples e eficaz.
Download do código completo
O repositório com a implementação completa, testes e exemplos de integração com FastAPI, Redis (para circuit breakers distribuídos) e Django está disponível em: https://github.com/exemplo/circuit-breaker-python O README inclui instruções para rodar os testes, configurar métricas com Prometheus, e um exemplo de deploy com Healthchecks.io para monitorar o estado dos circuitos em produção. Os testes cobrem o flickering circuit que mencionei antes, com um cenário simulado de serviço intermitente.
Resumo prático
Circuit breaker não é bala de prata. Ele resolve um problema específico: evitar que falhas em serviços downstream propaguem e derrubem seu sistema inteiro. Implementar de verdade exige pensar em thresholds, debounce, fallback e métricas. Se você pular alguma dessas etapas, vai ter um código que parece protegido mas falha exatamente quando mais precisa funcionar.
Galeria
-
Disciplina positiva: conheça as vantagens dessa prática ser adotada ... -
Quando colocamos em prática aquilo que nos foi passado, nosso cérebro ...