Timeout Pattern
Como proteger seus serviços de esperas indefinidas estabelecendo limites de tempo nas chamadas a dependências externas.
Qual problema resolve
Quando um serviço chama outro serviço ou recurso externo, a chamada pode ficar esperando indefinidamente se o receptor estiver lento ou não responder. Sem um timeout configurado, o serviço que faz a chamada mantém a conexão aberta, consome uma thread do pool e eventualmente esgota todos os seus recursos.
Sin Timeout:
Servicio A ──► Servicio B (lento, no responde)
⏳ Esperando... 10s
⏳ Esperando... 30s
⏳ Esperando... 60s
⏳ Esperando... 120s
Thread de Servicio A bloqueado indefinidamente
Más requests llegan → más threads bloqueados
Thread pool agotado → Servicio A deja de responder
Este é um dos problemas mais comuns e perigosos em sistemas distribuídos. Um serviço lento pode ser pior do que um serviço fora do ar, porque um serviço fora do ar falha rápido, enquanto um lento consome recursos por um período prolongado.
Como funciona
O padrão Timeout estabelece um limite de tempo máximo para cada chamada a uma dependência. Se a resposta não chegar dentro do tempo configurado, a chamada é cancelada e um erro é retornado ou um fallback é executado.
Con Timeout (5 segundos):
Servicio A ──► Servicio B (lento)
⏳ Esperando... 1s
⏳ Esperando... 3s
⏳ Esperando... 5s
⏠ TIMEOUT → Error o fallback
Thread liberado después de 5 segundos
Servicio A puede atender otras solicitudes
Tipos de timeout
| Tipo | Descrição | Exemplo |
|---|---|---|
| Connection timeout | Tempo máximo para estabelecer a conexão TCP | 2-5 segundos |
| Read/Response timeout | Tempo máximo para receber a resposta completa | 5-30 segundos |
| Request timeout | Tempo total da operação (conexão + leitura) | 10-60 segundos |
| Idle timeout | Tempo máximo de inatividade em uma conexão aberta | 30-120 segundos |
Como escolher o valor do timeout
Não existe um valor universal. O timeout deve se basear em dados reais:
Paso 1: Medir la latencia normal del servicio
P50 (mediana): 100ms
P95: 500ms
P99: 2s
Paso 2: Establecer timeout con margen
Timeout = P99 × 2 = 4 segundos
Esto permite que el 99% de las llamadas normales
completen, pero corta las que tardan anormalmente.
| Critério | Timeout sugerido | Raciocínio |
|---|---|---|
| API interna rápida | 1-3 segundos | Latência esperada < 200ms |
| API interna com DB | 5-10 segundos | Queries complexas podem demorar |
| API externa (terceiro) | 10-30 segundos | Menos controle sobre a latência |
| Operação batch | 60-300 segundos | Processamento pesado esperado |
| Health check | 2-5 segundos | Deve responder rápido ou algo está errado |
Timeout em cadeia
Em uma cadeia de chamadas, os timeouts devem ser decrescentes para evitar que um serviço intermediário espere mais do que o serviço que o chamou:
Cliente (timeout: 30s)
└──► API Gateway (timeout: 25s)
└──► Servicio A (timeout: 10s)
└──► Servicio B (timeout: 5s)
Si Servicio B tarda 6s:
Servicio B: timeout a los 5s → error
Servicio A: recibe error, puede hacer fallback
API Gateway: recibe respuesta de A (con fallback)
Cliente: recibe respuesta dentro de sus 30s
Se os timeouts não forem decrescentes, pode acontecer de o cliente já ter abandonado a solicitação quando o serviço finalmente responder, desperdiçando recursos.
Estratégias diante de um timeout
| Estratégia | Descrição | Quando usar |
|---|---|---|
| Erro imediato | Retornar HTTP 504 Gateway Timeout | Quando não há alternativa |
| Fallback | Retornar dados em cache ou padrão | Quando há dados alternativos |
| Retry | Tentar novamente a operação | Quando a falha pode ser transitória |
| Degradação | Oferecer funcionalidade reduzida | Quando parte do dado é suficiente |
Vantagens
- Liberação de recursos: As threads não ficam bloqueadas indefinidamente
- Fail fast: O usuário recebe uma resposta (mesmo que seja de erro) em tempo previsível
- Prevenção de cascatas: Um serviço lento não arrasta consigo os que dependem dele
- Implementação simples: A maioria dos HTTP clients e frameworks suporta timeouts nativamente
- Previsibilidade: O tempo máximo de resposta do sistema é conhecido e limitado
- Base para outros padrões: O timeout alimenta as métricas do Circuit Breaker e as tentativas do Retry
Trade-offs / Desvantagens
- Falsos positivos: Um timeout muito agressivo pode cortar operações legítimas que demoram mais que o normal
- Operações órfãs: O serviço downstream pode completar a operação depois do timeout, gerando inconsistências
- Configuração delicada: Valores incorretos causam mais problemas do que soluções
- Não resolve a causa raiz: O timeout protege o chamador, mas o serviço lento continua precisando de correção
- Complexidade em cadeias: Coordenar timeouts em cadeias de serviços exige um design cuidadoso
Quando usar
- Toda chamada a um serviço externo ou dependência de rede (é uma prática obrigatória)
- Consultas a bancos de dados que podem travar por locks ou queries lentas
- Chamadas a APIs de terceiros com latência variável
- Qualquer operação de I/O que possa bloquear indefinidamente
- Como complemento do Circuit Breaker (o timeout define quando uma chamada é considerada como falha)
Quando evitar
- Operações locais em memória, onde o timeout não faz sentido
- Processos batch de longa duração, onde o timeout deve ser muito alto ou gerenciado de outra forma (progress tracking)
- Streams de dados contínuos, onde a conexão deve permanecer aberta (usar heartbeats nesse caso)
Tecnologias e implementações comuns
| Categoria | Opções |
|---|---|
| HTTP Clients | Axios (Node.js), HttpClient (.NET), OkHttp (Java), requests (Python) |
| Frameworks | Spring Boot (configuração de timeouts), ASP.NET Core, Express.js |
| Bancos de dados | Connection pool timeouts, query timeouts, statement timeouts |
| Service Mesh | Istio, Linkerd, Envoy (timeouts em nível de infraestrutura) |
| Cloud | AWS ALB/NLB timeouts, Azure Application Gateway, Cloud Load Balancing |
Relação com outros padrões
Solicitud
│
▼
┌──────────┐
│ Timeout │ ← Define cuánto esperar
└────┬─────┘
▼
┌──────────┐
│ Retry │ ← Reintenta si hay timeout
└────┬─────┘
▼
┌──────────┐
│ Circuit │ ← Se abre si hay muchos timeouts
│ Breaker │
└──────────┘
- Retry: Quando ocorre um timeout, o Retry pode tentar novamente a operação
- Circuit Breaker: Os timeouts contam como falhas; muitos timeouts abrem o circuito
- Bulkhead: Isola os recursos para que os timeouts de um serviço não afetem outros
- Fallback: Estratégia de resposta quando ocorre um timeout
Próximos passos
O Timeout é a base de toda estratégia de resiliência. Para lidar com novas tentativas após um timeout, explore o padrão Retry. Para entender como os timeouts alimentam o Circuit Breaker, confira o artigo do Circuit Breaker.