BookinglyTech News
Software

El watchdog que pasa todos los tests y deja la sesión colgada igualmente

El parche SLICE-012 hace que un stream de proveedor bloqueado alcance un estado terminal, pero el fallo de fondo es de clasificación: un timeout solo arregla algo si su error es terminal y no reintentable.

3 min de lecturaDev.to0 vistas

Un timeout no arregla nada si su camino de fallo no desemboca en un estado terminal. Ese es el resumen de SLICE-012, el parche que se ha aplicado en el fork del harness de Ranex: un stream de proveedor que se quedaba bloqueado no tenía timeout asociado, así que su coordinador nunca se resolvía y la sesión activa seguía ocupada hasta que alguien intervenía a mano. El cambio ya enviado hace que ese stream llegue a un estado terminal por sí solo. Lo interesante es el agujero que los tests podían haber dejado pasar.

El timeout que no arregla nada

Si tu política de reintentos recrea la condición que expiró, reintentar es un bucle, no una recuperación. El runner ve un error que tiene permitido reintentar, relanza el stream, el proveedor vuelve a atascarse y el watchdog vuelve a dispararse. El test puede demostrar que el temporizador saltó y que el reintento ocurrió, y aun así no ver que el turno nunca termina.

En SLICE-012 la clasificación quedó explícita: los dos fallos del watchdog son errores tipados y no reintentables. Ese detalle sostiene todo lo demás. El prototipo anterior había esquivado el problema por accidente, y una revisión previa a la implementación encontró que ningún requisito decía que el timeout debía ser no reintentable, así que la propia slice tomó la decisión y exigió un test que probara que no se reintenta. "Expira" no basta; "falla de forma terminal, sin reintento" dice qué puede hacer el sistema después. El operador necesita un estado final, no una versión más enérgica de estar atascado.

Dos umbrales que no se pueden fusionar

Cuando los patrones de espera difieren en órdenes de magnitud hacen falta dos controles. Un idle deadline detecta el silencio entre chunks y se reinicia cada vez que llega uno; un presupuesto absoluto acota el turno completo aunque la actividad continúe. Cada uno hay que probarlo sin el otro: si metes chunks con suficiente frecuencia, el idle no dispara nunca y solo el presupuesto absoluto puede cortar un turno demasiado largo.

La primera petición del pull queda deliberadamente sin idle, y el registro de la slice explica por qué: los huecos entre chunks van de 10 a 100 ms, mientras que un modelo de razonamiento puede tardar minutos hasta el primer token. Un umbral de idle lo bastante ajustado para los huecos cortaría la primera respuesta en cada llamada. El coste de esa decisión está escrito: un proveedor que acepta la conexión y nunca envía un chunk queda acotado solo por el presupuesto absoluto, hasta 30 minutos con el valor por defecto. El documento no dice que ese ajuste sea el ideal; nombra la limitación y señala el arreglo correcto —un presupuesto separado para el primer chunk— que queda fuera de esta slice.

El segundo cuelgue, escondido detrás del primero

Al construir el arreglo apareció otro defecto: un fallo del watchdog es un error, no una interrupción. La limpieza estaba atada a interrupciones, así que las fibras de herramientas despachadas durante el streaming quedaban sin limpiar y la espera de settlement posterior mantenía el turno abierto indefinidamente. Es el tipo de fallo que solo se ve si pruebas el timeout con trabajo de herramientas en vuelo y compruebas el estado real de esas herramientas, no una línea de log que diga que la limpieza se hizo.

La conclusión operativa para cualquiera que tenga un pipeline con streaming, reintentos y estado de sesión es incómoda y poco vistosa: anota la clase de fallo de cada timeout, pregúntate si el siguiente intento cambia la condición que falló y separa tiempo hasta el primer token, huecos entre chunks y duración total. Lo que buscas es la salida del fallo, no su detección. Un test en verde puede estar demostrando únicamente que un temporizador sabe mirar el reloj.