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
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
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()});
}
}
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 — Retentar com backoff antes de abrir circuito
- Strategy — Alternar entre serviços quando circuito abre
- State Machine — Máquina de estados em detalhe
- Error Handling — Tratamento de erros em Zig
- FAQ Produção — Resiliência em produção