Una API puede tener todos sus timeouts configurados y seguir esperando demasiado. Ocurre cuando cada componente cuenta desde cero: el navegador deja de esperar, la API continúa trabajando y una dependencia inicia otro intento que ya no puede ayudar a esa petición. Para diseñar los timeouts de una integración, primero hay que decidir cuánto puede durar la operación completa y después repartir ese presupuesto.
El resultado buscado es un contrato verificable: qué espera cada parte, qué ocurre al agotar el tiempo y cómo se conoce el resultado de una escritura que pudo haberse ejecutado.
Separar el límite total de las esperas parciales
No todas las opciones llamadas timeout miden lo mismo. El límite para establecer una conexión, la espera de datos y el tiempo total de una operación responden a preguntas distintas. Incluso la espera de lectura puede medir inactividad entre lecturas en vez de la duración completa de la transferencia; hay que comprobar la biblioteca concreta.
La documentación del SDK de AWS para Java distingue el timeout de la llamada completa y el de cada intento. El primero incluye los reintentos y sus esperas. También diferencia límites del cliente HTTP, como conexión, lectura y adquisición de una conexión del pool. Esa separación sirve para revisar una integración, aunque los nombres y la cobertura cambian entre clientes.
Antes de configurar valores, dibuja el recorrido real: cliente, proxy, API, pool de conexiones y servicios dependientes. Anota dónde empieza cada reloj. Comprueba también si resolución DNS, negociación TLS, colas internas y transferencia del cuerpo quedan dentro del límite que estás usando. Un parámetro aislado no demuestra que todo el recorrido esté acotado.
Ejemplo: tres pasos que no caben en cinco segundos
Supongamos una consulta ficticia con un presupuesto total de cinco segundos. Necesita autenticar una petición, recuperar información y preparar la respuesta. Los valores siguientes son un ejemplo aritmético construido, no una recomendación de configuración ni una medición de una plataforma.
| Etapa secuencial | Límite propuesto inicialmente | Problema |
|---|---|---|
| Comprobar autorización | 2 segundos | Consume parte del presupuesto antes de consultar datos |
| Consultar el servicio de datos | 3 segundos | Puede agotar por sí sola todo el tiempo restante |
| Transformar y devolver la respuesta | 1 segundo | No queda reservado al sumar los límites anteriores |
Los máximos suman seis segundos. No es posible garantizar que los tres pasos terminen dentro de cinco si todos pueden consumir su máximo. Añadir un reintento completo empeora esa incompatibilidad.
Una distribución ilustrativa reservaría un segundo para autorización, tres para datos y uno para transformación y devolución. Aun así, la aplicación debe contabilizar el tiempo realmente transcurrido y los gastos adicionales. Si al llegar a datos solo quedan dos segundos, su límite efectivo no puede seguir siendo tres.
Expresa la regla como una condición: el tiempo concedido a la siguiente operación será, como máximo, el menor entre su límite propio y el presupuesto restante después de reservar el cierre. Si ese resultado no permite un intento útil, devuelve el estado previsto sin iniciar trabajo innecesario.
Propagar un vencimiento, no reiniciar el presupuesto
Dentro de un proceso, mide duraciones con un reloj apropiado para intervalos, resistente a ajustes del reloj del sistema. Entre servicios, define un contrato de propagación: un vencimiento o una duración restante con sus reglas de interpretación. Si utilizas una hora absoluta, considera el desfase entre relojes. No aceptes que un cliente externo amplíe sin límite el tiempo máximo del servidor.
Un vencimiento tampoco sustituye los límites locales. Una dependencia puede no entenderlo, ignorarlo o seguir ejecutándose tras cerrar la conexión. El componente que llama necesita controlar su propia espera y limitar los recursos ocupados.
Reintentar solo cuando queda tiempo y la acción lo permite
Decide quién es responsable de los reintentos. Cuando cliente, API y SDK repiten de forma independiente, aumenta el número de intentos contra la misma dependencia. Registra la política efectiva de todos ellos antes de añadir otra capa.
Para cada nuevo intento, comprueba tres condiciones:
- El fallo es compatible con un reintento según el contrato de la dependencia.
- Queda presupuesto para la espera previa, el intento y la devolución de la respuesta.
- Repetir la operación conserva su identidad y no duplica efectos.
En escrituras, una clave de idempotencia puede formar parte del contrato. Un timeout indica que el llamador no obtuvo respuesta dentro de su límite; por sí solo no prueba que la escritura haya fallado ni que el servidor remoto la haya cancelado. Si existe un identificador de operación, consulta su estado antes de crear otra distinta.
Pruebas que revelan una configuración incoherente
Prepara pruebas controladas para una conexión que no llega a establecerse, una respuesta que se interrumpe, una dependencia lenta y un pool saturado. Añade una escritura que termina remotamente después de que el cliente haya dejado de esperar. Son situaciones diferentes y deben producir estados interpretables.
Comprueba el tiempo observado en el borde de entrada, no solo el que registra una dependencia. Correlaciona identificador de petición, intento, tiempo restante y resultado, sin registrar cuerpos o credenciales innecesarios. Verifica que al terminar se liberan conexiones y tareas que sí pueden cancelarse.
Si el trabajo útil supera habitualmente la espera admisible de una petición, revisa el diseño de trabajos asíncronos en una API: aceptar una operación y consultar su estado puede ser más adecuado que alargar todas las conexiones.
Para revisar una integración concreta, nuestros servicios de desarrollo web y backend para proyectos de datos pueden partir de ese mapa de llamadas. En una conversación técnica, el material útil es la secuencia de dependencias, sus límites actuales y un ejemplo de resultado incierto.