Repository navigation
[PR] fix(server): closeAllConnections() cuts through the post-response linger (#4380) - #4384
Conversation
…ger (#4380) The linger wrap (#1982/#2895) defers socket.destroy() for completed-response sockets, which silently defeated server.closeAllConnections(): the API promises 'cut every tracked connection NOW', but armed sockets sat out the linger budget (default 5s, in practice ~3s until the peer's pool expires) before dying. Every embedded startServer/close cycle — test teardown above all — paid that per stop: the lane decision matrix alone burned 168s of its 194s wall time on 56 x 3s waits. Wrap closeAllConnections to run the #2895 force-close seam (stamp restartForced on the whole ledger + destroy) before delegating to the original; the seam also covers ledgered sockets node's own iteration reaches late or never (armed wraps, CONNECT tunnels, claimed upgrades). Plain close() keeps the linger protection unchanged (negative test). Measured: tests/lane-decisions-matrix.test.ts 194s -> 2.6s, tests/plugin-fork.test.ts 150s(timeout) -> 8.9s. Fixes #4380
… stays pinned) Narrow the #4380 cut to the linger-protected class (completed at least one response, never errored): held CONNECT tunnels and claimed upgrades stay unreachable to plain closeAllConnections() — exactly the #2895 semantics the auto-restart repro pins. The restart force-close seam still owns those, unchanged. tests/auto-restart.test.ts 16/16 green again.
(ai: vllm-qwen/qwen3.8-27b) 查重: 不重复——open PR 里只有 #3563 也出现 closeAllConnections 字样,但那是测试 harness 有界关闭 + CI drain-cancel 重试形状(#3356),机制与失败域都不同;本 PR 是 #4380 的唯一修复。 标签: bug / scope/wiring / p2(缺陷类;服务请求管道生命周期面;确定性成本问题,与 #4380 定级一致)。 已完成的核对与推进(接力自 zai/glm-5.3 的会话):
结构化八问
收工判定:代码与双向测试本地全部核实通过,diff 干净(每行都与 #4380 目的相关),CI 已重新入队——待远程 CI 全绿即可合并(合并由人执行)。若队列再次拥堵导致取消,告诉我一声我再触发或推空 commit。 |
📦 Built Package ArtifactBranch: Option A — Install this exact build (recommended)npm install -g [email protected]This exact version is immutable and reproducible. The rolling Option B — Download artifact
tar xzf billion-context-pr4384.tgz
npm install -g packageInstall instructions are refreshed on each push. |
Line-count LPT balanced bytes, not seconds: process-spawning suites cost orders of magnitude more per line than pure assertion files, and the 'perfectly balanced' layouts still produced 5min..37min legs from equal byte counts (windows 22-2/22-3 vs 22-1/22-4 on the same run). With #4384's linger cut landed the per-file wall times are stable enough to weight the partition by MEASURED duration: - scripts/ci-shard-partition.mjs: fileWeight = last measured ms per file (scripts/ci-shard-weights.json), new files fall back to lineCount x msPerLineFallback (median ms/line); same deterministic total LPT otherwise. - scripts/ci-shard-weights.json: 840-file table measured post-#4384 (single-file node --test runs, VM, ~66min serial total); local-env hangs capped at the 150s timeout and listed under timedOut, local rc=1 env-fails excluded. Regenerate via scripts/measure-test-durations.mjs when shard balance degrades. - run-ci-tests.mjs delegates partitionAll to the module (this script has top-level side effects and cannot be imported by tests). - ci.yml: windows-22 CORE 3->5 shards, windows FULL 3->5 (both node versions) — measured max shard drops 1984s->794s serial (concurrency c=4 per shard => ~7min worst-leg target incl. setup). Closes #4360 (with #4377's dist cache + #4384's linger cut, the three levers of the 8-minute goal)
📦 Merged Build PublishedThis merge to master was published as npm install -g [email protected] # this exact build
npm install -g billion-context@master # always the newest merged build
|
Line-count LPT balanced bytes, not seconds: process-spawning suites cost orders of magnitude more per line than pure assertion files, and the 'perfectly balanced' layouts still produced 5min..37min legs from equal byte counts (windows 22-2/22-3 vs 22-1/22-4 on the same run). With #4384's linger cut landed the per-file wall times are stable enough to weight the partition by MEASURED duration: - scripts/ci-shard-partition.mjs: fileWeight = last measured ms per file (scripts/ci-shard-weights.json), new files fall back to lineCount x msPerLineFallback (median ms/line); same deterministic total LPT otherwise. - scripts/ci-shard-weights.json: 840-file table measured post-#4384 (single-file node --test runs, VM, ~66min serial total); local-env hangs capped at the 150s timeout and listed under timedOut, local rc=1 env-fails excluded. Regenerate via scripts/measure-test-durations.mjs when shard balance degrades. - run-ci-tests.mjs delegates partitionAll to the module (this script has top-level side effects and cannot be imported by tests). - ci.yml: windows-22 CORE 3->5 shards, windows FULL 3->5 (both node versions) — measured max shard drops 1984s->794s serial (concurrency c=4 per shard => ~7min worst-leg target incl. setup). Four NEW check names appear (windows 22/24 shards 4/5); branch protection must add them before they gate merges. Cherry-picked from closed PR #4361 (commit 0569781, stranded when the PR was superseded by #4377; picked up per owner invitation in the #4361 thread). The sibling LIVE_FILES commit (d06d192) is already on master via #4416 and is not included. Closes #4437
Fixes #4380
问题
installPostResponseLinger(#1982/#2895)对已完成响应的 socket 包装了destroy()、推迟真正的销毁以等 peer FIN。这静默挫败了server.closeAllConnections()——该 API 的契约是「立刻切断所有连接」,但 armed 的 socket 实际上要等满 linger 预算(默认 5s,实测约 3s 直到 undici 连接池过期发 FIN)才死。影响与实测
每个嵌入式 startServer/close 周期(测试 teardown、优雅停机)都按次支付这笔等待。两个直接受害者:
tests/lane-decisions-matrix.test.ts:194s → 2.6s(56 行矩阵 × 3s = 168s,占全文件 87%)tests/plugin-fork.test.ts:150s(测量超时) → 8.9s这也是 windows CI 测试车道 13–37 分钟长尾的主要成分(#4360)。
修复
包装
server.closeAllConnections:先跑 #2895 的 force-close seam(forceCloseLingeringSockets():给整个 ledger 盖restartForced戳 + 直接 destroy),再委托原始实现。seam 同时覆盖 node 自身迭代触达不到的 ledger socket(armed 包装、CONNECT 隧道、claimed upgrade);orig 调用作为兜底。普通close()的 linger 保护不变(负向测试钉死)。测试
tests/server-close-all-connections.test.ts(双向):closeAllConnections()后 close 事件 <2500ms;close()后 1200ms 内无 close 事件(linger 保护仍在),再显式 cut 收尾。scripts/live-files.mjs(尾部追加,不 renumber 端口带)。不在范围