Definition
Imagina que estás esperando a un amigo que dijo que estaría ahí en 5 minutos. Después de 30 minutos de espera, decides que no viene y te vas. Ese punto de decisión - el momento en que dejas de esperar - es un timeout. En software, los timeouts son límites explícitos de cuánto tiempo puede tomar una operación antes de considerarse fallida, previniendo que tu sistema espere para siempre por algo que podría nunca pasar.
Los timeouts son esenciales para construir sistemas distribuidos confiables. Sin ellos, un solo servicio lento o que no responde puede causar fallos en cascada: las conexiones se acumulan esperando, los pools de hilos se agotan, la memoria se llena con peticiones pendientes, y eventualmente tu sistema entero se detiene. Un timeout bien configurado dice “si esto no se completa en X segundos, ríndete y maneja el fallo” - convirtiendo un cuelgue indefinido en un error predecible y manejable.
Hay varios tipos de timeouts a considerar: Timeout de conexión (cuánto esperar para que se establezca una conexión), Timeout de lectura (cuánto esperar por datos después de conectar), Timeout de escritura (cuánto esperar para que se envíen los datos), y Timeout de petición (tiempo total para toda la operación). Cada uno sirve un propósito diferente y necesita configuración independiente. Muy corto y fallarás operaciones lentas legítimas; muy largo y desperdiciarás recursos esperando conexiones muertas.
Example
Flujo de Checkout de E-commerce: El proceso de checkout de Amazon tiene timeouts ajustados para cada llamada a microservicio. Si el servicio de inventario no responde en 500ms, el checkout falla rápido con “no se pudo verificar stock” en lugar de hacer al cliente esperar 30 segundos. Esto mantiene la experiencia general de checkout ágil incluso cuando los servicios están luchando.
Timeouts de Edge de CDN: Cloudflare configura diferentes timeouts para diferentes tipos de contenido. Las imágenes estáticas obtienen un timeout de origen de 5 segundos, mientras que las respuestas de API podrían obtener 30 segundos. Si el origen no responde a tiempo, Cloudflare sirve una versión cacheada (si está disponible) o un error significativo.
Timeouts de Consultas de Base de Datos: El statement_timeout de PostgreSQL previene que consultas descontroladas consuman recursos para siempre. Una consulta que escanearía miles de millones de filas se mata después de 30 segundos, protegiendo la base de datos de que una mala consulta afecte a todos.
Timeouts de Health Check: Los probes de salud de Kubernetes tienen timeouts estrictos - típicamente 1-3 segundos. Si un contenedor no responde a un health check a tiempo, se considera no saludable. Esto previene que el tráfico se envíe a contenedores colgados.
Integración de Gateway de Pago: El cliente API de Stripe tiene un timeout por defecto de 120 segundos, pero la mayoría de operaciones de pago se completan en 2-3 segundos. Si una petición de pago expira, el cliente reintenta con claves de idempotencia, y los timeouts del lado del servidor de Stripe previenen que las transacciones queden en el limbo.
Analogy
El Pedido del Restaurante: Pides comida y el mesero dice que serán 15 minutos. Después de 45 minutos sin comida y sin actualizaciones, llamas al mesero. Eso es un timeout - decidiste que algo salió mal. Los buenos restaurantes (como los buenos sistemas) te dan actualizaciones (“disculpa el retraso”) o fallan rápido (“se nos acabó ese plato”).
El Botón del Elevador: Presionas el botón del elevador y esperas. Después de 2 minutos sin respuesta, tomas las escaleras. El elevador podría todavía estar viniendo, pero esperar indefinidamente no es práctico. Tu umbral de paciencia de 2 minutos es un timeout.
El Tiempo de Espera en Llamada Telefónica: “Tu llamada es importante para nosotros, por favor espera.” Después de 20 minutos en espera, la mayoría de la gente cuelga. Ese cuelgue es un timeout. Los buenos sistemas te dicen “tiempo de espera estimado: 5 minutos” para que puedas decidir si esperar o llamar después.
La Garantía de Entrega de Pizza: “30 minutos o es gratis” es un timeout de negocio - una promesa de que la operación (entrega) se completará dentro de cierto tiempo o hay consecuencias. Los timeouts en software hacen garantías similares sobre tiempos de respuesta.
Code Example
// Configuración completa de timeout
interface TimeoutConfig {
connectionTimeoutMs: number; // Tiempo para establecer conexión
readTimeoutMs: number; // Tiempo para recibir respuesta
writeTimeoutMs: number; // Tiempo para enviar petición
requestTimeoutMs: number; // Tiempo total de operación
idleTimeoutMs: number; // Timeout de conexión keep-alive
}
const defaultTimeouts: TimeoutConfig = {
connectionTimeoutMs: 5000, // 5 segundos para conectar
readTimeoutMs: 30000, // 30 segundos para leer
writeTimeoutMs: 10000, // 10 segundos para escribir
requestTimeoutMs: 60000, // 60 segundos total
idleTimeoutMs: 120000 // 2 minutos idle antes de desconectar
};
// Cliente HTTP con timeouts
async function fetchWithTimeout(
url: string,
options: RequestInit = {},
timeoutMs: number = 30000
): Promise<Response> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
return response;
} catch (error) {
if (error.name === 'AbortError') {
throw new TimeoutError(`Petición a ${url} expiró después de ${timeoutMs}ms`);
}
throw error;
} finally {
clearTimeout(timeoutId);
}
}
// Cliente estilo Axios con timeouts granulares
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 30000, // Timeout de petición
// Timeout de conexión vía httpAgent/httpsAgent
});
// Usando node-fetch con timeout
import fetch from 'node-fetch';
import https from 'https';
const agent = new https.Agent({
keepAlive: true,
timeout: 5000, // Timeout de socket
});
// Consulta de base de datos con timeout
import { Pool } from 'pg';
const pool = new Pool({
connectionTimeoutMillis: 5000, // Esperar 5s por conexión
idleTimeoutMillis: 30000, // Cerrar idle después de 30s
statement_timeout: 30000, // Matar consultas después de 30s
});
async function executeWithTimeout(query: string, params: any[], timeoutMs: number) {
const client = await pool.connect();
try {
// Establecer timeout por consulta
await client.query(`SET statement_timeout = ${timeoutMs}`);
return await client.query(query, params);
} finally {
await client.query('RESET statement_timeout');
client.release();
}
}
// Circuit breaker con timeout
async function callServiceWithTimeout<T>(
operation: () => Promise<T>,
timeoutMs: number,
fallback?: () => T
): Promise<T> {
return Promise.race([
operation(),
new Promise<never>((_, reject) =>
setTimeout(
() => reject(new TimeoutError('Operación expiró')),
timeoutMs
)
)
]).catch(error => {
if (error instanceof TimeoutError && fallback) {
console.warn('Operación expiró, usando fallback');
return fallback();
}
throw error;
});
}
class TimeoutError extends Error {
constructor(message: string) {
super(message);
this.name = 'TimeoutError';
}
}
Diagram
flowchart LR
subgraph Timeouts["Tipos de Timeout"]
A[Timeout de
Conexión] --> B[Timeout de
Escritura]
B --> C[Timeout de
Lectura]
D[Timeout de Petición
Duración Total]
end
subgraph Timeline["Línea de Tiempo de Petición"]
E[Búsqueda
DNS]
F[Conexión
TCP]
G[Handshake
TLS]
H[Enviar
Petición]
I[Esperar
Respuesta]
J[Recibir
Respuesta]
end
E --> F --> G --> H --> I --> J
A -.->|cubre| F
A -.->|cubre| G
B -.->|cubre| H
C -.->|cubre| I
C -.->|cubre| J
D -.->|cubre todo| Timeline
style A fill:#93c5fd
style B fill:#fcd34d
style C fill:#86efac
style D fill:#f9a8d4
Best Practices
- Establece timeouts explícitos en todas partes - Nunca confíes en valores por defecto; haz los valores de timeout explícitos en tu código
- Usa diferentes timeouts para diferentes operaciones - Health checks necesitan timeouts cortos, trabajos batch necesitan más largos
- Enfoque de presupuesto de timeout - Si el presupuesto total es 30s y tienes 3 llamadas seriales, cada una obtiene aproximadamente 10s máximo
- Timeout de conexión < Timeout de lectura - Las conexiones deben establecerse rápido; leer datos puede tomar más tiempo
- Considera la cadena completa - Tu timeout debe considerar todos los servicios downstream más tiempo de procesamiento
- Registra eventos de timeout - Los timeouts son señales de estrés del sistema que deben monitorearse y alertarse
- Falla rápido en timeout de conexión - Si no puedes conectar en unos segundos, esperar más rara vez ayuda
- Usa timeout junto con retry - Timeout captura peticiones atascadas; retry maneja fallos transitorios
- Prueba tus timeouts - Inyecta latencia para verificar que tu sistema se comporta correctamente cuando los timeouts se disparan
- Documenta valores de timeout - Los futuros mantenedores necesitan entender por qué los timeouts están configurados a valores específicos
Common Mistakes
Sin timeout configurado: El error más peligroso - sin timeouts, un servicio lento puede colgar todo tu sistema para siempre.
Timeouts muy largos: Un timeout de 5 minutos en una API orientada al usuario significa que los usuarios esperan 5 minutos antes de ver un error. La mayoría de usuarios abandonan después de 10 segundos.
Timeouts muy cortos: Dar timeout a una consulta de base de datos después de 1 segundo podría matar consultas complejas legítimas durante carga pico.
Solo timeout de petición, no timeout de conexión: Un servidor que acepta conexiones pero nunca responde mantendrá recursos por todo el timeout de petición.
No propagar presupuesto de tiempo restante: Si tu servicio tiene 30s para responder y ya gastó 25s, las llamadas downstream deberían obtener solo 5s, no otros 30s.
Ignorar timeout en operaciones async: Iniciar una operación async y olvidarte de ella todavía consume recursos incluso si ya seguiste adelante.
Mismo timeout para todo: Health checks, consultas de base de datos, y subidas de archivos tienen duraciones naturales muy diferentes.