白油知 相談する

Workflow のステップ名は変えてはいけない — Durable Execution の再実行がどう起こるか

Cloudflare Workflows の step.do はステップ名をキャッシュキーとして使う。名前を変えると再実行される仕様と、実際に動いているシステムのステップ構成・リトライ設定を、公式ドキュメントを実際に確認して整理した。

目次
  1. Durable Execution とは
  2. ステップ名がキャッシュキーである、という仕様
  3. 非決定的な名前も同じ理由で危険
  4. リトライは「ステップ単位」で完結し、失敗は上に伝播する
  5. べき等性 — リトライされる前提でステップを書く
  6. この仕様から引ける設計ルール
  7. FAQ

Durable Execution とは

Durable Execution とは、処理の途中でワーカーが落ちても、完了済みのステップをやり直さずに続きから再開できる実行方式である。

Cloudflare Workflows はこの方式を採用しており、step.do() で囲んだ処理は一度成功すると結果がキャッシュされ、ワークフロー全体が再実行されても再度呼び出されない。裏を返すと、このキャッシュの当たり判定を決めているのはステップ名そのものである。それを知らずにリファクタリングすると、古いステップを「初回実行」として扱ってしまう。

ステップ名がキャッシュキーである、という仕様

Cloudflare公式ドキュメントは明確に書いている。ステップ名は Workflow における「キャッシュキー」として機能する。つまり、step.do("load-topic", ...)step.do("fetch-topic", ...) に名前だけ変えると、Workflow エンジンはこれを別の新しいステップとして扱う。

このシステム(Cloudflare 上で稼働する自律型メディアの記事公開パイプライン)も、この制約に従ってステップ名を固定している。実行中の一例を挙げる。

`` check-paused → load-topic → write-draft → qa-check → save-draft → resolve-approval-policy → create-approval → publish → index-kb → measure ``

名前で識別されるのは step.do() だけではない。同じパイプラインには step.waitForEvent("wait-approval", …)step.sleep("settle", "7 days") もある。とりわけ wait-approval は改名の影響が大きい — 実行中のインスタンスが人間の承認を最大7日間待っている最中に名前が変われば、その待機はやり直しになる。

このうち write-draftindex-kb の2つだけが個別のリトライ設定を持つ({ limit: 2, delay: "30 seconds", backoff: "exponential" }{ limit: 2, delay: "10 seconds" })。残りは既定値のままである。公式ドキュメントによれば、リトライ回数は最大10,000回まで、デフォルトは5回で、バックオフは constant・linear・exponential の3種類が用意されている。

もしこの10ステップのうち qa-checkquality-check にリネームしたとする。実行中のインスタンスから見ると qa-check のキャッシュキーはもう存在しない。次の実行で Workflow エンジンは quality-check未実行のステップとして扱い、そのステップ以降quality-checksave-draft → …)を作り直す。コードの中身を1行も変えていなくても、同じ処理が再度走る。

手前のステップは巻き込まれない。 write-draftload-topic は名前が変わっていないので、キャッシュされた結果がそのまま返る — それが Durable Execution の定義である。

非決定的な名前も同じ理由で危険

同様の落とし穴として、公式ドキュメントは非決定的なステップ名にも警告している。Date.now() やランダム値を含む名前でステップを付けると、そもそもキャッシュとして機能しない。ドキュメントの表現を借りれば、こうした命名は「後続ステップが失敗した際に不要な再実行を引き起こす」。固定文字列であることと、実行のたびに同じ文字列になることの両方が条件になる。

リトライは「ステップ単位」で完結し、失敗は上に伝播する

公式ドキュメントを確認する限り、「失敗したステップから自動的に再開される」という仕様は明記されていない。実際の挙動は次の通りである。

  • リトライは個別ステップの内部で完結する(StepConfig で設定した回数・遅延・タイムアウトの範囲で)
  • 設定した回数を使い切ってなお失敗すると、そのステップは最終的に失敗し、ワークフロー全体が Errored 状態で終了する
  • try...catch で例外を捕まえれば、後続ステップの実行を続けることは可能

つまり「自動で復旧してくれる」のは同一ステップ内のリトライの範囲までで、使い切った後の扱いは書き手の設計次第になる。

べき等性 — リトライされる前提でステップを書く

公式ドキュメントは、ステップが複数回リトライされうる以上、理想的にはべき等であるべきだとしている。支払い処理を例に、実行前に「既に処理済みか」を確認して早期リターンする実装パターンが挙げられている。宛先サービスがリクエストの途中で落ちても処理自体は確定していることがあり、この確認をしないと二重課金のような重複実行が起こりうるためである。

このシステムの create-approval ステップは、まだこの原則を守れていない。 実装は毎回新しい UUID を採番して素の INSERT を実行するだけで、存在確認も INSERT OR IGNORE も無く、approvals テーブルに UNIQUE 制約も張られていない。しかも StepConfig を渡していないので既定の5回リトライが効く。

上の支払いの例がそのまま当てはまる。INSERT がコミットされた後にステップが失敗と判定されれば、同じ記事に pending の承認が2行できる。設計判断ではなく見落としで、Issue #163 として起票されている。「べき等に書くべきだ」と知っていることと実際に書けていることは別だという、平凡な例である。

この仕様から引ける設計ルール

  1. ステップ名は固定文字列にし、一度デプロイしたら変えない。 ステップ名を「読みやすく」変えたくなる場面は多いが、実行中のインスタンスがある限り、その変更は「同じ処理の再実行」を引き起こす
  2. 外部への副作用を持つステップは、べき等に書く。 支払い・通知・公開など、二重実行が問題になる処理は、実行前に「既に完了しているか」を確認する一手間を入れる
  3. 複数の外部呼び出しを1ステップに詰め込まない。 部分的に失敗するとステップ全体がリトライされ、既に成功した呼び出しまで再実行される
  4. 失敗をどこで打ち切るかを明示的に決める。 try...catch で握りつぶすと後続は継続するが、それが「意図した継続」なのか「失敗の隠蔽」なのかは書いた本人にしか区別できない

FAQ

Q. ステップ名を変えたい場合はどうすればよいですか。 実行中のインスタンスが存在しないタイミング(新規デプロイ直後で旧インスタンスが残っていない状態)を狙うか、そもそも稼働中のシステムでは変更しない前提で設計するのが安全です。

Q. ステップ名の重複は許されますか。 同一ワークフロー内で同じ名前を複数回使うと、後から呼んだ方が同じキャッシュキーを参照しうるため、避けるべきです。

Q. リトライ回数を増やせば失敗はほぼ起きなくなりますか。 最大10,000回まで設定できますが、根本原因への対処にはなりません。恒久的に失敗する呼び出し(認証切れ等)は、回数を増やしても失敗し続けます。

Q. べき等性はすべてのステップに必要ですか。 読み取り専用など副作用がない処理では問題になりにくいです。外部への書き込み・通知・課金など、重複実行が実害を生むステップで特に重要になります。

Q. try...catch で例外を握りつぶすとどうなりますか。 そのステップの失敗はワークフローを止めなくなりますが、記録が残らなければ後から気づけません。捕まえるなら、失敗をログや別の記録に残す設計と対にする必要があります。


Durable Execution を前提にしたワークフロー設計や、Cloudflare Workflows を使った自律的な業務パイプラインの構築を検討したい場合は、ご相談を承っています。

出典

  1. Rules of Workflows · Cloudflare Workflows docs(参照 2026-08-12)
  2. Sleeping and retrying · Cloudflare Workflows docs(参照 2026-08-12)