La respuesta directa

Si tu backend habla con APIs de terceros y no tienes timeouts configurados en esas llamadas salientes, estás a un proveedor lento de una caída. Define un connect timeout corto (cuánto esperas para abrir la conexión), un read timeout aparte (cuánto esperas la respuesta una vez conectado) y un presupuesto total de upstream por tipo de endpoint. Nunca permitas que una llamada saliente bloquee más de lo que el usuario está dispuesto a esperar, ni dejes que un mismo proveedor absorba todos tus hilos de trabajo mientras esperas.

Para un founder en solitario que lanza un producto con IA, esta es una de las mejoras de fiabilidad más baratas que existen. Es configuración, no arquitectura. Pero si te equivocas, un LLM inestable, una pasarela de pago o un servicio de email pueden congelar todo tu backend.

Por qué un proveedor lento puede tumbar tu backend entero

Imagina un backend en un VPS pequeño o en un contenedor modesto. Tienes un pool de workers, quizá una cola, y llamas a tres o cuatro proveedores: una API de LLM, una de pagos, un servicio de email transaccional y una base de datos vectorial sobre HTTP.

Si una de esas llamadas se queda colgada sin timeout, el hilo que la atiende queda bloqueado. La siguiente petición que necesita ese hilo también espera. Tras unas decenas de cuelgues, el pool se agota. Las peticiones nuevas se encolan, luego el load balancer las corta, luego el usuario ve un spinner eterno, luego tu bandeja de soporte se llena. La API del LLM funciona. La de pagos funciona. El email funciona. Tu backend está muerto, porque un proveedor dejó de responder y tú le diste una correa infinita.

Este es el patrón de fallo en cascada que más castiga a los equipos pequeños. No tienes pools redundantes, no tienes un equipo de plataforma triando el incidente y a tus usuarios no les importa de quién es la culpa.

Connect timeout y read timeout, en lenguaje claro

La mayoría de clientes HTTP te dejan configurarlos por separado. Son problemas distintos y merecen límites distintos.

Connect timeout es cuánto esperas a que se establezca la conexión TCP. Si falla, el proveedor es inalcanzable, tu DNS está roto o hay un firewall en medio. Un connect timeout razonable para la mayoría de APIs de terceros está en cifras de un solo dígito en segundos. Las fuentes del dossier que revisamos coinciden en sugerir valores en el rango de 3 a 10 segundos. Si no puedes establecer una conexión en 5 segundos, esperar más rara vez ayuda.

Read timeout es cuánto esperas a que lleguen datos una vez abierta la conexión. Este es el peligroso. Un proveedor puede aceptar tu conexión en milisegundos y luego tardar una eternidad en responder de verdad. El read timeout es donde las llamadas lentas a LLMs, las exportaciones lentas y las APIs de base de datos sobre HTTP te van a quemar. El trade-off es directo: demasiado corto y rechazas peticiones legítimas, demasiado largo y atas recursos esperando algo que quizá nunca llegue.

Total timeout es el seguro global. Connect más read siempre debe ser menor o igual que tu timeout total. Es tu red de seguridad frente a un bug de la librería, un reintento mal configurado o un read timeout que se reinicia silenciosamente en un socket keepalive.

Un punto de partida útil extraído de guías habituales: los healthchecks llevan presupuestos ajustados (connect alrededor de 3 segundos, read alrededor de 5 segundos), los GETs simples llevan presupuestos medios (connect 5 segundos, read entre 10 y 15 segundos), y las escrituras que pueden implicar transacciones llevan más holgura (connect 5 segundos, read 30 segundos). Las operaciones pesadas como exportaciones o informes grandes son un problema distinto y deberían ir por una vía asíncrona.

Elige un presupuesto de upstream más pequeño de lo que crees

Un modelo mental útil: cada llamada saliente a un tercero es un préstamo de la atención de tu servidor. Acorta el plazo del préstamo por debajo de la paciencia del usuario que la disparó.

Si tu usuario espera una respuesta síncrona de tu backend, el presupuesto de upstream para cualquier llamada dentro de esa petición debería estar muy por debajo de tu presupuesto total de petición. Si te das 30 segundos de extremo a extremo y llamas a dos proveedores en secuencia, ninguna llamada puede permitirse esperar 25 segundos. Si no, ya has fallado al usuario antes de que la segunda llamada empiece.

En un producto con IA esto importa especialmente porque las llamadas al LLM son la dependencia externa más lenta que vas a tener. Un endpoint de chat con un presupuesto total de 60 segundos que llama a un LLM con un read timeout de 55 segundos no tiene margen para nada más: ni reintento, ni logging, ni segunda llamada a otro proveedor, ni degradación elegante. Lo correcto suele ser un read timeout más corto en la llamada al LLM combinado con respuesta en streaming al usuario, para que la latencia percibida sea mucho menor que el timeout real.

Configurando timeouts en la práctica

La API exacta depende de tu stack, pero la forma es la misma en todos lados: define connect, define read, pon un tope total.

En Python con la librería requests, no hay timeout por defecto, lo que es un borde afilado. Un requests.get(url) a secas puede bloquearse para siempre. Pasa siempre una tupla: requests.get(url, timeout=(connect, read)). Para una llamada a un LLM, algo como timeout=(5, 30) dice “ríndete si no consigues conectar en 5 segundos, ríndete si no recibes respuesta en 30 segundos desde que conectaste”.

En Node, fetch no trae timeout integrado, así que necesitas un AbortController. Conecta un setTimeout que llame a controller.abort() cuando se cumpla tu presupuesto de read, captura el AbortError y trátalo como cualquier otro fallo de upstream. No confíes en que el socket subyacente se agote solo.

