一个仅 30 行的 Slack 通知器占用了我在 4 个客户端 Worker 上 60% 的 CPU 时间预算。罪魁祸首:一个我原以为不可能发生的失控闹钟循环。
以下是 Cloudflare 文档中没有明确说明的部分:当你的 alarm() 处理程序反复抛出异常时,平台会以指数退避方式进行重试——但退避间隔存在上限。在我的测试中,在大约连续 5 次失败后,间隔上限约为 30 分钟。此后,闹钟将无限期地按该上限间隔持续触发。它不会放弃,也不会丢弃闹钟。一个损坏的处理程序会让你的持久化对象(Durable Object)保持活跃并持续消耗 CPU 毫秒数,直到你进行干预或完全删除该对象。
真正改变我生产环境行为的做法是在 alarm() 内部捕获异常,并手动重新调度,而不是让平台控制重试时机:
async alarm(): Promise<void> {
try {
await this.runBatchInsert();
} catch (err) {
console.error("闹钟处理程序失败:", err);
const next = Date.now() + 60_000;
await this.ctx.storage.setAlarm(next);
}
}
如果你重新抛出异常,你将获得无法配置的指数退避机制。如果你自行捕获并重新调度,你将获得固定的 60 秒窗口——或者任何符合你外部应用程序接口(API)速率限制的时间间隔。对于任何涉及 D1 数据库或第三方端点的操作,我都需要这种控制权。
另一件值得了解的事情是:调度闹钟被视为存储状态的一部分。即使你从持久化对象(Durable Object)的存储中删除了所有应用键值,闹钟时间戳仍会使该对象保持活跃。我曾故意利用这一点来构建无键心跳持久化对象——轻量级,无应用数据,仅依靠重复触发的闹钟使对象保持温热状态。
本文未涵盖的内容包括:用于丢弃过时闹钟调用的生成计数器模式、用于捕获计费作业遗漏闹钟的看门狗 Worker(定时触发器 + 键值存储扫描),以及针对发票写入等非幂等性工作(其中“精确一次”语义至关重要)的闹钟墓碑标记方法。
我在 dailymanuallab.com 上撰写了完整的分析文章——包括所有三种恢复模式及其生产环境代码。
免责声明:本文内容来自互联网,该文观点不代表本站观点。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请到页面底部单击反馈,一经查实,本站将立刻删除。