Camada Zero · 35 · Circuit Breakers, Retries & Backoff

Quando um serviço começa a falhar, a reação ingênua (retentar na hora) é o que derruba ele de vez. Liga o downstream pra falhar aqui embaixo e compara as estratégias: olha a carga do downstream subir com retry imediato e o circuit breaker cortando a sangria.
🖥️
cliente
🛢️
downstream
carga do downstream
0Requests do cliente
0Retries disparados
0%Carga no downstream
Breakerdesligado
sucesso falha fail fast (breaker open) em trânsito
Roteiro: deixa o downstream falhando com retry imediato e veja a carga estourar (retry storm). Troca pra backoff + jitter e a carga cai. Agora liga o circuit breaker: depois de algumas falhas ele abre, para de chamar (fail fast) e dá tempo do downstream respirar.

Estados do breaker

closedTudo normal, as chamadas passam. Conta as falhas.
openEstourou o limite de falhas. Fail fast: nem chama o downstream, devolve erro na hora.
half-openPassou o cooldown. Deixa uma chamada de teste passar. Deu certo, fecha. Falhou, abre de novo.

Quando NÃO retry

4xxErro do cliente. Vai falhar igual no retry.
não idempotenteSem chave de idempotência, retry pode cobrar duas vezes (aula 36).

Retry com backoff + jitter (Go)

func retry(fn func() error) error {
    var err error
    for n := 0; n < maxRetries; n++ {
        if err = fn(); err == nil {
            return nil
        }
        if !isTransient(err) {
            return err // 4xx, não idempotente: para
        }
        base := time.Second * (1 << n) // 1s, 2s, 4s
        jitter := time.Duration(rand.Int63n(int64(base)))
        time.Sleep(base/2 + jitter) // espalha
    }
    return err
}

Circuit breaker (Go)

func (b *Breaker) Call(fn func() error) error {
    if b.state == "open" {
        if time.Now().Before(b.openUntil) {
            return ErrOpen // fail fast, nem chama
        }
        b.state = "half" // testa se voltou
    }
    if err := fn(); err != nil {
        b.fails++
        if b.fails >= b.threshold {
            b.state = "open"
            b.openUntil = time.Now().Add(b.cooldown)
        }
        return err
    }
    b.fails, b.state = 0, "closed" // fechou
    return nil
}

🧠 Desafio — Circuit Breakers & Retries

Mexe no visualizador antes de responder (downstream falhando, troca a estratégia de retry, liga o breaker). As duas últimas são de reflexão: escreve a sua e só então revela o modelo.