- Durable Execution(耐久実行)の考え方と、Workflows が解決する課題
WorkflowEntrypointクラスとstep.do/step.sleep/step.waitForEventの使い方- Worker のバインディングから Workflow を起動(
create)し、instance の status を追跡する方法 wrangler workflowsコマンドでステップの実行・リトライ・エラーを観測する方法- 自動リトライの既定値と、
NonRetryableErrorによるリトライ制御
Workflows とは — 失敗しても「続きから」再開する処理基盤
外部 API を呼び、結果を待ち、加工して保存する——そんな多段処理を普通の Worker で書くと、途中でネットワークエラーが起きたときの再実行、リトライ間隔の管理、途中結果の保存はすべて自前の宿題になります。Workflows はこの宿題を引き受けるサービスです。処理を step という単位に分割すると、各 step の成功結果が自動的に永続化され、失敗しても最後に成功した step の直後から再開されます。この「状態の保存をコードで書かなくても暗黙に永続化される」プログラミングモデルは一般に Durable Execution(耐久実行) と呼ばれます。
Workflows のインスタンスは数分どころか数時間・数週間にわたって実行を継続でき、待機中はコンピュートを消費しません。Workers Free プランでも利用できます。まず用語を整理します。
| 用語 | 意味 |
|---|---|
| Workflow | 名前付きの処理定義。1 つの Workers スクリプトに紐づく |
| instance | Workflow の個々の実行。1 つの Workflow から実質無制限に作れる |
| step | 個別にリトライ可能な最小単位。戻り値(状態)が永続化され、再開時の「続きから」の起点になる |
公式ドキュメントが挙げる代表的なユースケースは次のとおりです。
- データパイプライン・バッチ — R2 へのファイルアップロード後処理、D1 のエクスポートなど、失敗したら途中から再開したい処理
- ユーザーライフサイクル管理 — 登録 → ウェルカムメール → 数日待って試用期限の通知、といった長い時間軸のオンボーディング
- AI パイプライン — 画像取得 → AI で説明文生成 → 人間の承認待ち → 公開、のようなリトライと待機が混ざる処理
- human-in-the-loop 承認 — 外部イベント(Webhook や人間の承認)が届くまで実行を一時停止するフロー
step API — do・sleep・waitForEvent
Workflow の本体は WorkflowEntrypoint を継承したクラスで、run(event, step) メソッドに処理を書きます。event.payload には起動時に渡したパラメータが入り、step オブジェクトが提供する次のメソッドで耐久性のあるステップを定義します。
| メソッド | 役割 | 補足 |
|---|---|---|
step.do(name, config?, callback) | コードを実行し、戻り値を永続化する | 戻り値は直列化可能な値でストリーム以外は 1MiB まで(2026-08 時点)。ステップ名がキャッシュキーになる |
step.sleep(name, duration) | 相対時間だけ実行を一時停止する | "20 seconds" や "1 hour" 形式。最長 365 日。ステップ数上限にカウントされない |
step.sleepUntil(name, timestamp) | 指定日時まで一時停止する | Date または UNIX エポックミリ秒を渡す |
step.waitForEvent(name, options) | 外部イベントが届くまで待機する | type で対応するイベントを識別。デフォルトタイムアウトは 24 時間(1 秒〜365 日で指定可) |
step.do が失敗(例外を throw)すると、Workflows が自動的にリトライします。リトライ設定を省略した場合の既定値は次のとおりです。
const defaultConfig: WorkflowStepConfig = {
retries: {
limit: 5, // 最大試行回数
delay: 10000, // 試行間の遅延(10 秒)
backoff: "exponential", // "constant" | "linear" | "exponential"
},
timeout: "10 minutes", // 1 試行あたりのタイムアウト
};
backoff は遅延の増やし方(固定・線形・指数)を決めます。delay には固定値のほか、エラー内容や試行回数から次の遅延を計算する関数も渡せます。逆に「認証エラーなどリトライしても無駄な失敗」は、cloudflare:workflows からインポートする NonRetryableError を throw すると、リトライせず即座に instance を失敗させられます。step の失敗で Workflow 全体を止めたくない場合は、step.do の呼び出しを try...catch で囲めば、失敗を捕捉してクリーンアップ用の step に進む、といった制御もできます。
step を設計するときの判断基準は「一部が失敗したとき、このコード全体を再実行してよいか?」です。外部 API 呼び出し・DB クエリ・ストレージ読み取りはそれぞれ別の step に分けると、後続の失敗時に取得済みデータを再利用でき、無駄な API 呼び出しを避けられます。詳しくは公式の Rules of Workflows にまとまっています。
instance の起動・ライフサイクル・観測
Workflow は次の方法で起動(instance を作成)できます。
- Worker のバインディング — Wrangler 設定の
workflowsでバインドし、env.MY_WORKFLOW.create()を呼ぶ。id(省略時は自動生成)とparams(JSON 直列化可能な任意のデータ)を渡せる - wrangler CLI —
npx wrangler workflows trigger <名前> '<JSON>' - REST API — HTTP 経由でプログラマブルに作成
- cron スケジュール — Workflow 定義に
schedules(cron 式の配列)を追加すると、定期的に instance が自動作成される
// Worker から起動して ID と状態を返す
let instance = await env.MY_WORKFLOW.create({
id: crypto.randomUUID(),
params: { hello: "world" },
});
return Response.json({
id: instance.id,
details: await instance.status(),
});
instance.status() が返す status フィールドで、instance が今どの状態かを確認できます。
| status | 意味 |
|---|---|
queued | 起動待ち(同時実行数の上限に達している場合など) |
running | 実行中 |
waiting | 休止中。sleep・リトライ待ち・イベント待ちのいずれか。同時実行数の上限にカウントされない |
paused / waitingForPause | ユーザー操作による一時停止(完了 / 移行中) |
complete | 正常終了。run の戻り値が output に入る |
errored | リトライ上限到達や未捕捉例外による失敗 |
terminated | ユーザーが明示的に停止した |
instance には pause() / resume() / terminate() / restart() という操作も用意されています。同じことは wrangler workflows instances pause|resume|terminate|restart でも行えます。観測系のコマンドは次の 2 つが主役です。
npx wrangler workflows instances list <Workflow 名>— instance の一覧を status 付きで表示npx wrangler workflows instances describe <Workflow 名> latest— 各 step の成否・step が返した状態・sleep の起床予定・リトライ回数・エラーメッセージを表示(ID の代わりにlatestと書ける)
ローカル開発では npx wrangler dev がエミュレートされた Workflows 実行環境を提供し、各コマンドに --local フラグを付けるとローカルの instance を操作できます(--local は Wrangler 4.79.0 以降)。さらに wrangler dev 実行中にターミナルで e を押すと、ブラウザベースの Local Explorer(http://localhost:8787/cdn-cgi/explorer、Wrangler 4.82.1 以降)が開き、instance の step 履歴表示・イベント送信・一時停止などを GUI で行えます。
データ取得 → 20 秒待機 → 加工、という 3 段の Workflow を作り、ローカルで起動して status の変化を観察します。所要時間は 15 分程度です。
Workflows は Workers Free プランで動作します(ステップ数などの上限は Paid より小さめです。2026-08 時点で Free は 1 instance あたり 1,024 step、Paid は既定 10,000 step)。
- Worker プロジェクトを作成します。質問には「
Hello World example」「Worker only」「TypeScript」、デプロイは「No」を選びます。npm create cloudflare@latest -- my-workflow cd my-workflow src/workflow.tsを新規作成し、Workflow 本体を定義します。import { WorkflowEntrypoint, WorkflowStep } from "cloudflare:workers"; import type { WorkflowEvent } from "cloudflare:workers"; type Params = { name?: string }; type IPResponse = { result: { ipv4_cidrs: string[] } }; export class MyWorkflow extends WorkflowEntrypoint<Env, Params> { async run(event: WorkflowEvent<Params>, step: WorkflowStep) { const data = await step.do("fetch data", async () => { const response = await fetch( "https://api.cloudflare.com/client/v4/ips", ); return await response.json<IPResponse>(); }); await step.sleep("pause", "20 seconds"); const result = await step.do( "process data", { retries: { limit: 3, delay: "5 seconds", backoff: "linear" } }, async () => { return { name: event.payload.name ?? "World", ipCount: data.result.ipv4_cidrs.length, }; }, ); return result; } }wrangler.jsoncにworkflows設定を追加します。class_nameはエクスポートしたクラス名、bindingはコードから参照する変数名です。{ "$schema": "node_modules/wrangler/config-schema.json", "name": "my-workflow", "main": "src/index.ts", "compatibility_date": "2026-08-10", "observability": { "enabled": true }, "workflows": [ { "name": "my-workflow", "binding": "MY_WORKFLOW", "class_name": "MyWorkflow" } ] }src/index.tsを、instance の作成と status 照会を行う fetch ハンドラに置き換えます。export { MyWorkflow } from "./workflow"; export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); const instanceId = url.searchParams.get("instanceId"); if (instanceId) { const instance = await env.MY_WORKFLOW.get(instanceId); return Response.json(await instance.status()); } const instance = await env.MY_WORKFLOW.create(); return Response.json({ instanceId: instance.id }); }, } satisfies ExportedHandler<Env>;- バインディングの型を生成してから、ローカル開発サーバーを起動します。
npx wrangler types npx wrangler dev - 別ターミナルから instance を作成し、返ってきた ID で status を数回確認します。
curl http://localhost:8787 # {"instanceId":"abc-123-def"} curl "http://localhost:8787?instanceId=abc-123-def" # {"status":"running", ...}
sleep の 20 秒が明けると status が complete になり、output に process data step の戻り値(name と ipCount)が入っていれば成功です。
最初の 2 回はわざと失敗し 3 回目で成功する step を追加して、Workflows が自動リトライする様子を wrangler workflows instances describe で観測します。所要時間は 10 分程度です。
src/workflow.tsのstep.sleepの直後(process dataの前)に、失敗する step を追加します。コールバックの第 1 引数ctx(WorkflowStepContext)のattemptには現在の試行回数(1 始まり)が入ります。const flaky = await step.do( "flaky step", { retries: { limit: 3, delay: "3 seconds", backoff: "constant" } }, async (ctx) => { if (ctx.attempt < 3) { throw new Error(`attempt ${ctx.attempt} failed on purpose`); } return { succeededOnAttempt: ctx.attempt }; }, );wrangler devが再読み込みされたら、curl http://localhost:8787で新しい instance を作成します。- 20 秒の sleep が明けた頃に、別ターミナルから最新 instance の詳細を表示します。
npx wrangler workflows instances describe my-workflow latest --local
出力には step ごとの成否・リトライ・エラーが並びます。flaky step の欄で「1・2 回目の試行が attempt N failed on purpose というエラーで失敗し、約 3 秒間隔をおいた 3 回目で成功している」こと、そして先行する fetch data は再実行されていないことを確認してください。これが「最後に成功した step から再開する」durable execution の動きです。ちなみにこのエラーが NonRetryableError だった場合はリトライされず、instance は即座に errored になります。
Workflow を外部イベント(人間の承認)が届くまで待機させ、CLI からイベントを送って再開させます。所要時間は 10 分程度です。
src/workflow.tsのreturn result;の直前に、承認待ちの step を追加します。let approved = false; try { const approval = await step.waitForEvent( "wait for approval", { type: "approval", timeout: "15 minutes" }, ); // sendEvent で送られた payload が approval.payload に入る approved = (approval.payload as { approved?: boolean }).approved === true; } catch (e) { // タイムアウト: 承認が来なかった場合もここで拾って続行できる approved = false; }curl http://localhost:8787で新しい instance を作成し、20 秒の sleep とリトライが終わった頃に status を確認します。イベント待ちに入った instance はwaitingになります。- 別ターミナルから、
waitForEventのtypeと一致する--type approvalでイベントを送信します(<INSTANCE_ID>は手順 2 の ID に置き換えます。latestでも指定できます)。npx wrangler workflows instances send-event my-workflow <INSTANCE_ID> \ --type approval --payload '{"approved":true}' --local - 再度 status を確認し、
completeになっていれば成功です。
Worker のコードからは instance.sendEvent({ type: "approval", payload }) で同じことができ、Stripe などの Webhook を受けて Workflow を進める構成が組めます。なお、instance が作成済みであれば waitForEvent に到達する前に届いたイベントもバッファされ、対応する step に到達した時点で消費されます。
つまずきポイント
ステップ名に日時や乱数を入れるとキャッシュが効かない — step の名前は永続化された結果を引き当てる「キャッシュキー」です。step.do(`sync at ${Date.now()}`, ...) のような非決定的な名前を付けると、再開のたびに別ステップと見なされて再実行されます。名前は決定的にし、動的な値は戻り値やログに入れます。
step の外に置いた変数はハイバネーションで消える — 長い sleep やイベント待ちの間、エンジンは休止してメモリ上の状態を失うことがあります。トップレベルの配列に push して状態を溜めるコードは、再開後に空へ戻ります。状態は必ず step.do の戻り値として組み立て、副作用(別 Workflow の作成や Math.random() など)も step の中で行います。step の外の console.log はエンジン再起動時に複数回出力されることがあります。
waitForEvent の type 制約とタイムアウトの挙動 — type に使えるのは英数字と - _ のみで、. を含めると workflow.invalid_event_type エラーになります。またタイムアウト時は例外が throw され、そのままだと instance が失敗します。イベントが来なくても処理を続けたい場合は try...catch で囲んでください。
create の ID 重複エラー — 完了済み instance の状態は保持期間(2026-08 時点で Free 3 日 / Paid 30 日)の間残っており、その間に同じ ID で create を呼ぶと例外になります。同じ ID で再実行したい場合は既存 instance の restart() を使うか、ID を変えます。ちなみに createBatch(最大 100 件を一括作成)は冪等で、重複 ID はスキップされます。
理解度チェック
Q1. リトライ設定を省略した step.do は、失敗時にどう振る舞う?
既定値では 10 秒の遅延から始まる exponential バックオフで自動リトライされ、試行回数は合計最大 5 回(retries.limit: 5)です。1 試行あたりのタイムアウトは 10 分です。retries と timeout を渡せば step ごとに上書きできます。
Q2. step.sleep で 30 日眠っている instance は、同時実行数の上限やステップ数上限、CPU 課金にカウントされる?
いずれもカウントされません。sleep・リトライ待ち・イベント待ちの instance は waiting 状態となり同時実行数の上限から除外され、step.sleep はステップ数上限に数えられず、待機中は CPU 時間も消費しません。
Q3. 認証エラーのような「リトライしても無駄な失敗」を検出したとき、即座に instance を失敗させるには?
cloudflare:workflows からインポートした NonRetryableError を step 内で throw します。step のリトライは行われず、エラーはトップレベルへ伝播して instance は errored になります(try...catch で捕捉すれば続行も可能です)。
Q4. step.waitForEvent のデフォルトタイムアウトと、タイムアウトしたときの挙動は?
デフォルトは 24 時間で、1 秒〜365 日の範囲で変更できます。タイムアウトすると例外が throw され、捕捉しなければ instance は失敗します。イベントが来なくても続行したい場合は try...catch で囲みます。
まとめと次章へ
- Workflows は処理を step 単位に分割し、成功結果を自動永続化する durable execution 基盤。失敗しても最後に成功した step から再開する
step.doは自動リトライ(既定: 試行 5 回・遅延 10 秒・exponential)、step.sleepは最長 365 日の待機、step.waitForEventは外部イベント待ちを担う- 起動は Worker バインディングの
create・wrangler workflows trigger・REST API・cronschedulesの 4 通り。instance.status()で状態を追跡できる wrangler workflows instances describe <名前> latest --localで step ごとの成否・リトライ・エラーをローカルでも観測できる- ステップ名は決定的に、状態は step の戻り値で、副作用は step の中で——が Rules of Workflows の要点
参考リンク:
- Build your first Workflow
- Workers API(WorkflowEntrypoint / step / instance)
- Sleeping and retrying
- Events and parameters
- Rules of Workflows
- Limits / Pricing
多段処理の耐久性を手に入れたところで、次章は Workers AI です。エッジで動く AI 推論を Worker から呼び出します。この章で作った「AI パイプライン + 承認待ち」のような構成は、Workflows と Workers AI を組み合わせる典型例になります。