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

TipoDescriçãoExemplo
Connection timeoutTempo máximo para estabelecer a conexão TCP2-5 segundos
Read/Response timeoutTempo máximo para receber a resposta completa5-30 segundos
Request timeoutTempo total da operação (conexão + leitura)10-60 segundos
Idle timeoutTempo máximo de inatividade em uma conexão aberta30-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érioTimeout sugeridoRaciocínio
API interna rápida1-3 segundosLatência esperada < 200ms
API interna com DB5-10 segundosQueries complexas podem demorar
API externa (terceiro)10-30 segundosMenos controle sobre a latência
Operação batch60-300 segundosProcessamento pesado esperado
Health check2-5 segundosDeve 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égiaDescriçãoQuando usar
Erro imediatoRetornar HTTP 504 Gateway TimeoutQuando não há alternativa
FallbackRetornar dados em cache ou padrãoQuando há dados alternativos
RetryTentar novamente a operaçãoQuando a falha pode ser transitória
DegradaçãoOferecer funcionalidade reduzidaQuando 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

CategoriaOpções
HTTP ClientsAxios (Node.js), HttpClient (.NET), OkHttp (Java), requests (Python)
FrameworksSpring Boot (configuração de timeouts), ASP.NET Core, Express.js
Bancos de dadosConnection pool timeouts, query timeouts, statement timeouts
Service MeshIstio, Linkerd, Envoy (timeouts em nível de infraestrutura)
CloudAWS 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.