问题
build job(CI 的必需检查,没有 continue-on-error 兜底)里有两条真 spawn worker 的重并发测试会偶发失败,一抖就挡住所有 PR:
test/mojo-cross-boundary.test.ts > 1. concurrently queued credential turns run A/B/C, not A/C/C
AssertionError: expected [ '[A]', '[C]', '[C]' ] to deeply equal [ '[A]', '[B]', '[C]' ]
test/mojo-close-worker-journal.integration.test.ts > keeps writes fenced when recovery says retryable but admission says fenced
两条都是 13–16s 量级、bootWorker() 真起 worker 子进程 + IPC,断言依赖执行顺序而非确定性同步点。
证据
在 PR #1096 上观察到:同一个 SHA(78f673674,一字未改)两次 CI 结果相反 —— 第一次 build=failure(上面两条),重跑 build=success。⟹ 抖动,不是回归。
本地补充:
- 单独跑 5/5 全绿;造并发负载(并行 6 个 vitest 进程后立刻跑)14/14 仍全绿 —— 本地压不出来,只在 CI runner 的真实调度抖动下触发
- master 侧最近 8 个
build job 全 success 且这两条都过 ⟹ 概率上界大约 <1/8,但确实存在
为什么值得修
红起来与真回归完全同形。 #1096 里为了排除它,两个 reviewer 各花了一轮:先怀疑是新并进来的 #1057 动了 mojo spawn 时序(BOTMUX_REPLY_STYLE 注入落在 riff/mojo 的 env 分支上,看着很像),最后靠
git diff <before>..<after> -- src/worker.ts | grep -cE "applyMojoLivePatch|takeNormal|pendingMessages" # => 0
证明 A/C/C 的守卫代码(worker.ts drain 循环里的 applyMojoLivePatch(item.mojoLivePatch))两侧逐字相同、且与 spawn 期 env 注入相位分离,才排除掉。这个排查成本本身就是稳定化的理由。
建议
mojo-cross-boundary 的 A/C/C:断言的是凭证 turn 排队顺序(修法本身在 drain 循环里、注释已说明 IPC-receive 时机会把 A/B/C 塌成 A/C/C)。建议加确定性同步点(等每个 turn 落地再发下一个的可观测信号),而不是靠 waitFor(lines >= 3) 后比较顺序
mojo-close-worker-journal:admission/recovery 竞态,同理
- 若短期不便改造,至少把这两条从
build 门禁移到 advisory 腿,或标 retry —— 一个因环境而红的门禁会训练人忽略它(ci.yml 里 bun-test 的注释已经把这个道理写清楚了)
上下文
发现于 #1096 的评审收尾。该 PR 已合入(3b818ff69),本 issue 只记录这两条测试的稳定化,与 #1096 的改动无关。
问题
buildjob(CI 的必需检查,没有continue-on-error兜底)里有两条真 spawn worker 的重并发测试会偶发失败,一抖就挡住所有 PR:test/mojo-cross-boundary.test.ts > 1. concurrently queued credential turns run A/B/C, not A/C/Ctest/mojo-close-worker-journal.integration.test.ts > keeps writes fenced when recovery says retryable but admission says fenced两条都是 13–16s 量级、
bootWorker()真起 worker 子进程 + IPC,断言依赖执行顺序而非确定性同步点。证据
在 PR #1096 上观察到:同一个 SHA(
78f673674,一字未改)两次 CI 结果相反 —— 第一次build=failure(上面两条),重跑build=success。⟹ 抖动,不是回归。本地补充:
buildjob 全 success 且这两条都过 ⟹ 概率上界大约 <1/8,但确实存在为什么值得修
红起来与真回归完全同形。 #1096 里为了排除它,两个 reviewer 各花了一轮:先怀疑是新并进来的 #1057 动了 mojo spawn 时序(
BOTMUX_REPLY_STYLE注入落在 riff/mojo 的 env 分支上,看着很像),最后靠证明
A/C/C的守卫代码(worker.tsdrain 循环里的applyMojoLivePatch(item.mojoLivePatch))两侧逐字相同、且与 spawn 期 env 注入相位分离,才排除掉。这个排查成本本身就是稳定化的理由。建议
mojo-cross-boundary的 A/C/C:断言的是凭证 turn 排队顺序(修法本身在 drain 循环里、注释已说明 IPC-receive 时机会把 A/B/C 塌成 A/C/C)。建议加确定性同步点(等每个 turn 落地再发下一个的可观测信号),而不是靠waitFor(lines >= 3)后比较顺序mojo-close-worker-journal:admission/recovery 竞态,同理build门禁移到 advisory 腿,或标 retry —— 一个因环境而红的门禁会训练人忽略它(ci.yml里bun-test的注释已经把这个道理写清楚了)上下文
发现于 #1096 的评审收尾。该 PR 已合入(
3b818ff69),本 issue 只记录这两条测试的稳定化,与 #1096 的改动无关。