写 Cloudflare Workflows 要遵守哪些规则?

Workflows 里有一类 bug 很隐蔽:你在步骤外面写了一句 console.log 当审计日志,实例恢复后,这条日志又输出了一遍。我在本地重启实例时亲眼看到过。

写 Workflows 的核心规则是三条:步骤要细粒度、步骤名要确定、步骤外不要放副作用。 官方有一篇 Rules of Workflows,值得完整读一遍,下面是我认为最影响正确性的部分。

原因都出在同一个机制上:平台靠步骤名缓存结果,实例可能休眠并丢失内存,恢复时从头执行你的 run 函数,把已完成步骤的缓存结果喂回去。所有规则都是这个机制的推论。

一个步骤应该做多大?

细粒度。别在一个步骤里调用多个服务,否则一个服务失败,整个步骤里的调用都会重跑。

把 Workflow 当成「带重试的函数」、一个大步骤包住所有逻辑,是最常见的误用。步骤才是重试和缓存的单位:拆得越细,失败时重跑的范围越小。

步骤名为什么必须确定?

因为步骤名是缓存键。用 Date.now()、时间戳或随机数命名步骤,每次恢复时名字都不同,缓存就失效了。

条件分支同理:分支只能依赖 payload 与先前步骤的输出。随机或时间相关的判断,要包进步骤里,让它的结果被持久化。

步骤外的代码能写什么?

不要依赖步骤外的状态,也不要在步骤外做副作用。实例可能休眠并丢失内存,顶层变量只能由 step.do 的返回值构造;步骤外的代码在恢复时可能重新执行。

我在本地验证过:从某个步骤重启实例后,写在步骤外的日志又执行了一次。所以审计日志要放进步骤里,不要放在步骤外。

步骤内部也不是绝对安全。步骤的结果被缓存,但内部的副作用不是:超时、重试或持久化前被中断,回调可能执行多于一次。官方规则的第一条就是让 API 与绑定调用幂等,比如把 event.instanceId 加步骤名作为幂等键传给支付服务。

哪些调用要小心写法?

  • 每个 step.do 都要 await。
  • 不要把 sleep 或 waitForEvent 嵌套进 step.do。
  • 实例 ID 要唯一。

waitForEvent 有个容易忘的点:默认超时 24 小时,且超时会抛错。需要更长要显式设置 timeout,需要容忍超时就用 try...catch。我在本地试过,超时抛出的信息形如 Execution timed out after 2000ms,接住之后流程可以继续往下走。

返回值太大怎么办?

单个非流式步骤的返回值不超过 1 MiB。把大 JSON 当步骤返回值,会直接撞上这个限制。

大数据放 R2 或 D1,步骤只返回引用。

重试耗尽后会自动回滚吗?

不会。默认只是进入 Errored 状态,补偿逻辑要你自己写。官方 API 提供了 rollback 处理函数,用于 saga 式补偿,具体用法请读官方文档,我没有试过。

整体的执行模型和重试语义,见 Workflows 怎么让长流程不从头再来。

步骤外代码在重启后重复执行、waitForEvent 超时抛错并可被 try...catch 接住,是我在本地 wrangler dev 上看到的。其余规则、1 MiB 返回值上限、24 小时默认超时与 rollback 都是官方文档的说法,我没有逐条验证,其中 rollback 没有试过。限额与默认值以 2026-10 官方文档为准,会变化。官方参考:Rules of Workflows。