Repository navigation
Description
Problem
Reported on a real site (inline edit / edit view, pages with tables and blockquotes): the server stopped answering every URL, logged nothing, and only a restart cleared it. Not reproduced on a clean copy, so the trigger is still unknown.
That symptom means the event loop is blocked (a synchronous loop, e.g. catastrophic regex backtracking or a conversion that never terminates) or wedged. Deno runs all requests on one thread, so one bad request takes the site down for everybody, and a blocked thread can't log, time out, or shut down gracefully. Fixing this one bug won't prevent the next one; the failure mode needs containment.
Related, from the earlier dune ps work (#37): a machine ended up with ~45 stale dune processes and dune ps could not tell a healthy server from a stuck one. It finds processes by parsing command lines and lsof, which is heuristic and says nothing about whether a process is responsive.
What we know (spikes)
Deno.exit()called from a Worker only ends the Worker; a main thread spinning in a loop keeps running (process stayed at ~97% CPU). Killing a stuck process from inside needsSIGKILL.- A Worker cannot be unref'd and keeps the process alive until terminated, so every normal exit path must stop it explicitly (same class of bug as the idle
dune serveprocesses). - macOS
samplegives only native frames with no JS names, so it can't say which request hung. In-flight request tracking can.
Plan (stacked PRs)
- Watchdog (done, branch
feat/event-loop-watchdog): a Worker expects a main-thread heartbeat; after 15 s (DUNE_WATCHDOG_TIMEOUT_MS) of silence it writes a report to<site>/.dune/hangs/(in-flight requests, plus a native stack sample on macOS) and SIGKILLs the process so a supervisor (systemdRestart=, thedune devwrapper) sees a dead process.DUNE_WATCHDOG=warn|off. The main thread also logs when its own timer ran over a second late. Applies todune devanddune serve. - Instance registry:
serve/devwrite~/.dune/instances/<pid>.json(pid, process start time, mode, root, title, port, version) on listen and remove it on exit; stale entries are verified by pid + start time. The watchdog can mark an instancehungthere, since its Worker survives a blocked main thread. dune psreads the registry (keeping the current command-line heuristics as a fallback for older versions and orphans) and shows listening / not listening / hung. Rebased on a trimmed feat(cli): dune ps finds the CLI and task wrappers and says "not listening"; new dune stop #37 (which lands first as the base); stop/idle/SIGKILL semantics to be settled there (SIGKILL as opt-in--force).
Later, decided after the original hang is root-caused: Worker isolation with terminate() + timeout for CPU work on user-controlled input (markdown/MDX compile, editor markdown/document conversion, TSX).
Out of scope / known limits
- Cannot make a hang non-fatal in-process; this is detection + recovery.
- No Linux stack sample yet (the report says so and names
eu-stack/gdb). - The original hang's root cause is still open. Next time it happens, before restarting: record CPU % of the wrapper and child (
dune ps,ps -o pcpu) and runsample <pid> 5.
Acceptance
- A stuck main thread is killed within the configured timeout and leaves a report naming the in-flight requests.
- Graceful
serveshutdown is not delayed by the watchdog. dune pscan tell a hung instance from a healthy one without parsing command lines.
Activity
zumbrunn commentedon Oct 26, 2026
Related: #46 (
dune psrework, PR #37), #47 (servelingering after shutdown, PR #38). Thefeat/event-loop-watchdogbranch (PR 1 of the plan) will be linked here when opened.zumbrunn commentedon Oct 26, 2026
PR 1 of the plan (watchdog) is open: #51.
zumbrunn commentedon Oct 26, 2026
PR 2 (instance registry) is open as a stack on #51: see the PR for a small plan change (hung detection via a
/health/liveprobe indune psrather than a registry marker).zumbrunn commentedon Oct 26, 2026
PR 3 (dune ps probes health + reads the registry) is up as a draft stacked on #37/#51/#52: see the PR.
fix(runtime): watchdog no longer kills what is merely paused or legit…
fix(cli): dune ps probes more carefully
zumbrunn commentedon Oct 26, 2026
Review round done on the stack (#51, #52, #37, #53): watchdog now survives sleep/SIGSTOP, has a 2-minute startup limit,
watchdogProgress()/watchdogGrace(), and warns instead of killing under an inspector;dune stoponly acts on confirmed instances, only on your own processes, re-checks start times before every signal and leaves young instances alone under--idle; the health probe needs three failures and targets the registered port. Bodies refreshed on each PR; watchdog docs: duneorg/dune-docs (new PR).docs(deployment): tell sites to ignore .dune/ (hang reports land there)
feat(runtime): event-loop watchdog for dev and serve (#51)
feat(runtime): instance registry written by dev and serve (#52)
feat(cli): dune ps probes health and reads the instance registry (#53)
docs(deployment): the hang watchdog (#13)
zumbrunn commentedon Oct 27, 2026
Shipped in 0.35.0 (watchdog, instance registry, hang-aware
dune ps,dune stop). Docs are live in dune-docs: duneorg/dune-docs#13