---
title: "Cheatsheet: Circuit Breaker em Zig"
url: "https://ziglang.com.br/padroes/cheatsheet-circuit-breaker-em-zig/"
markdown_url: "https://ziglang.com.br/padroes/cheatsheet-circuit-breaker-em-zig.MD"
description: "Design pattern Circuit Breaker implementado em Zig: proteger contra falhas em cascata, disjuntor com estados fechado/aberto/semi-aberto. Guia completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Cheatsheet: Circuit Breaker em Zig

Design pattern Circuit Breaker implementado em Zig: proteger contra falhas em cascata, disjuntor com estados fechado/aberto/semi-aberto. Guia completo em português.


# Circuit Breaker em Zig

O padrão Circuit Breaker protege um sistema contra falhas em cascata, funcionando como um disjuntor elétrico. Quando um serviço externo apresenta falhas repetidas, o circuit breaker "abre", impedindo novas tentativas e falhando rapidamente. Após um período de espera, permite tentativas limitadas para verificar se o serviço se recuperou.

## Quando Usar

- Chamadas a serviços externos (APIs, bancos de dados)
- Operações de rede que podem falhar
- Proteção contra sobrecarga de serviços em recuperação
- Microservices e sistemas distribuídos

## Implementação

```zig
const std = @import("std");

fn CircuitBreaker(comptime T: type, comptime E: type) type {
    return struct {
        const Self = @This();

        const Estado = enum {
            fechado,    // operação normal
            aberto,     // falhas demais, bloqueando chamadas
            semi_aberto, // testando se serviço se recuperou
        };

        estado: Estado = .fechado,
        falhas_consecutivas: u32 = 0,
        limite_falhas: u32,
        tempo_timeout_ns: u64,
        ultimo_falha_ns: i128 = 0,
        sucessos_semi_aberto: u32 = 0,
        sucessos_necessarios: u32 = 2,

        pub fn init(limite_falhas: u32, timeout_segundos: u32) Self {
            return .{
                .limite_falhas = limite_falhas,
                .tempo_timeout_ns = @as(u64, timeout_segundos) * std.time.ns_per_s,
            };
        }

        pub fn executar(self: *Self, operacao: *const fn () E!T) E!T {
            switch (self.estado) {
                .aberto => {
                    const agora = std.time.nanoTimestamp();
                    if (agora - self.ultimo_falha_ns > self.tempo_timeout_ns) {
                        // Timeout expirou — tentar semi-aberto
                        self.estado = .semi_aberto;
                        self.sucessos_semi_aberto = 0;
                    } else {
                        return error.CircuitoBloqueado;
                    }
                },
                .fechado, .semi_aberto => {},
            }

            const resultado = operacao() catch |err| {
                self.registrarFalha();
                return err;
            };

            self.registrarSucesso();
            return resultado;
        }

        fn registrarFalha(self: *Self) void {
            self.falhas_consecutivas += 1;
            self.ultimo_falha_ns = std.time.nanoTimestamp();

            if (self.estado == .semi_aberto or
                self.falhas_consecutivas >= self.limite_falhas)
            {
                self.estado = .aberto;
                std.debug.print("[CIRCUIT BREAKER] Circuito ABERTO após {d} falhas\n", .{
                    self.falhas_consecutivas,
                });
            }
        }

        fn registrarSucesso(self: *Self) void {
            switch (self.estado) {
                .semi_aberto => {
                    self.sucessos_semi_aberto += 1;
                    if (self.sucessos_semi_aberto >= self.sucessos_necessarios) {
                        self.estado = .fechado;
                        self.falhas_consecutivas = 0;
                        std.debug.print("[CIRCUIT BREAKER] Circuito FECHADO — serviço recuperado\n", .{});
                    }
                },
                .fechado => {
                    self.falhas_consecutivas = 0;
                },
                .aberto => {},
            }
        }

        pub fn estadoAtual(self: *const Self) []const u8 {
            return switch (self.estado) {
                .fechado => "FECHADO (operando normalmente)",
                .aberto => "ABERTO (bloqueando chamadas)",
                .semi_aberto => "SEMI-ABERTO (testando recuperação)",
            };
        }
    };
}
```

## Uso Prático

```zig
const CB = CircuitBreaker([]const u8, anyerror);

var tentativas: u32 = 0;

fn chamarServico() ![]const u8 {
    tentativas += 1;
    if (tentativas < 8) return error.ServicoIndisponivel;
    return "resposta ok";
}

pub fn main() void {
    var cb = CB.init(3, 5); // 3 falhas para abrir, 5s timeout

    for (0..10) |i| {
        if (cb.executar(chamarServico)) |resp| {
            std.debug.print("Tentativa {d}: {s}\n", .{ i, resp });
        } else |err| {
            std.debug.print("Tentativa {d}: erro {}\n", .{ i, err });
        }
        std.debug.print("  Estado: {s}\n", .{cb.estadoAtual()});
    }
}
```

