Erro e resiliência

Padrões de resiliência em ação: circuit breaker, retry, timeout e fallback aplicados a fluxos reais entre serviços.

Por que a resiliência faz parte do fluxo

Em um sistema distribuído, as falhas não são exceções: são parte normal da operação. Serviços que caem, redes que falham, bancos de dados que ficam saturados. A pergunta não é se vai falhar, mas sim como o sistema se comporta quando falha.

Os padrões de resiliência não são conceitos teóricos isolados. Eles se aplicam aos fluxos reais entre serviços para que o sistema continue funcionando de forma degradada em vez de entrar em colapso completo.

Os quatro padrões principais

Timeout

Estabelece um tempo máximo de espera para uma operação. Se não houver resposta dentro desse tempo, a requisição é cancelada e o erro é tratado.

Retry

Tenta novamente uma operação que falhou, partindo do princípio de que a falha pode ser transitória (um pico de carga, um erro de rede momentâneo).

Circuit Breaker

Detecta quando um serviço está falhando repetidamente e para de enviar requisições a ele por um tempo, permitindo que ele se recupere.

Fallback

Fornece uma resposta alternativa quando a operação principal falha.

Timeout em ação

sequenceDiagram
    participant BFF as BFF
    participant MS as ms-catalog
    participant DB as Database

    BFF->>MS: GET /products (timeout: 3s)
    MS->>DB: SELECT * FROM products
    Note over DB: La DB está lenta (5s)
    
    BFF--xMS: Timeout después de 3s
    BFF->>BFF: Manejar timeout
    BFF->>BFF: Devolver respuesta de caché o error

Como configurar timeouts

Cada chamada entre serviços deve ter um timeout explícito. Sem timeout, uma requisição pode ficar pendurada indefinidamente, consumindo recursos.

Regras práticas:

  • O timeout deve ser maior que o tempo de resposta normal do serviço
  • Mas menor que o tempo que o usuário está disposto a esperar
  • Diferentes operações podem ter diferentes timeouts
OperaçãoTimeout sugerido
Consulta de catálogo2–3 segundos
Criação de pedido5 segundos
Pagamento externo10–30 segundos
Envio de notificação5 segundos

O que fazer quando ocorre um timeout

  1. Registrar o timeout nos logs com contexto (serviço, operação, duração)
  2. Retornar uma resposta degradada se possível (dados de cache, valores padrão)
  3. Não presumir que a operação falhou: ela pode ter sido concluída, mas a resposta se perdeu

Retry em ação

sequenceDiagram
    participant BFF as BFF
    participant MS as ms-orders

    BFF->>MS: POST /orders
    MS-->>BFF: 503 Service Unavailable

    Note over BFF: Esperar 1s (intento 2)
    BFF->>MS: POST /orders
    MS-->>BFF: 503 Service Unavailable

    Note over BFF: Esperar 2s (intento 3)
    BFF->>MS: POST /orders
    MS-->>BFF: 201 Created

Estratégias de retry

Retry imediato: tentar novamente sem espera. Útil apenas para erros muito transitórios.

Retry com backoff fixo: esperar um tempo fixo entre as tentativas (1s, 1s, 1s).

Retry com backoff exponencial: dobrar o tempo de espera a cada tentativa (1s, 2s, 4s, 8s). É a estratégia mais recomendada porque reduz a pressão sobre o serviço que está falhando.

Retry com jitter: adicionar um componente aleatório ao backoff para evitar que vários clientes tentem novamente ao mesmo tempo (thundering herd).

Quando tentar novamente e quando não

Tentar novamente:

  • Erros 503 (Service Unavailable)
  • Erros 429 (Too Many Requests)
  • Timeouts de rede
  • Erros de conexão

Não tentar novamente:

  • Erros 400 (Bad Request) — a requisição é inválida
  • Erros 401/403 — problema de autenticação/autorização
  • Erros 404 — o recurso não existe
  • Erros 409 (Conflict) — conflito de estado

Idempotência e retry

Se uma operação de escrita for repetida (criar pedido, processar pagamento), o serviço receptor deve ser idempotente. Caso contrário, o retry pode criar duplicatas.

Circuit Breaker em ação

stateDiagram-v2
    [*] --> Closed
    Closed --> Open: N fallos consecutivos
    Open --> HalfOpen: Después del timeout
    HalfOpen --> Closed: Request exitoso
    HalfOpen --> Open: Request falla

Os três estados

Closed (fechado): o circuito funciona normalmente. As requisições passam para o serviço. Se as falhas se acumulam, o circuito abre.

Open (aberto): o circuito está interrompido. As requisições não chegam ao serviço; um erro ou um fallback é retornado imediatamente. Isso protege o serviço que está falhando e evita que o chamador desperdice recursos esperando.

Half-Open (semiaberto): após um tempo, o circuito permite que uma requisição de teste passe. Se ela tiver sucesso, o circuito fecha. Se falhar, ele volta a abrir.

Exemplo concreto

O BFF chama o microsserviço de catálogo. Se 5 das últimas 10 requisições falharam:

  1. O circuit breaker abre
  2. Durante 30 segundos, todas as requisições ao catálogo recebem um fallback (dados de cache)
  3. Após 30 segundos, uma requisição de teste é permitida
  4. Se funcionar, o circuito fecha e o tráfego normal é retomado

Configuração típica

ParâmetroValor típico
Limiar de falhas50% em janela de 10 requests
Tempo no estado Open30 segundos
Requests de teste em Half-Open1

Fallback em ação

Quando uma operação falha (por timeout, circuit breaker aberto ou erro), o fallback fornece uma alternativa:

ServiçoFallback
CatálogoRetornar dados de cache (mesmo que estejam desatualizados)
RecomendaçõesRetornar produtos populares genéricos
Perfil de usuárioRetornar dados básicos do token JWT
NotificaçõesEnfileirar para envio posterior
AnalyticsDescartar o evento (não é crítico)

Níveis de degradação

Nem todas as falhas exigem a mesma resposta. É possível definir níveis:

  1. Degradação mínima: dados ligeiramente desatualizados (cache de 5 minutos)
  2. Degradação moderada: funcionalidade reduzida (catálogo sem filtros avançados)
  3. Degradação severa: funcionalidade mínima (somente leitura, sem operações de escrita)
  4. Indisponibilidade: página de manutenção

Combinando padrões

Na prática, esses padrões se combinam:

BFF → [Timeout: 3s] → [Retry: 3 intentos con backoff] → [Circuit Breaker] → ms-catalog
                                                                    ↓ (si abierto)
                                                              [Fallback: caché]
  1. O BFF faz uma requisição com timeout de 3 segundos
  2. Se houver timeout, tenta novamente até 3 vezes com backoff exponencial
  3. Se as tentativas falharem, o circuit breaker abre
  4. Com o circuito aberto, as requisições seguintes recebem o fallback diretamente

Observabilidade da resiliência

Cada padrão deve gerar métricas e logs:

  • Timeouts: quantos, em qual serviço, duração
  • Retries: quantas tentativas, taxa de sucesso em cada tentativa
  • Circuit Breaker: transições de estado, tempo em cada estado
  • Fallbacks: quantas vezes foi ativado, qual tipo de fallback

Essas métricas permitem detectar problemas antes que se tornem incidentes graves.

Resumo

A resiliência não é um componente que se adiciona no final. É uma propriedade do sistema que se projeta em cada fluxo. O timeout evita esperas infinitas, o retry lida com falhas transitórias, o circuit breaker protege serviços saturados e o fallback mantém a funcionalidade degradada. Combinados, eles permitem que o sistema continue funcionando mesmo quando partes dele falham.