跳转至

[ISSUE-0027] watchdog 自动恢复 cooldown 用 setTimeout(同款)

相关源码src/utils/scrape-watchdog.ts:190 v0.10.23/24 引入;v0.10.41 修复

根因

v0.10.23 引入 watchdog 自动恢复:检测到连续异常后,cooldownMs 后 pumpTasks。 v0.10.41 之前实现:

setTimeout(() => {
  pumpTasks();
  manageQueue();
}, cooldownMs);  // ❌ MV3 SW kill 时 setTimeout 丢

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 compile 0 错
  • pnpm build 8s

实测路径: 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 个版本后再加类似逻辑时没复用。复杂项目里这种"局部最优,全局重蹈"很常见。

防止再犯的最低代价:写完新功能后用沉淀的清单全局扫一遍同类陷阱