## Integrando com Métricas e Observabilidade

Em produção, o circuit breaker deve expor métricas para monitoramento. Uma extensão prática é adicionar callbacks de estado:

```zig
const CircuitoBreakerComMetricas = struct {
    cb: CircuitBreaker([]const u8, anyerror),
    total_chamadas: u64 = 0,
    total_bloqueadas: u64 = 0,
    total_falhas: u64 = 0,

    pub fn executar(self: *@This(), operacao: anytype) ![]const u8 {
        self.total_chamadas += 1;
        return self.cb.executar(operacao) catch |err| {
            if (err == error.CircuitoBloqueado) {
                self.total_bloqueadas += 1;
            } else {
                self.total_falhas += 1;
            }
            return err;
        };
    }

    pub fn taxaBloqueio(self: *const @This()) f64 {
        if (self.total_chamadas == 0) return 0;
        return @as(f64, @floatFromInt(self.total_bloqueadas)) /
               @as(f64, @floatFromInt(self.total_chamadas));
    }
};
```

## Considerações de Performance

- **Overhead mínimo no caminho feliz**: quando o circuito está fechado, a única operação extra é incrementar o contador de falhas após cada sucesso — custo desprezível.
- **Estado compartilhado entre threads**: se múltiplas threads chamam o mesmo circuit breaker, use `std.Thread.Mutex` para proteger os campos `falhas_consecutivas` e `estado`. Sem isso, leituras e escritas concorrentes geram comportamento indefinido.
- **`std.time.nanoTimestamp()` é uma syscall**: em ambientes de altíssima performance, considere checar o timestamp apenas uma vez por batch de chamadas, não a cada invocação.
- **Evite alocar no caminho de erro**: o circuit breaker deve ser livre de alocações — seu valor está justamente em falhar rápido sem overhead adicional.

## Erros Comuns

**Não diferenciar erros retentáveis de erros permanentes**: o circuit breaker deve abrir apenas para falhas que indicam indisponibilidade do serviço (`error.Timeout`, `error.ConexaoRecusada`), não para erros de negócio (`error.UsuarioNaoEncontrado`). Mapeie cuidadosamente quais erros contam como "falha".

**Timeout muito curto no estado semi-aberto**: se o timeout for menor que o tempo de recuperação real do serviço, o circuito vai abrir e fechar repetidamente, sem deixar o serviço se estabilizar.

**Usar um único circuit breaker global para múltiplos serviços**: cada serviço externo deve ter seu próprio circuit breaker. Se o banco de dados falhar, isso não deve bloquear chamadas para a API de pagamentos.

## Perguntas Frequentes

**Quantas falhas devo usar como limite antes de abrir o circuito?**
Depende do perfil de tráfego. Para serviços com baixo volume (< 10 req/s), 3 a 5 falhas consecutivas é razoável. Para alto volume, use uma taxa de falha percentual (ex: >50% em uma janela de 10 segundos) em vez de contagem absoluta.

**Qual é a diferença entre Circuit Breaker e Retry Pattern?**
O Retry retenta imediatamente ou com backoff — é adequado para falhas ocasionais. O Circuit Breaker para de tentar completamente por um período — é adequado quando o serviço está claramente indisponível. Use os dois juntos: Retry para falhas esporádicas, Circuit Breaker para indisponibilidade prolongada.

**Como testar o circuit breaker sem um serviço real falhando?**
Injete a operação como ponteiro de função (como no exemplo acima) e passe uma função de teste que falha nas N primeiras chamadas. Use `testing.allocator` para verificar que não há leaks durante os ciclos de abertura/fechamento.

## Quando Evitar

- Operações locais que não falham por indisponibilidade de serviço
- Quando falhas são esperadas e tratadas individualmente
- Sistemas simples sem dependências externas

## Veja Também

- [Retry Pattern](/padroes/retry-pattern/) — Retentar com backoff antes de abrir circuito
- [Strategy](/padroes/strategy/) — Alternar entre serviços quando circuito abre
- [State Machine](/padroes/state-machine/) — Máquina de estados em detalhe
- [Error Handling](/cheatsheets/error-handling/) — Tratamento de erros em Zig
- [FAQ Produção](/faq/faq-producao/) — Resiliência em produção
