fix(launcher): supervisar agentes y reiniciarlos cuando salen sin cancelacion
El launcher salia con status=0 cuando todos los runners (Agent/Robot) terminaban su Run() de forma natural — por ejemplo tras una rotacion de token de Matrix o un drop del sync. systemd, configurado con Restart=on-failure, no relanzaba el proceso al ver salida limpia y los bots quedaban caidos hasta una intervencion manual. Solucion: nueva rutina superviseUntilCanceled en agentRegistry que bloquea sobre waitAll, y si el ctx padre sigue vivo, espera un backoff y llama reloadAll para recrear los runners. Solo cuando el ctx padre se cancela (SIGINT/SIGTERM) la rutina retorna y el launcher sale. main.go pasa a invocar este supervisor en lugar de waitAll directo. Tests: - TestSuperviseUntilCanceled_ReturnsWhenCtxCanceledFirst — empty registry - TestSuperviseUntilCanceled_ReturnsAfterCtxCancelDuringBackoff — cancel durante el backoff debe desbloquear inmediatamente - TestSuperviseUntilCanceled_CallsReloadOnAgentExit — supervisor sigue vivo todo el deadline aunque reload falle por cfgPath invalido Diagnostico: tras varias horas el journalctl mostraba "Deactivated successfully" sin "Stopping" previo (Apr 13 18:22 tras 23h corriendo) y el log del agent registraba "context canceled" tras "starting matrix sync" — sintoma de que mautrix.SyncWithContext salio limpiamente y el ctx.cancel se propago al cerrar la goroutine sin que systemd hubiera enviado SIGTERM. El bucle supervisado lo arregla recreando los runners sin tocar la unit ni depender del Restart de systemd.
This commit is contained in:
@@ -230,6 +230,35 @@ func (r *agentRegistry) waitAll() {
|
||||
}
|
||||
}
|
||||
|
||||
// superviseUntilCanceled blocks until ctx is canceled, restarting agents
|
||||
// (via reloadAll) every time waitAll returns while the parent ctx is alive.
|
||||
// Each restart waits restartBackoff before recreating runners. Used by the
|
||||
// launcher main loop so the process keeps the agents up across token rotation
|
||||
// or sync drops without exiting cleanly to systemd.
|
||||
func (r *agentRegistry) superviseUntilCanceled(
|
||||
ctx context.Context,
|
||||
restartBackoff time.Duration,
|
||||
rulesFor func(string, *slog.Logger) []decision.Rule,
|
||||
logger *slog.Logger,
|
||||
) {
|
||||
for {
|
||||
r.waitAll()
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
if logger != nil {
|
||||
logger.Warn("all agents stopped while launcher active — restarting after backoff",
|
||||
"backoff", restartBackoff.String())
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(restartBackoff):
|
||||
}
|
||||
r.reloadAll(rulesFor)
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupLogs calls every agent's log cleanup function (called on launcher shutdown).
|
||||
func (r *agentRegistry) cleanupLogs() {
|
||||
r.mu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user