---
title: "Cheatsheet: Retry Pattern em Zig"
url: "https://ziglang.com.br/padroes/cheatsheet-retry-pattern-em-zig/"
markdown_url: "https://ziglang.com.br/padroes/cheatsheet-retry-pattern-em-zig.MD"
description: "Design pattern Retry implementado em Zig: retentar operações falhas com backoff exponencial, jitter, limites de tentativas e integração com circuit breaker. Guia completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Cheatsheet: Retry Pattern em Zig

Design pattern Retry implementado em Zig: retentar operações falhas com backoff exponencial, jitter, limites de tentativas e integração com circuit breaker. Guia completo em português.


# Retry Pattern em Zig

O padrão Retry automatiza a repetição de operações que falharam, com estratégias inteligentes de espera (backoff) para evitar sobrecarga. Em Zig, implementamos esse padrão com **generics**, **comptime** e controle explícito de timing.

## Quando Usar

- Chamadas de rede que podem falhar temporariamente
- Acesso a recursos compartilhados com contenção
- Operações de I/O em dispositivos que podem estar ocupados
- Interação com APIs rate-limited

## Implementação com Backoff Exponencial

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

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

        max_tentativas: u32 = 3,
        delay_inicial_ms: u64 = 100,
        delay_maximo_ms: u64 = 30_000,
        multiplicador: f64 = 2.0,
        com_jitter: bool = true,

        pub fn executar(self: Self, operacao: *const fn () E!T) E!T {
            var tentativa: u32 = 0;
            var delay_ms = self.delay_inicial_ms;

            while (tentativa < self.max_tentativas) : (tentativa += 1) {
                if (operacao()) |resultado| {
                    if (tentativa > 0) {
                        std.debug.print("[RETRY] Sucesso na tentativa {d}\n", .{tentativa + 1});
                    }
                    return resultado;
                } else |err| {
                    if (tentativa + 1 >= self.max_tentativas) {
                        std.debug.print("[RETRY] Falha final após {d} tentativas\n", .{tentativa + 1});
                        return err;
                    }

                    std.debug.print("[RETRY] Tentativa {d} falhou, esperando {d}ms\n", .{
                        tentativa + 1, delay_ms,
                    });

                    // Esperar com backoff
                    var delay_real = delay_ms;
                    if (self.com_jitter) {
                        // Adicionar jitter aleatório (0-50% do delay)
                        var prng = std.Random.DefaultPrng.init(@intCast(std.time.nanoTimestamp()));
                        const jitter = prng.random().intRangeAtMost(u64, 0, delay_ms / 2);
                        delay_real += jitter;
                    }

                    std.time.sleep(delay_real * std.time.ns_per_ms);

                    // Aumentar delay para próxima tentativa
                    delay_ms = @min(
                        @as(u64, @intFromFloat(@as(f64, @floatFromInt(delay_ms)) * self.multiplicador)),
                        self.delay_maximo_ms,
                    );
                }
            }
            unreachable;
        }
    };
}

// Uso
var contador_falhas: u32 = 0;

fn operacaoInstavel() ![]const u8 {
    contador_falhas += 1;
    if (contador_falhas < 3) return error.TemporariamenteIndisponivel;
    return "dados recebidos";
}

pub fn main() !void {
    const config = RetryConfig([]const u8, anyerror){
        .max_tentativas = 5,
        .delay_inicial_ms = 100,
        .multiplicador = 2.0,
    };

    const resultado = try config.executar(operacaoInstavel);
    std.debug.print("Resultado: {s}\n", .{resultado});
}
```

## Retry com Filtro de Erros

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

fn retrySeRetentavel(
    comptime T: type,
    operacao: anytype,
    args: anytype,
    max_tentativas: u32,
) !T {
    var tentativa: u32 = 0;
    while (tentativa < max_tentativas) : (tentativa += 1) {
        if (@call(.auto, operacao, args)) |resultado| {
            return resultado;
        } else |err| {
            // Só retentar erros temporários
            switch (err) {
                error.Timeout,
                error.ConexaoRecusada,
                error.ServicoIndisponivel,
                => {
                    if (tentativa + 1 < max_tentativas) {
                        const delay = std.math.shl(u64, 100, @intCast(tentativa));
                        std.time.sleep(delay * std.time.ns_per_ms);
                        continue;
                    }
                },
                // Erros permanentes — não retentar
                else => return err,
            }
            return err;
        }
    }
    unreachable;
}
```

## Retry com Deadline

