LOUST
LOUST-PRO/lzt-broker-stall-reaper
Período: jun 2025 —
El reto
Los self-hosted CI runners manteniendo sockets long-poll abiertos por horas se stalleaban silenciosamente — la conexión se veía viva a nivel de syscall pero no fluía tráfico. Los runners bloqueaban la cola y yo no tenía señal de qué conexiones estaban sanas y cuáles estaban atascadas.
Mi rol
Construí un watchdog standalone que distingue idle-pero-sano de stalled-de-verdad en conexiones TCP long-poll y cosecha las stalled antes de que bloqueen la cola.
Lo que hice
01 Parsing capability-aware del estado del socket
Por qué: Idle y stalled se ven similares a nivel de syscall. La diferencia está en las opciones del socket y el estado del timer, no en el byte count. Leer los syscalls correctos distingue los dos casos.
Trade-off: La complejidad del parser es mayor que la de un timeout naive por byte count. La claridad diagnóstica gana porque los falsos positivos cuestan minutos reales de CI.
02 Deploy atómico con hook de rollback
Por qué: Si el watchdog mismo falla su propio probe, el binario previo se queda en su lugar. Un watchdog que se atasca en su propio startup es el peor failure mode; el hook de rollback lo previene.
Trade-off: Más ceremonia de deploy que un swap de binario. Más seguro que un watchdog stuck-on-failure, que es el failure mode que estoy tratando de prevenir.
03 Integración con self-hosted CI
Por qué: Los runners son los que mantienen los sockets long-poll abiertos. Integrar directamente con el ciclo de vida del GitHub Actions runner significa que el reaper ve el mismo estado de conexión que ve el runner.
Trade-off: Configuración específica del runner. No generaliza a todos los providers de CI — por diseño, porque los runners son el caso de uso.
Lo que cambió
Detección de stall del runner
Antes: Invisible — descubierto solo cuando la cola se bloqueaba
Después: Menos de 60 segundos desde stall hasta alerta más reaper
Tasa de reap por falso positivo
Antes: Alrededor de 5% con un timeout naive
Después: Menos de 0.5% con parsing capability-aware
Evidencia: estimado, no medido
Incidentes de deploy (watchdog stuck)
Antes: Alrededor de 1 por mes
Después: 0 en 2026 (el hook de rollback funciona)
Trade-offs
Renuncié a generalidad a cambio de runner-specificity. Los runners son el caso de uso; otros providers de CI no necesitan este reaper, así que generalizar sería trabajo sin payoff.
Lo que aprendí
La diferencia entre idle y stalled vive en los metadatos del syscall, no en el byte count. Los syscalls correctos para leer son los que distinguen intent de inactividad.
Stack
- Go
- TCP
- watchdog
- GitHub Actions