A la 1 de la mañana del Black Friday de noviembre de 2024, el OMS de un 3PL transfronterizo de cosméticos en el este de China empezó a lanzar timeouts de API en masa. El primer instinto del ingeniero de guardia fue añadir reintentos: reenviar tras un timeout de 5 segundos, hasta 3 intentos. A las 3 de la mañana, toda la cadena había colapsado: el WMS no podía obtener confirmaciones de pedidos, el TMS no podía consultar facturas de flete, y los estados de las tareas de picking llevaban 40 minutos sin reportarse. El almacén volvió a la conciliación manual. No fue un incidente aislado; es un guion recurrente en las integraciones de sistemas de almacén. Un timeout de API, convertido por reintentos bienintencionados en una tormenta de reintentos.
El fenómeno: cómo un timeout se convierte en un colapso en cadena
La cronología de aquella noche es instructiva. La venta de medianoche del Black Friday disparó el volumen de pedidos del OMS; la API de recepción de pedidos del WMS se ralentizó y aparecieron timeouts de 5 segundos. La lógica de reintentos del WMS estaba codificada de forma rígida: reenviar de inmediato ante un timeout, 3 intentos. Pero la consulta de facturas de flete del TMS sufría la misma carga pico, y el tráfico de reintentos del WMS multiplicó varias veces la tasa de consultas al TMS. El TMS cedió y golpeó la API de seguimiento del transportista, activando su limitador de tasa. Una vez limitado, las devoluciones de llamada se acumularon en cola y cada actualización de estado de picking del WMS se atascó: un timeout en un eslabón, amplificado salto a salto por los reintentos de cada capa, hasta que cayó toda la cadena.
La literatura pública de diseño de sistemas nos permite cuantificar esta amplificación. Supongamos que la cadena de llamadas tiene 4 saltos (OMS → WMS → TMS → API del transportista) y cada salto reintenta dos veces ante un timeout. En el peor caso, el servicio más aguas abajo enfrenta 3 × 3 × 3 = 27 veces su volumen normal de solicitudes. Una API sana a 200 QPS se enfrenta de pronto a 5,400 QPS, mientras que los límites de tasa de los transportistas suelen fijarse en solo 2–3× el pico normal. Es un cálculo ilustrativo, pero explica por qué el TMS cayó primero aquella noche: no enfrentaba "un poco más" de tráfico, sino un aplastamiento de un orden de magnitud.
Un segundo caso merece igual atención. El TMS de un 3PL llamaba a la API de seguimiento de un transportista con un timeout de 30 segundos y sin circuit breaker. En temporada alta, las solicitudes se quedaban colgadas en timeouts de 30 segundos, el pool de hilos se llenó, y las devoluciones de llamada de estado del WMS no conseguían ningún hilo: quedaron en cola 40 minutos. La causa raíz no era realmente el timeout; era que las solicitudes con timeout seguían ocupando hilos. Los timeouts largos dañan aguas abajo más que los cortos — una conclusión que muchos ingenieros tienen al revés.
Los números: la amplificación de reintentos da más miedo de lo que parece
Llevemos el cálculo un paso más allá. El poder destructivo de una tormenta de reintentos proviene de dos multiplicadores que se combinan: la profundidad — cada salto extra multiplica la amplificación por otro conteo de reintentos; y la duración — cuanto más largo el timeout, más tiempo mantiene recursos cada solicitud colgada, y más corta la ventana de recuperación aguas abajo. Según el análisis de 2015 del AWS Architecture Blog en "Exponential Backoff And Jitter", los reintentos de ritmo fijo esconden otro asesino: la sincronización. Cada cliente expira en el mismo momento y reintenta en el mismo momento, resonando en oleadas — justo cuando aguas abajo recupera el aliento, llega la siguiente oleada.
Así que el colapso de aquella noche no fue "demasiado tráfico" sino "reintentos demasiado sincronizados". El OMS, el WMS y el TMS usaban la misma plantilla — timeout de 5 segundos, 3 reintentos inmediatos — latiendo en perfecta sincronía, como tres multitudes apretujándose por una puerta a la vez. Por eso muchas postmortems concluyen "añadir servidores" y ven poca mejora: los servidores añaden capacidad, mientras las tormentas consumen ventanas de tiempo de recuperación. Dimensiones distintas.
La atribución: cuatro mecanismos de reintento, y por qué solo sobrevive "backoff + jitter + breaker"
Alineadas lado a lado, las estrategias de reintento comunes cuentan una historia clara:
| Estrategia | Mecanismo | Amplificación | Defecto fatal |
|---|---|---|---|
| Reintento inmediato | Reenviar al instante, hasta N veces | N×, crece con la duración de la interrupción | La segunda oleada llega antes de que aguas abajo se recupere |
| Reintento a intervalo fijo | Reenviar cada T segundos | N×, escalonamiento limitado | Todos los clientes reintentan al unísono — resonancia |
| Backoff exponencial | Las esperas crecen 2s → 4s → 8s | Decae con el tiempo, da margen de respiro | No corrige la sincronización de reintentos |
| Backoff + jitter | Backoff más jitter aleatorio | Decae y desincroniza | Algo más complicado de implementar correctamente |
| Circuit breaker + fallback | Se abre ante un umbral de tasa de fallos, sondas half-open | Casi cero | Requiere una ruta de fallback diseñada |
El valor del backoff es "cada vez más despacio", comprando tiempo de recuperación; el valor del jitter es romper la sincronización — el verdadero combustible de una tormenta de reintentos no es reintentar en sí, sino que cada cliente reintente en el mismo segundo. El circuit breaker, un patrón que Michael Nygard introdujo en Release It!, abre el circuito pasado un umbral de tasa de fallos, deja de enviar solicitudes, y hace sondas half-open antes de dejar pasar el tráfico de nuevo. Para las integraciones WMS-transportista es casi obligatorio: golpear una API de transportista limitada solo consigue que te pongan en su lista de bloqueo.
Una regla más infravalorada: solo los endpoints idempotentes merecen reintentos. Las operaciones de escritura como la creación de pedidos o la deducción de inventario, reintentadas sin clave de idempotencia, pueden convertirse en pedidos duplicados y deducciones duplicadas — mucho más caras que un timeout. Muchos documentos de integración WMS/OMS escriben casualmente "reintentar 3 veces" sin responder nunca a "¿y si el pedido se procesa dos veces?".
En la práctica: tres reglas para las integraciones de sistemas de almacén
Primero, escalona tus timeouts. Las consultas de borde reciben 3 segundos, las APIs centrales de pedidos 10 segundos, y las conciliaciones pesadas por lotes omiten por completo el HTTP síncrono en favor de colas asíncronas. Los timeouts más largos no son más indulgentes — mantienen recursos ocupados más tiempo.
Segundo, escribe la política de reintentos en el documento de integración, no en los valores por defecto del código. Base del backoff, rango de jitter, intentos máximos, umbrales del breaker — cuatro números para alinear con aguas arriba y aguas abajo durante las pruebas de integración. El colapso de aquella noche ocurrió, directamente, porque tres capas compartían una configuración de plantilla y nadie consideró la resonancia.
Tercero, limita aguas abajo y aísla mamparos (bulkheads). La consulta de facturas de flete del TMS y las devoluciones de llamada de estado del WMS no deberían compartir un pool de hilos; una saturada no debe matar a la otra. La limitación del lado cliente en las APIs externas de transportistas también importa — su límite de tasa los protege a ellos; el tuyo te protege a ti.
Un juicio contraintuitivo merece énfasis: tras un timeout, "esperar más antes de reintentar" suele ser peor que "fallar rápido y dejar que el llamante encole". Los pools de hilos mueren por ocupación concurrente, no por conteo de reintentos. Una solicitud ocupando un hilo durante 30 segundos daña aguas abajo más que un timeout de 3 segundos más 2 reintentos. Timeouts cortos, pocos reintentos, fallo rápido expuesto al llamante para encolar — aparentemente peor UX, pero la opción más responsable para toda la cadena.
La tendencia es clara: el reintento está migrando de "responsabilidad del llamante" a "capacidad de la infraestructura". Las colas de mensajes muertos (dead-letter queues), el patrón outbox y las service meshes empujan la política de reintentos a la capa de infraestructura, y el código de negocio cada vez escribe menos lógica de reintentos a mano. Es un progreso — una causa raíz de las tormentas es que cada equipo escribía sus propios reintentos, ritmos nunca alineados. El día que la lógica de reintentos desaparezca por completo del código de negocio, las tormentas quedarán verdaderamente domadas. Hasta entonces, la sección de "estrategia de reintentos" de tu documento de integración WMS merece una reescritura — ahora.
Por Dr. Chen, investigador de la cadena de suministro
Traducido del inglés. En caso de discrepancia, prevalece la versión en inglés.










