You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ci,test: a #4250 stall now names its own culprit; test kernels stop intercepting SIGTERM (#4250) (#4341)
Investigation results behind this change (full analysis on #4250):
- Both stall-point files sit in packages/objectql, but neither the
in-test drivers (pure in-memory no-ops) nor bare ObjectQL engines hold
event-loop handles, lifecycle sweep timers are unref'd, and the logger
is threadless -- the issue's unreleased-handle hypothesis does not fit
this package's unit suites.
- What DID show up: one objectql run booted 47 kernels whose
gracefulShutdown default installed SIGINT/SIGTERM/SIGQUIT handlers
inside vitest fork workers -- 48 SIGTERM interceptions per run, every
worker recycle running a multi-kernel async shutdown race (3 of 47
graceful shutdowns never completed, cut short by another kernel's
process.exit). Every path does eventually exit, so this is not proven
to BE #4250 -- but it is the machinery standing closest to it, and it
serves no test purpose.
- Those same kernels declared `logLevel: 'silent'` -- a config key
ObjectKernelConfig never had (the real shape is `logger.level`; CI
type-checks spec and examples, not these tests, which is why the dead
key survived). All 47 boots logged at info: ~half the package's log
volume, the exact "engine init sequences" frozen at the top of both
#4250 logs.
Changes:
- plugin.integration.test.ts / plugin.step2.test.ts: kernels boot with
the real silence key (`logger: { level: 'silent' }`) and
gracefulShutdown:false, and afterEach shuts down what it booted.
Verified: kernel bootstrap log lines in a full run drop 47 -> 0,
worker SIGTERM interceptions 48 -> 0, package log 4579 -> 2096 lines,
87 files / 1374 tests stay green.
- run-with-stall-guard.mjs: on a declared stall, before killing, it now
(1) samples every process in the frozen group twice via /proc and
classifies each as ON-CPU (sync spin / GC thrash) or idle (awaiting
something that never settles), and (2) with --report-dir set, sends
SIGUSR2 so every node process whose event loop is alive dumps a
diagnostic report -- digested into the log as an exact JS stack; a
process that produces NO report is named as loop-blocked (verified
behavior: node's report-on-signal is served by the event loop).
Forensics are Linux-/proc best-effort, add at most ~6s before the
kill, and never fail the kill path.
- ci.yml: Test Core and Dogfood test steps arm the harvest via
NODE_OPTIONS (--report-on-signal) and pass --report-dir; on failure
the full reports upload as artifacts (14-day retention).
The next real stall stops being a mystery: the step log will say which
process froze, in which mode, and -- for the await-forever class -- on
exactly which stack.
Claude-Session: https://claude.ai/code/session_014NNaWXVEyG6Pv4EFRfYGg6
Co-authored-by: Claude <noreply@anthropic.com>
` -- pid ${pid} (${file}): ${js.message||'event loop responsive, no active JS frame -- awaiting something that never settles; check the report\'s libuv handles'}`,
0 commit comments