[ISSUE-0027] watchdog 自动恢复 cooldown 用 setTimeout(同款)¶
相关源码:
src/utils/scrape-watchdog.ts:190v0.10.23/24 引入;v0.10.41 修复
根因¶
v0.10.23 引入 watchdog 自动恢复:检测到连续异常后,cooldownMs 后 pumpTasks。 v0.10.41 之前实现:
cooldownMs 用户可设 0-600 秒。默认 10s,但如果用户调到 60s+,SW 大概率被 kill。
讽刺:watchdog 本身就是为了"自动恢复"机制存在的,结果它依赖的 setTimeout 会丢,让自动恢复永远不触发 —— 用户卡在"已重置但没继续跑"状态。
与 ISSUE-0026 的关系¶
完全同款 bug 模式:
- ISSUE-0026 是拦截恢复的 setTimeout 丢
- ISSUE-0027 是watchdog 自动恢复的 setTimeout 丢
- 都用 chrome.alarms 修
之所以 v0.10.40 修 ISSUE-0026 时没发现 ISSUE-0027:v0.10.40 只盯着自己刚写的代码(interception),没扫历史代码同类陷阱。v0.10.41 复用 ISSUE-0026 沉淀的清单扫了 scrape-watchdog / engine-manager / batch-controller,逮到这个。
修复¶
// 新增 alarm 常量
const WATCHDOG_RESUME_ALARM = 'scrape_watchdog_resume';
// 替换 setTimeout
if (cooldownMs <= 0) {
// 0 冷却:立即 pump(向后兼容)
pumpTasks().catch(() => {});
manageQueue();
} else {
browser.alarms.create(WATCHDOG_RESUME_ALARM, { when: Date.now() + cooldownMs });
}
// 新增 alarm handler
export async function onWatchdogResume() {
await pumpTasks().catch(() => {});
manageQueue();
}
// background/index.ts onAlarm 路由
if (alarm.name === WATCHDOG_RESUME_ALARM_NAME) {
onWatchdogResume().catch(...);
}
改动文件¶
| 文件 | 改了什么 |
|---|---|
src/utils/scrape-watchdog.ts |
+ WATCHDOG_RESUME_ALARM + onWatchdogResume;setTimeout → alarm |
src/entrypoints/background/index.ts |
+ WATCHDOG_RESUME_ALARM_NAME 路由 |
package.json |
0.10.40 → 0.10.41 |
验证¶
- ✅
pnpm compile0 错 - ✅
pnpm build8s
实测路径: 1. 触发连续异常让 watchdog 自动恢复 2. console 看 "alarm scheduled" 3. SW 强制 kill(chrome://serviceworker-internals) 4. 等 cooldownMs 到 → SW 被 alarm 唤醒 → onWatchdogResume → pumpTasks
其他扫到但 OK 的可疑点¶
复用 ISSUE-0026 沉淀的"深度审查清单"扫了几处,确认其余设计正确:
| 位置 | 类型 | 评估 |
|---|---|---|
engine-manager.ts:223 setTimeout(manageQueue, ms) |
短延迟(getScrapeDelayMs,几秒) | OK — 是 idempotent 重试,每次任务结束 finally 都会再调,setTimeout 丢一次问题不大 |
batch-controller.ts:220 setInterval(keepAlive, 20s) |
SW 保活心跳 | OK — 目的就是保活,SW 死了由 alarm 唤醒 |
batch-controller.ts:336 setTimeout(finalizeTab, TAB_TIMEOUT=60s) |
Tab 超时兜底 | OK — SW 死后由 watchdog 兜底处理孤儿 tab |
batch-controller.ts:157 sleep = setTimeout(r, ms) |
短期等待(如 1s) | OK — 短延迟在同一 SW 内 |
如何避免再犯¶
- 写"X 时间后做 Y"的代码时立刻问:"X > 5s 吗?如果 SW 在期间 kill 怎么办?"
- 新加状态变量时立刻问:"SW kill 后重新初始化会破坏什么?"
- 类似模式跨场景扫:本次修完 interception 后用同清单扫 watchdog 找到了同款
复盘:跨场景模式复用¶
v0.10.15 引入 watchdog 周期用 chrome.alarms 而非 setInterval ✅
v0.10.23/24 引入 watchdog 自动恢复用 setTimeout ❌(同模块违反同原则)
v0.10.37 引入 interception 用 setTimeout ❌(重蹈覆辙)
v0.10.40 修 interception 用 alarm ✅
v0.10.41 同清单扫,发现 watchdog 自动恢复同款 → 修 ✅
教训:好的模式(alarm)已经在项目里存在过,但 1-2 个版本后再加类似逻辑时没复用。复杂项目里这种"局部最优,全局重蹈"很常见。
防止再犯的最低代价:写完新功能后用沉淀的清单全局扫一遍同类陷阱。