fix(channel): track qqbot close reconnect timer#5416
Conversation
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Hey @tt-a1i, thanks for the contribution! The fix itself looks spot-on — storing the timer reference and calling .unref() to prevent the process from hanging on exit is exactly the right approach for this issue.
However, the PR body doesn't follow our PR template. The template requires these sections:
## What this PR does## Why it's needed## Reviewer Test Plan(with### How to verify,### Evidence (Before & After),### Tested on)## Risk & Scope## Linked Issues<details><summary>中文说明</summary>(Chinese translation)
Your current headings (## Summary, ## Test Plan) don't match. Could you reformat the PR body to follow the template? The content is all there — it just needs to be reorganized under the expected headings so reviewers can find what they need.
Looking forward to reviewing the code once the template is updated! 🙏
中文说明
嗨 @tt-a1i,感谢贡献!修复本身是正确的——存储 timer 引用并调用 .unref() 防止进程退出时挂起,这正是解决该问题的正确方法。
但是 PR 描述没有按照我们的 PR 模板 填写。模板要求以下章节:
## What this PR does(本 PR 做了什么)## Why it's needed(为什么需要这个改动)## Reviewer Test Plan(审查者测试计划,含### How to verify、### Evidence (Before & After)、### Tested on)## Risk & Scope(风险与范围)## Linked Issues(关联 Issue)<details><summary>中文说明</summary>(中文翻译)
当前使用的标题(## Summary、## Test Plan)与模板不匹配。能否按照模板重新组织 PR 描述?内容都已经有了,只需要按照预期的标题重新排列即可。
更新模板后会继续审查代码!🙏
— Qwen Code · qwen3.7-max
|
updated the PR description to match the template. code is unchanged. |
wenshao
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
— qwen3.7-max via Qwen Code /review
|
@qwen-code /triage |
1 similar comment
|
@qwen-code /triage |
|
Thanks for the PR, @tt-a1i! Template looks good ✓ On direction: this is a clear-cut bug fix — the close-handler On approach: the scope is minimal and correct — 4 lines of production code that match the existing store+unref pattern already used at L845-846. No unrelated changes. The test additions are justified: the MockWebSocket infrastructure (~40 lines) is necessary to exercise the real close handler path rather than mocking internals. Feels right-sized. Moving on to code review. 🔍 中文说明感谢 @tt-a1i 的贡献! 模板完整 ✓ 方向:这是一个明确的 bug 修复——close handler 里的 方案:范围极小且正确——4 行生产代码,与 L845-846 已有的存储+unref 模式一致。没有无关改动。测试新增部分合理:MockWebSocket 基础设施(约 40 行)是触发真实 close handler 路径所必需的,比 mock 内部状态更好。范围恰当。 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewIndependent proposal: to fix "close-handler setTimeout not tracked/unref'd," I would (1) store the The production change is 4 lines in The test adds a Test ResultsReal-Scenario TestingN/A — this fixes internal reconnection timer logic in the QQ bot channel. Triggering it requires a live QQ bot gateway connection, which is not available in CI. The unit test exercises the actual close handler path end-to-end and is the appropriate verification for this change. 中文说明代码审查独立方案:修复"close handler setTimeout 未跟踪/未 unref",我会 (1) 把 生产代码改动仅 4 行——在 close handler 处存储并 unref 定时器,与 L845-846 已有的模式一致。无正确性 bug、无安全问题、无回归。字段声明(L129)和 测试新增 测试结果40/40 测试通过,TypeScript 类型检查通过,Prettier 格式检查通过。 真实场景测试不适用——此修复针对 QQ Bot 内部重连定时器逻辑,触发需要真实的 QQ Bot 网关连接(CI 环境不可用)。单元测试走完了真实 close handler 的端到端路径,是对此改动的恰当验证。 — Qwen Code · qwen3.7-max |
|
This is a clean, well-scoped bug fix. The problem is real (unref'd timer keeping daemon alive during backoff), the fix is the minimum viable change (4 production lines matching an existing pattern), and the test covers the actual code path rather than mocking around it. My independent proposal was identical to what the PR does — that's a good sign. The test infrastructure additions (~40 lines for MockWebSocket) might look like a lot relative to the 4-line fix, but they're testing the real close-handler → reconnect-timer path, which is strictly better than mocking No concerns. Ships the fix, adds coverage, gets out of the way. Approving. ✅ 中文说明这是一个干净、范围恰当的 bug 修复。问题真实存在(未 unref 的定时器在 backoff 期间让 daemon 保持存活),修复是最小可行改动(4 行生产代码,与已有模式一致),测试覆盖了真实代码路径而非在周围 mock。我的独立方案与 PR 完全相同——这是个好信号。 测试基础设施新增约 40 行(MockWebSocket),相对于 4 行修复看起来不少,但它们测试的是真实的 close handler → reconnect timer 路径,比 mock 没有顾虑。修复到位、覆盖到位、不多不少。 批准 ✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM — minimal fix for the unref'd reconnect timer, matches existing pattern, solid regression test. Ready to ship. ✅
✅ Maintainer verification — real local + tmux testingBuilt the PR head ( What it fixesIssue #5413: the QQ bot close handler scheduled its reconnect backoff with a bare this.reconnectTimer = setTimeout(() => this.reconnectWithRetry(), delay);
this.reconnectTimer.unref();
Real process-liveness A/B (tmux) — reproduces #5413Scheduled a reconnect timer the way the close handler does (attempt-1 delay =
The un- Regression test + mutationThe PR's test (
CI & the bot's review
Evidence
VerdictCorrect, minimal, CI-green fix for a real process-liveness leak (#5413): tracking + 🇨🇳 中文版(点击展开)✅ 维护者验证 —— 本地真实 + tmux 测试在隔离 worktree 构建 PR head( 修了什么Issue #5413:QQ bot 的 close 处理器用一个裸的 this.reconnectTimer = setTimeout(() => this.reconnectWithRetry(), delay);
this.reconnectTimer.unref();
真实进程存活性 A/B(tmux)—— 复现 #5413按 close 处理器的方式调度一个 reconnect 定时器(第 1 次重试延迟 =
未 回归测试 + 变异测试PR 的测试(
CI 与机器人的 review
证据
结论针对一个真实的进程存活性泄漏(#5413)的正确、精简、CI 全绿的修复:追踪 + Method: isolated worktree build of |
✅ Local verification — built & real-testedI checked this out on Environment: Node v22.22.2, Linux · built with Static checks
Real integration test (the part that matters)The unit test mocks Test A — timer is tracked, unref'd, and cleared on
Test B — does the pending reconnect keep the Node process alive?
So with the fix a daemon that's mid-backoff exits cleanly; without it the referenced reconnect timer pins the event loop open — precisely the #5413 symptom. Raw harness outputCode notes
LGTM 👍 中文版(点击展开)✅ 本地验证 —— 已构建并真实测试我在 环境: Node v22.22.2,Linux,使用 静态检查
真实集成测试(关键部分)单测里 测试 A —— timer 被跟踪、被 unref、并在
测试 B —— 待执行的重连会不会让 Node 进程一直不退出?
也就是说,应用修复后,处于退避中的 daemon 能干净退出;不修复时被引用的重连 timer 会把事件循环钉住 —— 正是 #5413 的现象。 代码层面
LGTM 👍 Verified locally with Claude Code (Opus 4.8). |
What this PR does
This stores the QQ bot close-handler reconnect timeout in
this.reconnectTimer, calls.unref()on that timeout, and keepsdisconnect()responsible for clearing it. It also adds a regression test that triggers the real close handler and verifies disconnect clears the timer.Why it's needed
Issue #5413 showed that the close-handler reconnect backoff could keep the daemon process alive because the timeout was not tracked or unrefed. Tracking the timer makes cleanup reliable, and unrefing it prevents a pending reconnect delay from being the only reason the Node process stays open.
Reviewer Test Plan
How to verify
Check that the close handler stores its reconnect timer, that the timer is unrefed, and that
disconnect()clears the pending timer. The regression test exercises the actual close handler instead of only mocking the final state.Evidence (Before & After)
Before: a close-handler reconnect timeout could remain referenced and keep the daemon alive during backoff. After: the timeout is tracked, unrefed, and cleared on disconnect.
Tested on
Environment (optional)
Node 22 via
npx -p node@22.Commands run locally:
npx -p node@22 node node_modules/vitest/vitest.mjs run --coverage.enabled=false packages/channels/qqbot/src/send.test.ts packages/channels/qqbot/src/api.test.tsnpx prettier --check packages/channels/qqbot/src/QQChannel.ts packages/channels/qqbot/src/send.test.tsnpx eslint packages/channels/qqbot/src/QQChannel.ts packages/channels/qqbot/src/send.test.tsnpx -p node@22 node node_modules/typescript/bin/tsc --noEmit --project packages/channels/qqbot/tsconfig.jsongit diff --checkRisk & Scope
Linked Issues
Fixes #5413
中文说明
What this PR does
这个 PR 把 QQ bot close handler 里的 reconnect timeout 存到
this.reconnectTimer,对这个 timeout 调用.unref(),并继续由disconnect()负责清理它。同时增加了一个回归测试:触发真实 close handler,并验证 disconnect 会清掉 timer。Why it's needed
#5413 暴露出 close-handler reconnect backoff 可能让 daemon 进程一直不退出,因为这个 timeout 没有被跟踪,也没有 unref。跟踪 timer 可以让 cleanup 更可靠,unref 则避免一个等待重连的延迟成为 Node 进程保持打开的唯一原因。
Reviewer Test Plan
How to verify
确认 close handler 会保存 reconnect timer,timer 会被 unref,并且
disconnect()会清理待执行 timer。回归测试执行真实 close handler,而不是只 mock 最终状态。Evidence (Before & After)
Before:close-handler reconnect timeout 可能保持 referenced,在 backoff 期间让 daemon 进程不退出。After:timeout 被跟踪、unref,并会在 disconnect 时清理。
Tested on
Environment (optional)
通过
npx -p node@22使用 Node 22。本地运行过:
npx -p node@22 node node_modules/vitest/vitest.mjs run --coverage.enabled=false packages/channels/qqbot/src/send.test.ts packages/channels/qqbot/src/api.test.tsnpx prettier --check packages/channels/qqbot/src/QQChannel.ts packages/channels/qqbot/src/send.test.tsnpx eslint packages/channels/qqbot/src/QQChannel.ts packages/channels/qqbot/src/send.test.tsnpx -p node@22 node node_modules/typescript/bin/tsc --noEmit --project packages/channels/qqbot/tsconfig.jsongit diff --checkRisk & Scope
Linked Issues
Fixes #5413
AI Assistance Disclosure
I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.