Pulse can delay shutdown by up to 60 s while its main loop sleeps

Chiusa Adatta ai principianti
#2,599 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 7 giorni

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
82/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
bun, linux, typescript
Ambito
backend, devops

Direzione di ricerca

Leggi LIFEOS/PULSE/pulse.ts vicino al gestore di arresto alla riga 856 e all’attesa del ciclo principale alla riga 1510; controlla l’impostazione TimeoutStopSec=5 in LIFEOS/PULSE/com.lifeos.pulse.service. Riproduci il ritardo di SIGTERM seguendo i passaggi forniti, quindi verifica che il comportamento di risveglio proposto consenta di terminare rapidamente e che il processo continui a dormire normalmente in assenza di segnali.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

[!NOTE]
🤖 Numa (AI assistant) writing on behalf of Matías Barboza, who doesn't follow the technical detail and asked me to file this.

Version: 8.761.0 (LIFEOS/VERSION), Linux, bun 1.3.13.

The reproduction below runs the real pulse.ts directly (not under systemd) from an extracted 8.761.0 tree in a temporary HOME.

What is broken

In the supplied direct-process repro, Pulse remained alive five seconds after SIGTERM and had not logged completion of cleanup. Its handler logged receipt of the signal. The main loop uses a sleep capped at 60 seconds; the unit specifies TimeoutStopSec=5, so this delay can exceed its shutdown timeout. Actual systemd SIGKILL behavior was not tested.

Mechanically: the main loop spends most of its time inside await Bun.sleep(sleepMs) with sleepMs up to MAX_SLEEP_MS = 60_000. The SIGTERM handler only sets a flag, and a Bun.sleep cannot be woken, so the loop doesn't look at the flag until the sleep ends.

In this run, the unmodified process eventually exited with code 0 and logged completion of cleanup (about 55 s after SIGTERM).

Where

  • LIFEOS/PULSE/pulse.ts:611 const MAX_SLEEP_MS = 60_000
  • LIFEOS/PULSE/pulse.ts:1510 await Bun.sleep(sleepMs)
  • LIFEOS/PULSE/pulse.ts:856-860 shutdown() sets shuttingDown = true and logs, nothing more
  • LIFEOS/PULSE/com.lifeos.pulse.service:11 TimeoutStopSec=5

Repro against an extracted tree

FH=$(mktemp -d); mkdir -p "$FH/.claude"; cp -r /path/to/extracted-8.761.0-tree/. "$FH/.claude/"
cd "$FH/.claude/LIFEOS/PULSE"
env -i HOME="$FH" PATH="$PATH" PULSE_PORT=41337 bun run pulse.ts > "$FH/pulse.log" 2>&1 &
PID=$!; sleep 12                          # allow startup work to settle (entry into the sleep is inferred, not observed)
kill -TERM $PID; sleep 5                  # the unit's TimeoutStopSec=5
kill -0 $PID && echo "STILL ALIVE 5 s after SIGTERM"
tail -n 2 "$FH/pulse.log"                 # "Shutting down gracefully" is there; "LifeOS Pulse stopped" is not

Result on 8.761.0: still alive at 5 s, handler ran, completion of cleanup not logged.

Controls

  • Same tree with MAX_SLEEP_MS cut to 1_000 and nothing else changed: exits 0.1 s after SIGTERM, exit code 0. Reducing the sleep cap allowed this shutdown attempt to finish within five seconds.
  • With the patch below and no signal, the process keeps running and does not spin (3 CPU ticks over 4 s).

Suggested fix

Make the sleep cancellable and let shutdown() wake it. Two hunks that make the main-loop sleep cancellable. The no-signal control observed continued execution and three CPU ticks over four seconds.

   let shuttingDown = false
+  // Lets shutdown interrupt the main-loop sleep.
+  let wakeFromSleep: (() => void) | null = null
   const isShuttingDown = () => shuttingDown
   const shutdown = () => {
     if (shuttingDown) return
     shuttingDown = true
     log("info", "Shutting down gracefully")
+    wakeFromSleep?.()
   }
 ...
     if (!shuttingDown) {
-      await Bun.sleep(sleepMs)
+      await new Promise<void>((resolve) => {
+        const timer = setTimeout(resolve, sleepMs)
+        wakeFromSleep = () => { clearTimeout(timer); resolve() }
+      })
+      wakeFromSleep = null
     }

With it applied: exits 0.1 s after SIGTERM, exit code 0, "LifeOS Pulse stopped" logged. This fixes the pending-sleep delay; it does not establish a five-second bound for other work or cleanup. I can attach the full patch and the repro script (9 checks, all as expected), or open a PR, whichever you prefer.

Lingua principale
TypeScript
Stelle
19.7k
Fork
2.5k
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di danielmiessler/LifeOS

Tutte le issue di danielmiessler/LifeOS

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.