Marcio Cunha

System down? How to design resilient circuit breakers

Learn how circuit breakers prevent cascading failures in the cloud. Protect your dependent services with robust fault tolerance patterns.

Marcio Cunha•5 min
Also available in:PortuguêsEspañol
Summary
  • Circuit breakers interrupt repeated calls to unstable services to preserve critical system resources
  • The open state prevents new connection attempts until dependent systems recover their stability
  • Periodic tests in the half-open state ensure safe traffic resumption without immediate overload
  • Smart fallbacks keep applications functional even when secondary services fail completely
  • Fine-tuning timeouts and thresholds prevents false positives in highly volatile cloud networks

The invisible danger of cascading failures in the cloud

cuando un motor de bases de datos ou uma API externa de pagamentos começa a responder lentamente, o comportamento padrão de grande parte das aplicações modernas é continuar insistindo. Na prática, isso significa que centenas de servidores continuam abrindo novas conexões simultâneas, esgotando rapidamente as filas de espera e a memória RAM dos servidores. Esse fenômeno destrutivo é conhecido como queda em cascata, onde uma única falha localizada derruba progressivamente todos os demais microsserviços interligados de uma arquitetura corporativa. Quando um banco de dados ou um microsserviço essencial começa a apresentar lentidão extrema na nuvem, o comportamento padrão das aplicações sem proteção adequada é continuar enviando milhares de requisições. Na prática, isso significa que threads e conexões de rede se acumulam rapidamente, consumindo toda a memória RAM e esgotando os recursos disponíveis dos servidores. Esse fenômeno destrutivo é conhecido como falha em cascata, onde um problema em um único componente acaba arrastando toda a infraestrutura para baixo.

Para evitar que um pequeno contratempo na rede se transforme em uma indisponibilidade total do sistema, a engenharia de software moderna adota o padrão de tolerância a falhas conhecido como circuit breaker ou disjuntor. Inspirado nos disjuntores elétricos residenciais, esse componente monitora ativamente as chamadas de rede e interrompe o tráfego assim que a taxa de erros ultrapassa um limite aceitável. Assim como discutimos em nossa análise sobre arquiteturas de microsserviços tolerantes a falhas, isolar problemas impede que o sistema inteiro colapse por falta de contenção.

How the three states of a software circuit breaker operate

Para operar com precisão cirúrgica, um circuit breaker transita constantemente entre três estados distintos: Fechado, Aberto e Semi-Aberto. No estado fechado, que representa a operação normal do dia a dia, todas as requisições passam livremente para o serviço de destino enquanto o componente contabiliza silenciosamente a proporção de falhas e sucessos. Caso a taxa de erros ultrapasse, por exemplo, cinquenta por cento em uma janela de tempo específica, o disjuntor muda imediatamente para o estado aberto, bloqueando de forma preventiva qualquer tentativa de chamada futura.

Quando o disjuntor está aberto, o código não perde tempo esperando a rede expirar ou esgotar timeouts; ele retorna instantaneamente um erro amigável ou um dado alternativo conhecido como fallback. Após um intervalo de tempo pré-determinado, o componente transita para o estado semi-aberto, permitindo que apenas uma requisição de teste passe para verificar se o serviço dependente já se recuperou. Se essa única chamada obtiver sucesso, o circuito se fecha novamente e a operação normal é restabelecida, caso contrário, ele retorna ao estado aberto por mais tempo.

Implementing a circuit breaker in practice with code

Abaixo apresentamos uma implementação funcional e direta de um circuit breaker em código para ilustrar a lógica por trás das bibliotecas comerciais que você utiliza no dia a dia. O algoritmo controla o histórico recente de falhas e decide se deve executar a função remota ou disparar uma resposta de proteção imediata:

class SimpleCircuitBreaker { constructor(threshold, timeout) { this.threshold = threshold; this.timeout = timeout; this.state = 'CLOSED'; this.failureCount = 0; this.nextAttempt = Date.now(); } async execute(action) { if (this.state === 'OPEN') { if (Date.now() > this.nextAttempt) { this.state = 'HALF-OPEN'; } else { throw new Error('Circuit is OPEN. Fast failing.'); } } try { const result = await action(); this.onSuccess(); return result; } catch (error) { this.onFailure(); throw error; } } onSuccess() { this.failureCount = 0; this.state = 'CLOSED'; } onFailure() { this.failureCount++; if (this.failureCount >= this.threshold) { this.state = 'OPEN'; this.nextAttempt = Date.now() + this.timeout; } } }

Esse modelo mental simples demonstra como a aplicação deixa de gastar recursos preciosos tentando falar com um servidor que claramente está fora do ar. Além disso, essa mesma preocupação com o uso eficiente de recursos aparece quando precisamos garantir a resiliência operacional, tal como detalhamos no guia sobre como desenhar sistemas tolerantes a falhas na nuvem.

Fallback strategies and user experience considerations

Bloquear requisições para evitar a sobrecarga do sistema é apenas metade do trabalho, pois a aplicação ainda precisa decidir o que entregar para o usuário final. Se um microsserviço que retorna o catálogo de produtos de uma loja virtual falhar, exibir uma tela branca ou um erro genérico destrói completamente a conversão de vendas e frustra o cliente. É exatamente nesse momento que entram as estratégias de fallback inteligentes, que fornecem respostas alternativas baseadas em dados em cache, catálogos estáticos ou mensagens informativas amigáveis.

Um bom fallback nunca deve mascarar totalmente um problema sistêmico a ponto de esconder alertas críticos da equipe de engenharia, mas deve preservar a dignidade da navegação sempre que possível. Em sistemas de recomendação de conteúdo, por exemplo, se o motor de inteligência artificial falhar, o sistema pode exibir os itens mais populares da semana armazenados localmente na memória. Dessa forma, a interface continua responsiva e o usuário consegue concluir suas tarefas principais sem perceber que nos bastidores uma dependência importante está temporariamente inoperante. To explore these architectural concepts further in production, see our related technical analysis on Why models hallucinate and how to build RAG guardrails.

Fine-tuning, false positives, and the future of resilience

Configurar os parâmetros de um circuit breaker exige calibração cuidadosa baseada no tráfego real e nas características operacionais do seu ambiente de produção. Se o limite de falhas for rigoroso demais, oscilações momentâneas de rede podem desarmar o disjuntor sem necessidade real, gerando falsos positivos incômodos. Por outro lado, um limiar muito permissivo deixará a aplicação exposta por tempo demais, permitindo que quedas em cascata comprovem a instabilidade dos demais serviços da nuvem.

Em suma, desenhar disjuntores de circuito resilientes é um exercício contínuo de observabilidade, testes de carga e compreensão profunda dos limites de cada dependência do sistema. Combinar essas práticas de proteção com um monitoramento rigoroso garante que sua arquitetura permaneça elástica, estável e preparada para absorver falhas inevitáveis sem impactar a experiência de quem mais importa: o usuário final.