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
{{ message }}
Repository navigation
CI: Test Core stalls mid-suite with frozen log output — three occurrences in one day, each costing a manual diagnosis + rerun #4250
mongodb-driver.test.ts 的文件头注释把意图写得很清楚:"In sandboxed / offline CI that download can fail; when it does we skip this suite rather than failing the whole package's test run (and, with it, the monorepo Test Core job)."
Activity
ci: convert Test Core mid-suite stalls into labeled fast failures (#4250
ci: roll out run-with-stall-guard beyond Test Core (#4314) (#4316)
os-zhuang commentedon Jul 31, 2026
根因排查综合分析(2026-07-31)
止损已全部落地(#4309 Test Core 看门狗、#4316 铺至 Dogfood/nightly),这里是对根因的系统排查。结论先行:"每用例新建引擎后句柄未释放"的假说与 objectql 单测的实际情况不符;真正可疑的机制是 vitest fork worker 里的 kernel 信号处理器 + 资源竞争,且本机无法复现停摆 —— 所以 #4341 把取证自动化装进了看门狗,让下一次真实停摆自己交出栈。
排除项(逐一验证过)
metadata-validation-sweep根本不建真引擎(mock engine);protocol-unknown-query-param的内联 memory driver 的 connect/disconnect 是纯 no-op。裸new ObjectQL()不持有任何事件循环句柄(lifecycle 扫描器只在 plugin 路径启动,且定时器全部unref并有stop();logger 是无线程的 console/stream 写)发现项(数字都来自实测)
ObjectKernelConfig.gracefulShutdown默认 true,plugin.integration/plugin.step2的每个用例都在 fork worker 里注册 SIGINT/SIGTERM/SIGQUIT 处理器且从不 shutdown。vitest 默认 isolate 模式每跑完一个文件就 SIGTERM 回收 worker —— 于是每次回收都在跑"多个 running kernel 并发异步优雅停机、先完成者process.exit截断其余"的竞赛(实测 47 次 graceful shutdown 有 3 次没跑完)。静态分析里每条路径最终都会 exit,25 次隔离循环也没复现 —— 不能断言这就是停摆根因,但它是站得离事故最近的机器,且对测试毫无价值。logLevel: 'silent'是个从未存在过的配置键(真实形状logger.level;CI typecheck 不覆盖这些测试文件,死键得以存活)。47 个 kernel 全程 info 级输出,占包日志近一半 —— 正是两次停摆日志顶部的"ObjectQL Engine Instance Created→Driver connected→initialization complete反复多次"。#4341 的策略:让下一次停摆自己交出证据
看门狗在杀进程前增加两步取证(已用合成停摆验证):
/proc双采样分类:冻结组内每个进程标注ON-CPU(同步自旋/GC 抖动)或idle(等待永不 settle 的东西)+ CPU 增量 + RSS;--report-on-signal启动;事件循环存活的 node 进程会写出诊断报告(精确 JS 栈 + libuv 句柄清单),摘要进 step 日志、全文上传 artifact;沉默即判词 —— 实测确认 report-on-signal 由事件循环服务,同步阻塞进程必然不出报告,配合 CPU 分类恰好把"await 型 vs 自旋型"切开。后续(按取证结果走)
maxForks、--concurrency)的配比;ObjectKernel的信号处理器对第二次信号目前只 warn 不退出,常规约定是"第一次优雅、第二次立即退出",值得单独提一笔。Generated by Claude Code
ci,test: a #4250 stall now names its own culprit; test kernels stop i…
test(driver-mongodb): a hung MongoDB binary download skips the suite …
os-zhuang commentedon Jul 31, 2026
第四、五次:同一分支上确定性复现,但机制与前三次不同
守卫说明写着"同一文件停摆两次就补记到这里",照做。不过这两次的根因取到了,和已记录的三次不是一回事,所以分开写。
::endgroup::(cli 套件 628 测试跑完之后)@objectstack/driver-mongodb#test@objectstack/driver-mongodb#test同一 commit(
7d4313e)、同一停摆点、连续两次 —— 不是随机的。根因:一个没有上界的网络等待,而
catch接不住"卡住"driver-mongodb的三个套件用mongodb-memory-server起真 mongod,它会在模块加载期从fastdl.mongodb.org抓一个 ~123 MB 的二进制。三处都是同构的顶层 await:mongodb-driver.test.ts的文件头注释把意图写得很清楚:"In sandboxed / offline CI that download can fail; when it does we skip this suite rather than failing the whole package's test run (and, with it, the monorepoTest Corejob)."但 try/catch 只能表达"下载失败",表达不了"下载卡住"。 卡住时 promise 永不 settle,套件既不跑也不跳,就地停住——正是本 issue 描述的现象。取证:两次 attempt 的日志里,这个包一行测试输出都没有,也没有那句 skip 警告,说明
create()既没 resolve 也没 reject。另外
instance.launchTimeout: 60_000不管用:它约束的是二进制已在磁盘上之后 spawn mongod 的阶段,比抓取晚一步。不是某个 diff 的锅,是缓存未命中就会撞上
同一个任务在 main(run 30608331534,06:05:21Z)是
cache miss, executing真跑的,日志里有完整的Downloading MongoDB "8.2.6": 0% → 98.2% → 100% (122.6mb),通过。40 分钟后同样的抓取在 PR runner 上一个字节的进度都没打印就永久挂住。也就是说:网络侧在变,而等待没有上界。任何改动
@objectstack/spec的 PR 都会让全仓缓存失效(Cached: 3 cached, 129 total)、让这个任务真跑,于是必然撞上——不能靠重跑绕过去。已在 #4322 里修掉(
354caba)新增
packages/plugins/driver-mongodb/src/test-mongod.ts:给这个等待加 120s 截止时间,让"卡住"落进套件本来就有的 skip 分支。降级结果和可见性都不变(warn + 跳过套件),只是从"永远不发生"变成"有界发生";二进制可达时(开发机、以及 CI 下载正常时)真实覆盖一点没少。验证:成功路径本地 160/160 照过;用 mock 一个永不 settle 的
create()确认到点返回undefined而非挂死;helper 不会进 dist(tsup 入口只有src/index.ts,不引用它,而mongodb-memory-server是 devDependency)。与前三次的关系
前三次停在
metadata-validation-sweep.test.ts/protocol-unknown-query-param.test.ts,本 issue 的怀疑方向是 ObjectQL 引擎用例的句柄泄漏——那条线索本次没有任何新证据,依然开着。这两次只是同一个症状(Test Core 停摆)的另一个来源,修掉之后不影响原方向的排查。顺带一提,守卫本身这次表现完全符合预期:两次都在 10 分钟静默后给出带包名的明确红,让根因几分钟内就定位了——如果还是老的"挂到 job timeout",这次诊断会贵得多。
Generated by Claude Code
fix(spec,rest,runtime): request bodies are checked against the schema…
xuyushun441-sys commentedon Aug 3, 2026
认领(PM 分派)
session_018iARDqtrhQgz6fVHDeDkbQclaude/issue-4250-test-core-stalldomain:devx(落点.github/workflows/及相关脚本)发版期 CI 稳定性直接影响合并队列吞吐 —— 本会话今天已合入 8 个 PR,队列是串行的,一次 stall 就要人工重跑。
派发时的约束:仓内已有若干 stall guard 相关的 changeset(
test-core-stall-guard、stall-guard-rollout、temporal-conformance-stall-guard、stall-forensics-and-kernel-test-hygiene),先读它们,确认本单是不是已被部分解决 —— 今天 #4832 就是派下去才发现前提已失效。若已解决,报告实情并建议关单,不要硬造 PR。Generated by Claude Code
xuyushun441-sys commentedon Aug 3, 2026
审核通过 — PR #4960 已取消草稿并挂上 auto-merge(SQUASH);本单保持 OPEN,不关
前提核实的结论是"症状已被覆盖,但覆盖物本身有个从未暴露的真缺陷" —— 比"关单"或"照做"都有价值:
gracefulShutdown的 SIGTERM handler —— 最可能泄漏 worker 的那类 stall,恰好是 guard 存在的理由。已修:等进程组清空、SIGKILL 幸存者并点名(有 t=15s 后代存活的复现)。--self-test造四种合成挂死(idle / sync-spin / 零输出 / trap-SIGTERM 后代),21 条断言;更要紧的是 6 组 mutation 共 28 条判红证明它有判别力 —— 其中两组 mutation 反过来抓出 self-test 自己的两个 bug(包括清扫例程把它要断言的进程泄漏先杀掉了)。一个从未红过的检查与不存在的检查无法区分,这条已经是本会话的第八次了。process.exit()截断了自己的 stdout),撤回并说明。如实撤回记入加分。为什么不关单:三次原始 stall 的 root cause 仍未知;#4341 的策略("让下一次真 stall 交出自己的栈")仍是活方案 —— 本 PR 让那个策略变得可信(guard 确定还活着、真的会触发、不会留孤儿),但没有终结它。等下一次真实 stall 带着完整取证出现,再决定关单或转为具体缺陷单。
共享文件:
lint.yml一步、package.json一行。错峰已解除(#4948 先行落地),队列合并时若同 hunk 冲突会被踢回,届时按并集重解。Generated by Claude Code
Merge origin/main into claude/issue-4001-ui-batch14
ci: 让 stall guard 自证仍会触发,并修复停摆时从未真正执行的 SIGKILL 升级 (objectstack-ai#4250)…
ci(dogfood): the sharded gate builds its closure in a passthrough-fre…