Em vez de limitar pelo número de tentativas, você pode limitar pelo tempo total máximo:

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

fn retryComDeadline(
    comptime T: type,
    operacao: *const fn () anyerror!T,
    deadline_ns: i128,
    delay_inicial_ms: u64,
) !T {
    var delay_ms = delay_inicial_ms;
    while (true) {
        if (operacao()) |resultado| return resultado else |_| {}

        const agora = std.time.nanoTimestamp();
        if (agora >= deadline_ns) return error.DeadlineExcedido;

        // Calcular quanto tempo resta
        const restante_ns = deadline_ns - agora;
        const delay_ns = delay_ms * std.time.ns_per_ms;

        if (delay_ns >= @as(u64, @intCast(restante_ns))) return error.DeadlineExcedido;

        std.time.sleep(delay_ns);
        delay_ms = @min(delay_ms * 2, 10_000); // backoff com cap de 10s
    }
}

pub fn main() !void {
    const deadline = std.time.nanoTimestamp() + 30 * std.time.ns_per_s; // 30s
    const resultado = try retryComDeadline([]const u8, minhaOperacao, deadline, 100);
    _ = resultado;
}

fn minhaOperacao() ![]const u8 {
    return error.Falhou;
}
```

## Considerações de Performance

- **`std.time.sleep` é uma syscall**: em sistemas com muitas retentativas simultâneas, considere agendar as retentativas em uma fila de eventos em vez de bloquear uma thread por operação.
- **Jitter é essencial em sistemas distribuídos**: sem jitter, todos os clientes que falharam ao mesmo tempo vão retentar ao mesmo tempo, causando uma nova onda de falhas no serviço. O jitter distribui as retentativas aleatoriamente no tempo.
- **Backoff exponencial com cap**: sem cap (`delay_maximo_ms`), o delay pode crescer para horas após muitas falhas. Sempre limite o delay máximo a um valor razoável para o seu caso de uso (tipicamente 30s a 5min).
- **Evite retentar se a operação não for idempotente**: se a operação pode ter efeitos colaterais (débito em conta, envio de email), um retry pode executar a operação mais de uma vez. Use idempotency keys ou verifique o estado antes de retentar.

## Erros Comuns

**Retentar todos os erros indiscriminadamente**: `error.AutenticacaoFalhou` e `error.PermissaoNegada` nunca vão se resolver com mais tentativas. Filtre os erros retentáveis explicitamente com `switch`, como demonstrado no exemplo `retrySeRetentavel`.

**Não logar as tentativas em produção**: sem logs, você não tem visibilidade de quantas retentativas estão acontecendo. Isso pode mascarar problemas sérios — um sistema que retenta 4 vezes por requisição está efetivamente recebendo 5x a carga esperada.

**Compartilhar contador de falhas entre requests**: `var contador_falhas: u32 = 0` como variável global faz com que o estado de uma operação afete outras. Mantenha o estado de retry local a cada invocação.

## Perguntas Frequentes

**Qual é a fórmula correta para backoff exponencial com jitter?**
`delay = min(cap, base * 2^tentativa) + random(0, base * 2^tentativa / 2)`. A parte fixa garante espaçamento mínimo entre tentativas; o jitter distribui as retentativas no tempo.

**Devo usar Retry ou Circuit Breaker?**
Use ambos em camadas. O Retry lida com falhas esporádicas (1-2 tentativas antes de sucesso). O Circuit Breaker detecta quando o serviço está completamente fora do ar e para de retentar por um período, evitando sobrecarga.

**Como testar o comportamento de retry sem esperar os sleeps?**
Injete uma função de sleep como dependência: `sleepFn: *const fn (u64) void`. Em testes, passe uma função que apenas conta o número de sleeps sem realmente esperar. Isso torna os testes de retry rápidos e determinísticos.

## Quando Evitar

- Erros que não são temporários (autenticação, permissão, dados inválidos)
- Operações não idempotentes (pagamentos, envio de email)
- Quando o serviço está claramente offline (use Circuit Breaker)
- Loops infinitos de retry sem backoff

## Veja Também

- [Circuit Breaker](/padroes/circuit-breaker/) — Parar de retentar quando há muitas falhas
- [Error Handling](/cheatsheets/error-handling/) — Tratamento de erros em Zig
- [Strategy](/padroes/strategy/) — Diferentes estratégias de backoff
- [Concorrência](/cheatsheets/concorrencia/) — Retry com timeout
- [FAQ Produção](/faq/faq-producao/) — Resiliência em sistemas reais
