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ção | Timeout sugerido |
|---|---|
| Consulta de catálogo | 2–3 segundos |
| Criação de pedido | 5 segundos |
| Pagamento externo | 10–30 segundos |
| Envio de notificação | 5 segundos |
O que fazer quando ocorre um timeout
- Registrar o timeout nos logs com contexto (serviço, operação, duração)
- Retornar uma resposta degradada se possível (dados de cache, valores padrão)
- 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:
- O circuit breaker abre
- Durante 30 segundos, todas as requisições ao catálogo recebem um fallback (dados de cache)
- Após 30 segundos, uma requisição de teste é permitida
- Se funcionar, o circuito fecha e o tráfego normal é retomado
Configuração típica
| Parâmetro | Valor típico |
|---|---|
| Limiar de falhas | 50% em janela de 10 requests |
| Tempo no estado Open | 30 segundos |
| Requests de teste em Half-Open | 1 |
Fallback em ação
Quando uma operação falha (por timeout, circuit breaker aberto ou erro), o fallback fornece uma alternativa:
| Serviço | Fallback |
|---|---|
| Catálogo | Retornar dados de cache (mesmo que estejam desatualizados) |
| Recomendações | Retornar produtos populares genéricos |
| Perfil de usuário | Retornar dados básicos do token JWT |
| Notificações | Enfileirar para envio posterior |
| Analytics | Descartar o evento (não é crítico) |
Níveis de degradação
Nem todas as falhas exigem a mesma resposta. É possível definir níveis:
- Degradação mínima: dados ligeiramente desatualizados (cache de 5 minutos)
- Degradação moderada: funcionalidade reduzida (catálogo sem filtros avançados)
- Degradação severa: funcionalidade mínima (somente leitura, sem operações de escrita)
- 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é]
- O BFF faz uma requisição com timeout de 3 segundos
- Se houver timeout, tenta novamente até 3 vezes com backoff exponencial
- Se as tentativas falharem, o circuit breaker abre
- 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.