O padrão circuit breaker protege sua aplicação contra falhas em cascata ao monitorar erros consecutivos na chamada à API de CPF e bloquear automaticamente novas requisições quando o threshold é atingido — retornando um fallback imediato em vez de deixar o sistema acumular timeouts. Para integrações com a API da CPFHub.io, que tem latência média de ~900ms, um circuit breaker com threshold de 5 falhas e recovery timeout de 60 segundos é um bom ponto de partida. O artigo de Martin Fowler sobre CircuitBreaker é a referência canônica do padrão.
O que é o padrão circuit breaker
O circuit breaker funciona como um disjuntor elétrico. Quando detecta que uma dependência externa está falhando repetidamente, ele "abre o circuito" e para de enviar requisições, evitando que a falha se propague.
Os três estados
- Fechado (Closed): tudo funciona normalmente. As requisições passam para a API e as respostas são retornadas ao chamador.
- Aberto (Open): a API está falhando. O circuit breaker bloqueia as requisições imediatamente, sem nem tentar chamar a API, retornando um erro ou resposta padrão.
- Meio-aberto (Half-Open): após um período de espera, o circuit breaker permite que uma requisição de teste passe. Se ela for bem-sucedida, o circuito fecha novamente. Se falhar, volta a abrir.
Por que isso importa para consulta de CPF
Quando sua aplicação depende de uma API externa para consultar CPFs, qualquer instabilidade nessa dependência pode se propagar e derrubar todo o sistema. Um timeout que deveria afetar apenas a validação de CPF acaba travando filas, acumulando conexões e degradando a experiência do usuário em funcionalidades que nem dependem dessa consulta. O circuit breaker existe para prevenir exatamente esse tipo de falha em cascata — e é especialmente relevante para APIs com latência na casa de ~900ms, onde timeouts acumulados afetam rapidamente a performance geral.
Implementação em Python
Vamos construir um circuit breaker do zero, sem dependências externas, para entender cada componente:
import time
import requests
import logging
from enum import Enum
from threading import Lock
logger = logging.getLogger(__name__)
class CircuitState(Enum):
CLOSED = "closed"
OPEN = "open"
HALF_OPEN = "half_open"
class CircuitBreaker:
"""Circuit breaker para proteger chamadas à API de CPF."""
def __init__(
self,
failure_threshold: int = 5,
recovery_timeout: int = 60,
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 = CircuitState.CLOSED
self._failure_count = 0
self._last_failure_time = 0
self._half_open_calls = 0
self._lock = Lock()
@property
def state(self) -> CircuitState:
with self._lock:
if self._state == CircuitState.OPEN:
if time.time() - self._last_failure_time >= self.recovery_timeout:
self._state = CircuitState.HALF_OPEN
self._half_open_calls = 0
logger.info("Circuit breaker transicionou para HALF_OPEN")
return self._state
def record_success(self):
with self._lock:
self._failure_count = 0
if self._state == CircuitState.HALF_OPEN:
self._state = CircuitState.CLOSED
logger.info("Circuit breaker fechou -- API recuperada")
def record_failure(self):
with self._lock:
self._failure_count += 1
self._last_failure_time = time.time()
if self._failure_count >= self.failure_threshold:
self._state = CircuitState.OPEN
logger.warning(
f"Circuit breaker ABERTO apos {self._failure_count} falhas consecutivas"
)
def allow_request(self) -> bool:
current_state = self.state
if current_state == CircuitState.CLOSED:
return True
if current_state == CircuitState.HALF_OPEN:
with self._lock:
if self._half_open_calls < self.half_open_max_calls:
self._half_open_calls += 1
return True
return False
return False
class CPFClientWithCircuitBreaker:
"""Cliente de API de CPF com circuit breaker integrado."""
BASE_URL = "https://api.cpfhub.io/cpf"
TIMEOUT = 30
def __init__(self, api_key: str):
self.api_key = api_key
self.circuit = CircuitBreaker(
failure_threshold=5,
recovery_timeout=60
)
self.session = requests.Session()
self.session.headers.update({
"x-api-key": self.api_key,
"Accept": "application/json"
})
def consultar(self, cpf: str) -> dict:
if not self.circuit.allow_request():
logger.warning("Circuit breaker ABERTO -- requisicao bloqueada")
return {"success": False, "error": "circuit_open"}
try:
response = self.session.get(
f"{self.BASE_URL}/{cpf}",
timeout=self.TIMEOUT
)
response.raise_for_status()
resultado = response.json()
self.circuit.record_success()
return resultado
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
self.circuit.record_failure()
logger.error(f"Falha na API de CPF: {e}")
return {"success": False, "error": str(e)}
except requests.exceptions.HTTPError as e:
if response.status_code >= 500:
self.circuit.record_failure()
return {"success": False, "error": str(e)}
Uso do cliente
cliente = CPFClientWithCircuitBreaker(api_key="SUA_CHAVE_API")
resultado = cliente.consultar("12345678900")
if resultado.get("success"):
print(f"Nome: {resultado['data']['name']}")
elif resultado.get("error") == "circuit_open":
print("Servico temporariamente indisponivel. Tente novamente em breve.")
else:
print(f"Erro na consulta: {resultado.get('error')}")
Implementação em Node.js
Para aplicações JavaScript, a biblioteca opossum facilita a implementação:
const CircuitBreaker = require("opossum");
const axios = require("axios");
const API_KEY = "SUA_CHAVE_API";
const BASE_URL = "https://api.cpfhub.io/cpf";
const TIMEOUT_MS = 30000;
async function consultarCPF(cpf) {
const response = await axios.get(`${BASE_URL}/${cpf}`, {
headers: {
"x-api-key": API_KEY,
"Accept": "application/json"
},
timeout: TIMEOUT_MS
});
return response.data;
}
const circuitOptions = {
timeout: TIMEOUT_MS,
errorThresholdPercentage: 50,
resetTimeout: 60000,
volumeThreshold: 5
};
const breaker = new CircuitBreaker(consultarCPF, circuitOptions);
breaker.on("open", () => console.warn("Circuit breaker ABERTO"));
breaker.on("halfOpen", () => console.info("Circuit breaker HALF_OPEN"));
breaker.on("close", () => console.info("Circuit breaker FECHADO"));
breaker.fallback(() => ({
success: false,
error: "circuit_open"
}));
// Uso
breaker.fire("12345678900")
.then(result => {
if (result.success) {
console.log(`Nome: ${result.data.name}`);
} else {
console.log("Fallback ativado:", result.error);
}
})
.catch(err => console.error("Erro:", err.message));
Configurações recomendadas
Os valores ideais dependem do contexto da sua aplicação, mas estas são boas referências para integrações com API de CPF:
| Parâmetro | Valor recomendado | Justificativa |
|---|---|---|
| Failure threshold | 5 falhas | Evita abrir o circuito por falhas isoladas |
| Recovery timeout | 60 segundos | Tempo suficiente para a maioria dos incidentes |
| Timeout da requisição | 30 segundos | Margem generosa sobre a latência média de ~900ms |
| Half-open max calls | 1-3 | Testa a recuperação sem sobrecarregar |
O que fazer quando o circuito está aberto
Quando o circuit breaker abre, sua aplicação precisa de uma estratégia de fallback. As opções mais comuns são:
1. Retornar erro amigável
Informe ao usuário que a validação está temporariamente indisponível e que ele pode prosseguir (se o fluxo permitir).
2. Validação local apenas
Use a validação algorítmica do CPF (dígitos verificadores) como fallback. Ela não confirma a identidade, mas garante que o formato é válido.
3. Enfileirar para processamento posterior
Salve o CPF em uma fila e processe a consulta quando o circuito fechar. Essa abordagem é ideal para fluxos que não precisam da resposta em tempo real.
Monitorando o circuit breaker
Registre métricas sobre o comportamento do circuit breaker:
- Número de vezes que o circuito abriu por período.
- Tempo médio no estado aberto antes de recuperar.
- Taxa de falhas que disparam a abertura versus falhas isoladas.
Essas métricas ajudam a ajustar os parâmetros e a identificar padrões de instabilidade.
Testando o circuit breaker
Para validar que o circuit breaker funciona corretamente, simule cenários de falha:
import unittest
from unittest.mock import patch, MagicMock
class TestCircuitBreaker(unittest.TestCase):
def test_abre_apos_threshold(self):
cliente = CPFClientWithCircuitBreaker(api_key="teste")
with patch.object(cliente.session, "get", side_effect=requests.exceptions.Timeout):
for _ in range(5):
cliente.consultar("12345678900")
self.assertEqual(cliente.circuit.state, CircuitState.OPEN)
def test_bloqueia_quando_aberto(self):
cliente = CPFClientWithCircuitBreaker(api_key="teste")
cliente.circuit._state = CircuitState.OPEN
cliente.circuit._last_failure_time = time.time()
resultado = cliente.consultar("12345678900")
self.assertEqual(resultado["error"], "circuit_open")
Perguntas frequentes
Quantas falhas consecutivas devo configurar para abrir o circuito?
O threshold recomendado é de 5 falhas consecutivas para integrações com API de CPF. Valores muito baixos (1-2) abrem o circuito por falhas isoladas que a API já resolveria com um simples retry. Valores muito altos (10+) permitem que a falha persista por tempo demais antes de acionar o fallback. Ajuste com base nos dados de monitoramento: se o circuito abre com frequência por falhas isoladas, aumente o threshold.
O circuit breaker substitui o retry com backoff exponencial?
Não — os dois padrões são complementares. O retry com backoff exponencial lida com falhas isoladas e transitórias, tentando a mesma requisição algumas vezes com intervalo crescente. O circuit breaker atua quando as falhas são sistêmicas e consecutivas, bloqueando o fluxo para proteger o sistema. A combinação ideal é: retry com até 3 tentativas, e circuit breaker que abre após 5 falhas acumuladas.
Como definir o recovery timeout do circuit breaker?
O recovery timeout (tempo que o circuito permanece aberto antes de testar novamente) deve ser longo o suficiente para a API se recuperar de incidentes típicos, mas curto o suficiente para não deixar o sistema no fallback por muito tempo. Para a API da CPFHub.io, 60 segundos é um bom padrão. Se incidentes costumam durar mais tempo na sua experiência, considere aumentar para 120-300 segundos.
O circuit breaker precisa ser compartilhado entre threads ou instâncias?
Sim, para funcionar corretamente. Um circuit breaker por instância de processo — usando um singleton com lock de thread (como no exemplo Python acima) — garante que o estado seja consistente dentro do processo. Em arquiteturas com múltiplas instâncias da aplicação, considere um circuit breaker distribuído com estado em Redis para que todas as instâncias compartilhem o mesmo estado de abertura.
Conclusão
O circuit breaker é uma camada essencial de resiliência para qualquer aplicação que depende de APIs externas. Ao implementá-lo em integrações com a API de CPF, você protege sua aplicação contra falhas em cascata, melhora a experiência do usuário e ganha visibilidade sobre a saúde das suas dependências.
Cadastre-se em cpfhub.io
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



