Durable Objects 的 alarm 怎么用?

我在本地用 setAlarm(now + 500ms) 设了一个 alarm,大约半秒后 alarm() 触发;再让它前两次都抛错,第二次、第三次调用的 retryCount 分别是 1 和 2。

所以答案是:用 setAlarm(时间戳毫秒) 设置,到点运行时调用对象的 alarm() 方法。它是每个对象一个的、可持久的定时器,适合会话过期、延迟清理、批量刷盘这类按实体的任务。

但 alarm 有几条语义很容易踩:只有一个、不自动重复、至少执行一次。下面按你会遇到的顺序讲。对象本身的模型见 Durable Objects:单线程模型、一个 ID 一个实例与存储。

最小的 alarm 怎么写?

export class Session extends DurableObject<Env> {
  async touch() {
    await this.ctx.storage.setAlarm(Date.now() + 30 * 60_000);
  }

  async alarm(info?: AlarmInvocationInfo) {
    this.ctx.storage.sql.exec("DELETE FROM data");
    // 周期任务需要在这里再次 setAlarm
  }
}

touch 每次调用都会把 alarm 推到 30 分钟后,因为同一时刻每个对象只有一个 alarm,再次 setAlarm 会覆盖。

alarm 失败了会怎样?

alarm() 抛未捕获异常时自动重试,从 2 秒起指数退避,最多 6 次,且重试只针对最近一次 setAlarm。想无限重试,就自己捕获异常并重新 setAlarm。

我在本地让 alarm() 前两次都抛错:alarmInfo 里第一次是 retryCount: 0 与 isRetry: false,第二次、第三次的 retryCount 是 1、2,isRetry 为 true,重试间隔大约 2 秒、再约 4 到 5 秒,符合从 2 秒起的指数退避。alarm() 运行期间 getAlarm() 返回 null。

为什么 alarm() 必须幂等?

因为 alarm 保证至少执行一次,不是恰好一次,重试时同一个任务可能被再跑一遍。

怎么做周期任务和多事件?

alarm 不自动重复,周期任务要在处理完之后设置下一次。

多事件的做法是:把待办放进表里,alarm() 处理所有到期项,然后只为最近的下一项设置 alarm。

还有一个坑:对象从休眠中醒来时,构造函数先于 alarm() 运行。构造里如果无条件 setAlarm,会覆盖已有的,要先用 getAlarm() 判断。

alarm 能替代 cron 吗?

不能。alarm 是按实体的定时器,不是全局 cron。全站定时任务用 Workers 的 Cron Triggers;跨步骤、可恢复的长流程见 Workflows;需要可靠地「至少一次」投递给别的系统,见 Queues。

计费上,每次 setAlarm 在 SQLite 后端计一次写入,每次 alarm 调用计一次请求。

500 毫秒触发、retryCount 与 isRetry 的变化、重试间隔和 getAlarm() 返回 null,我在本地 workerd 上跑过,线上的调度精度我没测过。单个 alarm 的覆盖语义、至少一次、最多 6 次重试、构造函数先于 alarm() 运行,以及计费方式,都来自官方文档,我没有验证过。数值会变化,以 2026-10 的官方文档为准。