Skip to content

ci,test: a #4250 stall now names its own culprit; objectql test kernels stop intercepting worker SIGTERM - #4341

Merged
os-zhuang merged 1 commit into
mainfrom
claude/test-core-mid-suite-stalls-unjeh9
Jul 31, 2026
Merged

ci,test: a #4250 stall now names its own culprit; objectql test kernels stop intercepting worker SIGTERM#4341
os-zhuang merged 1 commit into
mainfrom
claude/test-core-mid-suite-stalls-unjeh9

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#4250 根因排查的落地 PR。完整分析随后贴在 issue 上;这里是结论对应的改动。

排查结论(细节见 #4250 评论)

  1. "句柄泄漏拖住收尾"的假说与 objectql 单测不符:两个停摆点用例的内联 driver 是纯内存 no-op、裸 ObjectQL 引擎不持有事件循环句柄、lifecycle 定时器全部 unref、logger 无线程。
  2. 真正站在事故半径里的机制:一次 objectql 套件运行会 boot 47 个 kernel,其 gracefulShutdown 默认值在 vitest fork worker 里注册 SIGINT/SIGTERM/SIGQUIT 处理器 —— 实测每轮 48 次 SIGTERM 截获,每次 worker 回收都在跑多 kernel 并发异步停机竞赛(47 次 graceful shutdown 有 3 次没跑完,被别的 kernel 的 process.exit 截断)。理论上每条路径最终都会 exit,所以不能断言这就是 CI: Test Core stalls mid-suite with frozen log output — three occurrences in one day, each costing a manual diagnosis + rerun #4250;但它是离事故最近的机器,且对测试毫无价值。
  3. 静音是假的:这些 kernel 传的 logLevel: 'silent'ObjectKernelConfig 上从未存在过(真实形状是 logger.level;CI 的 typecheck 只覆盖 spec 和 example apps,测试文件不在内,所以死键一直活着)。47 个 kernel 全程 info 级输出 —— 正是两次停摆日志顶部反复出现的"引擎初始化序列"。
  4. 单独循环 25 次、全量套件多次,本机未复现停摆 —— 冻结需要 CI 那种资源竞争环境,所以本 PR 的重心是让下一次真实停摆自动取证

改动

plugin.integration.test.ts / plugin.step2.test.ts(objectql)

  • kernel 改用真实静音键 logger: { level: 'silent' } + gracefulShutdown: false,并在 afterEach shutdown 自己 boot 的 kernel。
  • 实测:kernel bootstrap 日志 47 → 0,worker SIGTERM 截获 48 → 0,包日志 4579 → 2096 行,87 文件 / 1374 用例全绿。

run-with-stall-guard.mjs 停摆取证(杀进程之前)

  • 对冻结进程组做两次 /proc 采样(2 秒间隔),逐进程分类:ON-CPU(同步自旋 / GC 抖动)vs idle(等待永不 settle 的东西);
  • 配合 --report-dir:向组内 node 进程发 SIGUSR2,事件循环存活者会写出诊断报告(精确 JS 栈 + libuv 句柄),摘要进日志;不出报告本身就是判词(已实测验证:report-on-signal 由事件循环服务,同步阻塞进程必然沉默)——配合 CPU 分类即可定位冻结者与冻结类型;
  • 取证仅限 Linux /proc、全程 try/catch、最多让 kill 晚 ~6 秒,绝不影响杀进程路径。

ci.yml

  • Test Core(PR/push)与 Dogfood 两 shard 的测试 step 通过 NODE_OPTIONS=--report-on-signal ... 武装全部 node 进程,guard 传 --report-dir;
  • 失败时上传完整诊断报告为 artifact(保留 14 天)。

验证

  • 合成停摆实测(一个 node 自旋 + 一个 node 空转):自旋者 [R->R] cpuΔ=200 被判 ON-CPU 且无报告、空转者 [S->S] cpuΔ=0 出报告,分类与收割全部正确;
  • 三条基础路径(正常退出/红套件透传/停摆 exit 75)回归通过;
  • ci.yml YAML 解析通过;objectql 全量套件两次全绿。

效果

下一次 #4250 停摆的 step 日志会直接写明:哪个进程冻结、哪种冻结模式、(await 型)冻在哪个栈上 —— 从"要人工判读的谜题"变成"自带答案的红"。

ref #4250#4309#4316。不关闭 #4250 —— 等下一次停摆的取证数据落地后再收口。

🤖 Generated with Claude Code

https://claude.ai/code/session_014NNaWXVEyG6Pv4EFRfYGg6


Generated by Claude Code

…ntercepting SIGTERM (#4250)

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.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014NNaWXVEyG6Pv4EFRfYGg6
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 31, 2026 6:54am

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation ci/cd tests tooling and removed size/m labels Jul 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

@os-zhuang
os-zhuang marked this pull request as ready for review July 31, 2026 07:03
@os-zhuang
os-zhuang merged commit fc3bd1c into main Jul 31, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/test-core-mid-suite-stalls-unjeh9 branch July 31, 2026 07:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd documentation Improvements or additions to documentation tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants