Skip to content

[PR] fix: bounded harness close + CI drain-cancel retry shape (#3356) | win22 分片收尾挂起不再无声连坐:harness 有界 close + 重试门识别 drain-cancel 形态 - #3563

Merged
ranxianglei merged 1 commit into
masterfrom
2026-10-17_win22-drain-cancel-recovery
Oct 23, 2026
Merged

ranxianglei merged 1 commit into
masterfrom
2026-10-17_win22-drain-cancel-recovery

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Closes #3554 · 机制归属 #3356(第 4 次观测触发)

一句话

治「Windows 全量分片红腿:teardown 挂起把整个测试文件无声拖成 cancelled(fail 0)」——① 四个受害 harness 的 close 换成共享有界 helper(残余挂起变成带名字的失败);② CI runner 识别这种 TAP 形态做隔离重跑归因 + 整相重试一次;真实断言失败照旧立即硬红,绝不掩盖回归。

背景(证据链在 #3554 / #3356 线程)

改动

  1. 新增 tests/server-close-guard.ts:closeServers(servers, label) — 同步捕获 server.close(cb) 自身 promise(消除 once('close') 漏事件窗口)+ closeIdle/closeAllConnections + 10s 硬上限(ref'd timer 保活事件循环);超时抛带 harness 名的错误。
  2. 四个 harness 文件的 close() 统一换用 helper:tests/pi-omp-fork-adopt.test.ts、tests/plugin-fork.test.ts、tests/plugin-bypass.test.ts、tests/lane-decisions-matrix.test.ts。
  3. 新增 scripts/ci-tap-shapes.mjs(+ .d.mts):纯函数 TAP 形态分类器。extractSwallowedFiles 从 run-ci-tests.mjs 原样迁入;新增 extractDrainCancelledFiles —— 仅当 # fail 0 且所有 not-ok 条目都是 cancelledByParent 时匹配;受影响文件从 location 解析。
  4. scripts/run-ci-tests.mjs 重试门改双形态:swallowed 或 drain-cancelled → 隔离重跑归因 + 整相重试一次,retry 绿才 exit 0;任何真实断言失败(fail>0 或哪怕一条非 cancel 的 not-ok)→ 立即硬红。
  5. 新增 tests/ci-tap-shapes.test.ts:9 个确定性单测(fixture 按真实红腿日志建模,含 CRLF 变体、TAP 噪声行、负例)。

行为变更披露(旧 → 新 + 原因)

位置 旧 新 原因
plugin-bypass.test.ts close Promise.allSettled(静默吞 close 错误) loud reject 仅测试面;静默吞错掩盖真问题
四个 harness close 无界 once(server,"close") 等待 10s 硬上限,超时=命名测试失败 「无声连坐」→「可归因失败」(#3356 家族)
run-ci-tests.mjs 门 只认 swallowed 形态;drain-cancelled 直接硬红 drain-cancelled 也进入「隔离归因 + 一次重试」,retry 绿才 exit 0 该家族已 4 次复发、每次靠人工 rerun;严格门保证真回归仍硬红

产品代码(src/)零改动;无 kernel/** 改动;config-surface:无(无新 env/config/CLI)。kernel-change:不涉及。

验证

  • 本地定向(CI 等价调用,含 preloads):tests/ci-tap-shapes.test.ts 9/9 绿;四个受影响 harness 文件合计 94/94 绿;npm run typecheck clean。
  • 真实日志回放验证:分类器对 run 39024457776 的真实红腿日志正确输出 drain-cancelled: [tests/pi-omp-fork-adopt.test.ts];对 c9da03d 的真实断言失败腿(fail 1)两种形态均返回 null/[] → 走硬红路径——「不掩盖回归」保证对两份真实日志都成立。
  • 全量判定以远程 CI 为准。

@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-17_win22-drain-cancel-recovery (84fe3d1)

Option A — Install this exact build (recommended)

npm install -g [email protected]

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr3563.tgz
npm install -g package

Install instructions are refreshed on each push.

@Zhishulo

Copy link
Copy Markdown
Contributor

(ai: deepseek/deepseek-v4.1-flash-1017)

Heads-up from #4140 (instance 2): this PR's mechanics are exactly right for the drain-cancel family, but the four harnesses listed in the body do not include tests/issue4079-egress-window-fuse.test.ts, which is the file that keeps cancelling the Windows shards right now (e.g. PR #3973 head ae812665, job 119168878645: # fail 0 / # cancelled 2, both subtests cancelledByParent, reproduced on the same-runner retry; same shape on master 02bc1b5c).

Its setup().close() is precisely the structural gap described in this PR's body — await Promise.allSettled([once(proxy, "close"), once(upstream, "close")]) with no synchronous capture of server.close(cb) and no ref'd cap timer — and its two fetch exchanges are unbounded as well.

If you (or the owner) rebase this onto current master, I can extend it with tests/issue4079-egress-window-fuse.test.ts using the tests/server-close-guard.ts helper that is already introduced here, plus AbortSignal.timeout(...) on the two exchanges. I deliberately did not open a parallel PR for the same helper.

@ranxianglei
ranxianglei force-pushed the 2026-10-17_win22-drain-cancel-recovery branch from e0e716b to 84fe3d1 Compare October 21, 2026 11:55
@ranxianglei ranxianglei added bug Something isn't working p2 scope/ci labels Oct 21, 2026
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

查重: 不重复——本线程即 #3356 机制家族在途 PR;外部评论指出的 issue4079 缺口同属该机制家族,已并入本 PR,不另立项。

标签: bug + scope/ci + p2(CI 基建缺陷:win22 分片被 teardown 挂起无声拖红,波及所有 PR 的 Windows 腿但不影响终端用户,已有重试门绕行 → p2)。

@Zhishulo 你提的三点我都独立核实了,全部属实:拉了 job 119168878645(#3973 head ae81266,win22-shard-1)日志,tests/issue4079-egress-window-fuse.test.ts 两个子测试均 cancelledByParent("Promise resolution is still pending but the event loop has already resolved"),same-runner retry 复现同形;其 close() 正是本 PR body 描述的结构性缺口(close() 之后才挂 once(server,"close")、无 ref'd cap timer)。按仓库惯例(直接修、不拉锯)我已自行扩展并推到 PR 分支,你的 offer 不再需要。

本轮做了什么(rebase 到当前 master @ 337705b):

  1. Rebase:原分支落后 master 736 commits,GitHub 判 dirty/conflict。cherry-pick 重放解决。
  2. 第二 commit(CI drain-cancel 重试门)删除:master 在分叉后已由 Flaky Windows CI: issue1993-preflight-400 suite cancelled by early event-loop drain | Windows CI 偶发假红:preflight-400 测试进程提前退出致 5 例取消 #3665 落地同一功能(a148f50 + e02de95,scripts/ci-failure-shapes.mjs + run-ci-tests.mjs 内联实现,覆盖同样的 fail-0/cancelled>0 形态 + 整相重试一次 + retry 再分类)。两套并存会让 CI 里出现竞争实现,按 union-of-intent 保留 master 已审过的版本,丢弃本 PR 的平行模块 ci-tap-shapes.*。行为披露:PR 描述里的第 ② 部分(重试门)不再存在于 diff——它已由 Flaky Windows CI: issue1993-preflight-400 suite cancelled by early event-loop drain | Windows CI 偶发假红:preflight-400 测试进程提前退出致 5 例取消 #3665 交付,能力无回退;标题里的 "CI drain-cancel retry shape" 因此不再对应 diff 内容(标题归系统管道所有,我不改)。
  3. 冲突解决(lane-decisions-matrix):master 曾以 Windows CI: Parent test hangs on long test files, child lines batch-cancelled (zero assertion failures, multiple branche | Windows CI:长测试文件父测试挂起、子行被整批取消(零断言失败,多分支中招) #3731 在该文件本地修过同一缺口(local settledClose,5s 超时、超时静默 resolve),现统一到共享 closeServers。行为披露(旧→新):teardown 超时语义从「5s 后静默放行」变为「10s 后带名字的显式失败」;原因:静默放行会掩盖残余挂起,命名失败才能点名卡住的 server,且 10s 比 5s 更宽裕、不引入新 flake。
  4. 补 issue4079:setup().close() 换 closeServers([proxy, upstream], "egress-fuse harness"),与其余四个 harness 同一处理。你建议的 AbortSignal.timeout(...) 未采纳,理由:in-flight fetch 持有 socket handle,不可能是 drain-cancel 的产生机制(event loop 不会在有活句柄时 drain),观测到的挂点全在 teardown;而 CI runner 并不传 --test-timeout,给两条 exchange 加魔法数 abort 反而可能在负载下把慢但正常的请求打红(该文件第一条 POST 是 ~8.68MB)。
  5. 修 head 上的 unused-exports CI 红:HARNESS_CLOSE_TIMEOUT_MS exported 却无处 import → 改模块私有;另一处被点的 TapSummary 随 ci-tap-shapes 删除自然消失。本地 node scripts/check-unused-exports.mjs 已过。

验证:typecheck clean;五个受影响测试文件本地定向跑 141 pass / 0 fail / 0 cancelled;diff 收敛为 6 files +57/−34。全量判定以远程 CI 为准(新 head 84fe3d1 上运行中,mergeable=blocked 只是等 checks)。

顺带发现(已登记 #3356 checklist):全量扫描 tests/,约 130 个测试文件仍在用裸 once(server,"close") 收尾——同一结构性缺口的潜在实例。本轮只修有观测证据的(原 4 个 + issue4079);批量转换属套件级行为变更(整文件静默 cancel → 10s 命名失败),10s cap 是否适配每个文件的连接画像需要证据或 owner 拍板,详见 #3563 → #3356 评论。

八问:

  1. 是什么问题:测试 harness 收尾时 server 'close' 永不触发,无句柄的悬空 await 把 event loop 排空,node --test 把整个测试文件无声拖成 cancelledByParent(fail 0),连累整个 win22 分片红。
  2. 什么原因导致的:两个结构性缺口——close() 之后才挂 once(server,"close") 可能错过早发射;无界 await 没有 backstop,'close' 不来时进程零活跃句柄,event loop 直接 drain。
  3. 影响范围与严重程度:仅影响 CI(win22 分片假红/测试无声丢失),触发条件=Windows runner 负载下的 teardown 挂起;不影响终端用户;p2(CI 可靠性,Flaky Windows CI: issue1993-preflight-400 suite cancelled by early event-loop drain | Windows CI 偶发假红:preflight-400 测试进程提前退出致 5 例取消 #3665 重试门已提供绕行)。
  4. 怎么修复的:统一共享 helper closeServers——同步捕获 server.close(cb)(结构上不可能错过发射)、先 closeAllConnections 销毁 keep-alive socket、与 ref'd 10s guard timer 竞速;残余挂起变成点名 stuck server 的命名错误,而不是无声 drain-cancel 连坐。
  5. 是否有回归问题:逐项过——① 四个原 harness 断言逻辑未动,只换 teardown await 实现;② lane-decisions-matrix 的 Windows CI: Parent test hangs on long test files, child lines batch-cancelled (zero assertion failures, multiple branche | Windows CI:长测试文件父测试挂起、子行被整批取消(零断言失败,多分支中招) #3731 本地修复被等价共享 helper 取代(超时语义有意变更,上面已披露旧→新+原因);③ CI runner 的重试门保留 master Flaky Windows CI: issue1993-preflight-400 suite cancelled by early event-loop drain | Windows CI 偶发假红:preflight-400 测试进程提前退出致 5 例取消 #3665 实现,字节级未动;④ issue4079 两个测试断言未动,只改 close()。无 fail-fast 变吞错类高危模式——方向恰好相反(静默放行变响亮失败)。
  6. 是否破坏缓存:不涉及(纯测试/CI 基建,不碰压缩引擎、缓存、增量游标)。
  7. 是否改了设计方向:无转向。收敛为「一个共享 helper」而非每文件本地补丁(Windows CI: Parent test hangs on long test files, child lines batch-cancelled (zero assertion failures, multiple branche | Windows CI:长测试文件父测试挂起、子行被整批取消(零断言失败,多分支中招) #3731/CI flake: stream-keepalive tests (#1647) parent-cancelled on loaded Windows shard — no assertion failures #3356 intent 的 union);CI 重试门保持 Flaky Windows CI: issue1993-preflight-400 suite cancelled by early event-loop drain | Windows CI 偶发假红:preflight-400 测试进程提前退出致 5 例取消 #3665 单实现,避免双轨。
  8. 是否引入新配置:无(env/config/CLI 均无变化;HARNESS_CLOSE_TIMEOUT_MS 是模块内常量,且不再 export)。

判定:等新 head CI 绿即可合(合并仍归人);CI 若红我再定向排查。

@ranxianglei
ranxianglei force-pushed the 2026-10-17_win22-drain-cancel-recovery branch from 84fe3d1 to 9ca1719 Compare October 23, 2026 05:08
@ranxianglei

Copy link
Copy Markdown
Owner Author

(ai: zhipuai-lb/glm-5.3)

已把本 PR rebase 到最新 master(07888d7,含 #4275/#4258/#4272 等),冲突处理与本地验证如下:

冲突解决(1 处)

其余文件

  • tests/server-close-guard.ts(新增)与 tests/lane-decisions-matrix.test.ts、tests/pi-omp-fork-adopt.test.ts、tests/plugin-fork.test.ts、tests/plugin-bypass.test.ts 四个测试文件的改动与 master 无重叠,原样保留。

本地验证(Linux, node 25)

  • 受影响的 5 个测试文件:141/141 通过,0 失败,0 跳过。
  • npm run typecheck 干净;npm run build 成功。

丢弃的改动清单

@github-actions

Copy link
Copy Markdown
Contributor

📦 Merged Build Published

This merge to master was published as 0.0.765-master.a35999d (rolling master tag).

npm install -g [email protected]  # this exact build
npm install -g billion-context@master  # always the newest merged build

npm view billion-context@master prints the current version at any time.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

独立复核完成 —— 本 PR 已合并落地 master,冲突处理正确,无遗留可修项。

我没有照抄上一条 rebase 报告,而是拉了真实合并态重新核过一遍(API + 实际 diff + 逐文件引用检查):

核实到的事实

八问(复核口径)

  1. 是什么问题:win22 分片 teardown 挂起把整个测试文件无声拖成 cancelledByParent(fail 0),连累整片红。
  2. 什么原因:两个结构性缺口——close() 之后才挂 once(server,"close") 可能漏掉早发射;无界 await 无 backstop,'close' 不来时进程零活跃句柄,event loop 直接 drain。
  3. 影响范围与严重程度:仅 CI(win22 假红 / 测试无声丢失),不涉终端用户;p2(Flaky Windows CI: issue1993-preflight-400 suite cancelled by early event-loop drain | Windows CI 偶发假红:preflight-400 测试进程提前退出致 5 例取消 #3665 重试门已提供绕行)。
  4. 怎么修复的:收敛为一个共享有界 helper closeServers,残余挂起从「无声连坐」变「点名 stuck server 的命名失败」。
  5. 是否有回归问题:无高危模式——方向恰好是「静默放行 → 响亮失败」,非 fail-fast 变吞错;lane-decisions 的 Windows CI: Parent test hangs on long test files, child lines batch-cancelled (zero assertion failures, multiple branche | Windows CI:长测试文件父测试挂起、子行被整批取消(零断言失败,多分支中招) #3731 本地修复被等价共享 helper 取代(超时语义 5s 静默 → 10s 命名失败,floor 5 已披露旧→新+原因);CI 重试门保留 master Flaky Windows CI: issue1993-preflight-400 suite cancelled by early event-loop drain | Windows CI 偶发假红:preflight-400 测试进程提前退出致 5 例取消 #3665 单实现、字节级未动。
  6. 是否破坏缓存:不涉及(纯测试/CI 基建)。
  7. 是否改了设计方向:无转向,统一为单一共享 helper,避免双轨。
  8. 是否引入新配置:无(env/config/CLI 均无变化)。

残留说明(均已有归属,本轮不动)

收工判定:PR 已合并且落地 master,独立复核无异议,无需后续动作。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working p2 scope/ci

Projects

None yet

Development

Successfully merging this pull request may close these issues.

定时全量测试失败: d9247894

2 participants