Demo 04:持久计时器
普通 time.sleep() 的问题是:等待状态存在进程内存里。进程重启后,它不知道自己还要等多久,也不知道该继续哪个 workflow。
Durable Timer 的目标是把“未来继续执行”变成持久事实。
Timer 的语义
python
ctx.sleep("settlement-wait", seconds=5)它不是简单阻塞线程,而是:
- 在 Journal 中记录
PENDING_TIMER,包含fire_at。 - 当前 attempt 暂停,invocation 进入
WAITING_TIMER。 - 后台 worker 周期扫描,到期后重新执行 handler。
- 重放到
sleep时发现时间已到,把该 step 标记为完成并继续。
流程图
为什么 timer 也要进 Journal
因为等待本身也是业务执行的一部分。比如:
python
ctx.run("send-email", send_email)
ctx.sleep("wait-user-confirm", seconds=86400)
ctx.run("disable-account", disable_account)如果 sleep 不持久化,系统重启后可能:
| 错误 | 后果 |
|---|---|
| 忘记等待 | 永远不继续 |
| 从头等待 | 额外延迟一天 |
| 重新发邮件 | 产生重复通知 |
Durable Timer 让等待和其他步骤一样可重放。
教学版和 Restate 的差异
Restate 支持 durable sleeps、delayed messages、timeouts,并且在 FaaS 场景可以让函数挂起,避免为长时间等待付计算资源费用。教学版只实现 sleep,并通过后台 loop 扫描恢复。
| 能力 | 教学版 | Restate 生产版 |
|---|---|---|
| sleep | 支持 | 支持 |
| delayed message | 不支持 | 支持 |
| promise / awakeable | 不支持 | 支持 |
| FaaS suspension | 不支持 | 支持 |
| 分区 timer index | 简化为查询 Journal | 分区存储索引 |
小练习
- 把 sleep 时间改成 60 秒,重启后端进程,确认 invocation 仍能继续。
- 增加一个
cancelled状态,思考 timer 到期后是否还应继续执行。