コンテンツにスキップ

第17章: Cloudflare Workflows による耐久バッチ実行(実践チュートリアル)

従来のサーバーレス関数では「処理時間が数十秒を超えるとタイムアウトする」「外部API障害で途中で落ちたら最初から全処理をやり直し」という課題がありました。 Cloudflare Workflows は、コード内に複数のステップ(step.do())やスリープ(step.sleep())を記述するだけで、途中でサーバーが再起動しても 完了済みステップの結果を保持したまま、失敗したステップだけを自動再試行する耐久性実行エンジン です。


本チュートリアルで作成するもの

  • マルチステップ耐久ワークフロー:
    • Step 1: 外部APIからデータ収集・集計(失敗時は自動指数バックオフ再試行)
    • Step 2: 指定時間(例: 1分間、または3日間)のコストゼロ待機(step.sleep
    • Step 3: 完了通知の送信
  • CLI操作:
    • wrangler workflows trigger によるCLIからの直接実行
    • wrangler workflows describe によるステップ進捗の追跡

Step 1: wrangler.jsonc の設定

プロジェクトの設定ファイルに workflows 定義を追加します。

{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "durable-workflow-demo",
"main": "src/index.ts",
"compatibility_date": "2024-10-22",
"compatibility_flags": [
"nodejs_compat"
],
"workflows": [
{
"name": "report-pipeline",
"binding": "REPORT_WORKFLOW",
"class_name": "ReportWorkflow"
}
]
}

型定義を更新します。

Terminal window
npx wrangler types

Step 2: ワークフロークラスの実装(src/index.ts

src/index.ts
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
// ワークフロー起動パラメータの型定義
type WorkflowParams = {
reportId: string;
recipientEmail: string;
};
// 1. 耐久ワークフロー本体
export class ReportWorkflow extends WorkflowEntrypoint<Env, WorkflowParams> {
async run(event: WorkflowEvent<WorkflowParams>, step: WorkflowStep) {
const { reportId, recipientEmail } = event.payload;
// ステップ1: データ集計処理(失敗した場合は自動で3回まで指数バックオフ再試行)
const reportData = await step.do(
'aggregate-report-data',
{ retries: { limit: 3, delay: '5 seconds', backoff: 'exponential' } },
async () => {
console.log(`[Step 1 開始] レポートID: ${reportId} のデータ集計中...`);
// 何らかの外部API呼び出しやD1クエリのシミュレーション
return {
totalUsers: 1450,
revenue: 350000,
generatedAt: new Date().toISOString(),
};
}
);
// ステップ2: 待機(CPUリソースを一切消費せず、安全にスリープ)
// ※ チュートリアル検証のため10秒待機(実務では '1 hour' や '3 days' と指定可能)
console.log('[Step 2] 一時待機に入ります(10秒間)...');
await step.sleep('wait-for-cooloff', '10 seconds');
// ステップ3: 集計結果のレポート送信
await step.do('send-final-report', async () => {
console.log(`[Step 3 開始] ${recipientEmail} へ集計レポートを送信します`);
console.log(`集計結果: ユーザー数=${reportData.totalUsers}, 売上=${reportData.revenue}`);
return { sent: true };
});
console.log(`[ワークフロー完了] レポート ${reportId} の全パイプラインが正常終了しました`);
}
}
// 2. HTTPエンドポイント(Webからワークフローをキックする場合)
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const url = new URL(req.url);
if (url.pathname === '/trigger') {
const instance = await env.REPORT_WORKFLOW.create({
params: {
reportId: `REP_${Date.now()}`,
recipientEmail: 'boss@example.com',
},
});
return Response.json({
message: 'ワークフローを起動しました',
instanceId: instance.id,
});
}
return new Response('Use /trigger to start workflow');
}
};

Step 3: ローカル開発環境での動作確認

ローカルサーバーを起動します。

Terminal window
npx wrangler dev

別ターミナルからワークフローを起動します。

Terminal window
curl http://localhost:8787/trigger

レスポンス例:

{
"message": "ワークフローを起動しました",
"instanceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

サーバーのターミナルログに、各ステップが順序通り、10秒のスリープを挟んで実行される様子がリアルタイムに表示されます。


Step 4: 本番デプロイとCLIによる運用追跡

Terminal window
# 本番へデプロイ
npx wrangler deploy
# 1. 登録済みワークフロー一覧を確認
npx wrangler workflows list
# 2. CLIから直接パラメータを渡して手動実行
npx wrangler workflows trigger report-pipeline \
--params '{"reportId":"MANUAL_01","recipientEmail":"admin@example.com"}'
# 3. 実行状況(実行中 / 完了 / ステップ進捗)を確認
npx wrangler workflows describe report-pipeline <INSTANCE_ID>

まとめ

  • ステップ分割(step.do: 処理が分割され、各ステップの戻り値が永続化されます。
  • 長期間スリープ(step.sleep: 数時間〜数日間の待機でもサーバー費用は0円。
  • CLI操作: wrangler workflows コマンドでCLIからワンライナーでテスト・ステータス確認が可能。

💡 用語解説コラム

[!NOTE] 耐久性実行 (Durable Execution)
コードの途中でサーバー停止や障害が起きても、完了済みのステップの状態を保持し、自動的に直前のステップから再開・再試行される実行アーキテクチャ。

[!NOTE] 指数バックオフ (Exponential Backoff)
エラー時の再試行間隔を1秒、2秒、4秒、8秒…と倍々に延ばしていく手法。障害中の相手サーバーへの負荷集中を防ぎます。