En Java con OkHttp, connectTimeout, readTimeout y writeTimeout tienen todos un valor por defecto de 10 segundos, que es una base razonable si no haces nada más. Apache HttpClient es parecido, pero sus defaults han sido fuente de confusión entre versiones, y HttpURLConnection tiene infinite en ambos por defecto, por lo que la configuración explícita es innegociable.

Elige los valores por tipo de endpoint, no por proveedor completo. Tu healthcheck contra la API del LLM y la llamada de chat completion contra la misma API no deberían compartir el mismo timeout.

Timeouts y reintentos son un único diseño, no dos

Un timeout sin política de reintentos es un sistema a medio construir. Una política de reintentos sin timeouts es un ataque de denegación de servicio que te estás haciendo a ti mismo.

Cuando una llamada saliente expira, tienes unas pocas opciones honestas:

  1. Fallar rápido y devolver el error. Para peticiones síncronas de usuario donde datos obsoletos o parciales son peores que ningún dato, esto suele ser lo correcto. Devuelve un 503, loguéalo y sigue.
  2. Reintentar con backoff, pero solo en operaciones idempotentes. Un POST que cobra una tarjeta no debería reintentarse a ciegas. Un GET, un check de estado o un envío de webhook sí pueden reintentarse un número pequeño de veces con backoff exponencial y jitter.
  3. Degradar. Devuelve una respuesta cacheada, un valor por defecto o un resultado parcial. En un producto con IA esto puede significar devolver una respuesta de un modelo más rápido y barato, o una respuesta plantilla con un mensaje claro de “el razonamiento profundo no está disponible temporalmente”.

Elijas lo que elijas, tu presupuesto de reintentos tiene que ser menor que tu presupuesto de upstream. Si tu read timeout es de 10 segundos y reintentas tres veces, la peor espera es de 30 segundos más el backoff, que es más de lo que la mayoría de usuarios tolera para una llamada síncrona.

El circuit breaker más barato que puedes desplegar

Una librería completa de circuit breaker es overkill para la mayoría de backends de un solo fundador. Puedes desplegar una aproximación útil en pocas líneas: lleva en memoria los fallos recientes por proveedor y, si un proveedor ha fallado N veces en los últimos M segundos, cortocircuita las llamadas nuevas durante una ventana de enfriamiento y devuelve directamente la respuesta degradada.

Esto te protege de dos modos de fallo a la vez: el proveedor lento que está a punto de agotar tu pool, y el proveedor que acaba de tener una caída y vuelve a estar disponible mientras todas las tormentas de reintentos del ecosistema le golpean a la vez. Tras el enfriamiento, prueba con una petición. Si funciona, vuelve al tráfico normal. Si falla, amplía el enfriamiento.

La implementación no necesita ser sofisticada. Un diccionario indexado por nombre de proveedor, con un contador y una marca temporal, basta para un founder en solitario.

Observabilidad que de verdad necesitas

No puedes depurar lo que no ves. Para llamadas salientes, registra al menos tres cosas: el proveedor, la familia de endpoint y qué timeout se disparó (connect, read o total). Esa única línea de log estructurado convierte un vago “la API va lenta” en señal accionable.

Monitoriza, por proveedor: número de llamadas, número de timeouts por tipo, y la p50 y p95 de latencia en ventana móvil. No necesitas una plataforma de observabilidad completa. Un pequeño endpoint de métricas que tu herramienta de monitorización pueda scrapear, o incluso un resumen diario en log, detectará la mayoría de problemas antes que tus usuarios.

Configura una alerta sobre la tasa de timeouts por proveedor, no sobre la tasa de errores global. Un pico de connect timeouts apunta a problemas de red o DNS. Un pico de read timeouts apunta a un proveedor lento o sobrecargado. Necesitan respuestas distintas.

Checklist corta antes de desplegar la próxima llamada saliente

  • Connect timeout definido, en segundos de un solo dígito.
  • Read timeout definido, dimensionado a la operación, nunca mayor de lo que el usuario puede esperar.
  • Total timeout como red de seguridad, ligeramente mayor que connect más read.
  • Timeouts configurados por tipo de endpoint, no por proveedor.
  • Política de reintentos que respete la idempotencia y que quepa en el presupuesto de upstream.
  • Alguna forma de circuit breaking o enfriamiento tras fallos repetidos.
  • Logging que distinga entre connect, read y total timeouts.
  • Una alerta sobre la tasa de timeouts por proveedor.

Nada de esto requiere un servicio nuevo, un proveedor nuevo ni un rewrite. Es un fichero de configuración, un pequeño wrapper sobre tu cliente HTTP y unas pocas líneas de log. Para un founder en solitario, es el trabajo de fiabilidad con mayor retorno que puedes hacer esta semana.

FAQ

¿Debería poner el mismo timeout para todas las llamadas salientes? No. Un healthcheck, una completion de chat y una exportación de informe deberían tener presupuestos distintos. Configura por tipo de endpoint.

¿Qué pasa si mi llamada al LLM tarda legítimamente 45 segundos? Devuelve la respuesta en streaming para que el usuario vea los tokens según llegan y mantén tu read timeout por debajo de la paciencia total del usuario. Si la llamada realmente no cabe en tu presupuesto, trátala como un fallo y degrada con elegancia.

¿Es un circuit breaker overkill para un backend de una persona? Una librería completa a menudo lo es. Un contador de fallos en memoria con un cooldown basta para evitar que un proveedor malo se coma tu pool de workers.

¿Cómo sé si mis timeouts son demasiado agresivos? Observa la tasa de read timeouts por proveedor. Si estás cortando llamadas que habrían funcionado, esa tasa se disparará en tráfico normal y tus usuarios reportarán respuestas truncadas. Afloja el presupuesto de read poco a poco hasta que la tasa de timeouts falsos caiga casi a cero.

Fuentes