Cloudflare Workflows がサガ・ロールバック対応!途中で失敗しても自動で巻き戻せるよ!
こんにちは、しぃちゃんだよ!今日はね、途中で失敗しても安心な「お片付けのしくみ」のお話だよ。Cloudflare Workflows に、失敗したときの巻き戻しを書けるサガ(saga)ロールバックがやってきたの。エンジニアさんが「どうやって作ったか」まで語ってくれる、じっくり読みたい一本だよ!
Cloudflare Blog
なにが発表されたの?
Cloudflare の Blog(エンジニアリング記事)で、Cloudflare Workflows がサガ方式のロールバックに対応したことが紹介されたよ。
Cloudflare Workflows は、複数ステップのアプリを動かす「durable execution(持続実行)エンジン」。各ステップは外部システムを呼んだり、失敗をリトライしたり、再起動をまたいで状態を保存できるんだって。
今回のアップデートで、ひとつひとつの step.do() に「失敗したときに実行する打ち消し処理(compensating action)」を指定できるようになったよ。しかもこの記事は「どうやって実装したか」を語るエンジニアリングの読み物で、中身が濃いの!
今までどうだったの?
これまでは、途中のステップが失敗したときの後始末を、開発者が自分で書く必要があったの。
記事の例がわかりやすくてね。銀行 A から引き落として、銀行 B に入金して、確認メールを送る、という送金フローを考えてみて。もし B への入金が失敗したら、A の引き落としは自動では戻らないよね。お金が宙ぶらりんになっちゃう。
だから開発者は「どこまで成功したか」を try-catch で追いかけて、巻き戻しの順番を手で管理して……というのを、ステップの定義とは別の場所で組み立てる必要があったの。原文でも、補償ロジックをステップの外側で自前実装しないといけなかった、と説明されているよ。
これで何が変わるの?
これからは、step.do() にロールバック用の関数を一緒に渡しておくだけ。ワークフローが最終的に失敗したら、Workflows が登録済みの打ち消し処理を自動で呼んでくれるの。
うれしいのは、ロールバック処理も通常のステップと同じしくみに乗ること。リトライもタイムアウトもログも、ちゃんと効くんだよ!補償ロジックが「アプリのコードの外にある見えない配線」じゃなくて、ステップの定義のすぐ隣に書けるようになるのが大きいね。
深く潜ってみよう
しくみをもう少し細かく見てみるね。
API のかたち
ロールバック関数は、step.do() にオプションとして渡すよ。
await step.do(
"debit-bank-a",
() => bankA.debit(from, amount),
{
rollback: async ({ output }) =>
bankA.credit(from, amount, output.id),
}
);
ロールバックのハンドラは、失敗を引き起こしたエラー・ステップのコンテキスト・そのステップの出力(output)を受け取るよ。ステップが結果を保存する前に落ちた場合、output は undefined になるんだって。
どのステップが対象?
開始済み、または完了済みで、ロールバックハンドラを持つステップが対象。失敗したステップ自身も、ハンドラを登録していれば対象になるよ。実行順は「ステップを開始した順の逆」で、完了した順ではないのがポイント。
いつ動くの?
ロールバックが走るのは、ワークフローそのものが終端的に失敗したときだけ。ユーザーのコードがエラーを catch して処理を続けた場合は動かないよ。
冪等性は大事
ロールバック関数は、通常のステップと同じく冪等(idempotent)であるべき。決済プロバイダの冪等キーを使うなど、何度呼ばれても安全にしておこうね。原文はこう言っているよ。
Rollback functions should be idempotent, just like regular Workflow steps.
専用の設定
ロールバックだけのリトライやタイムアウトは rollbackConfig に書けるよ。
{
rollback: async ({ output }) => { /* ... */ },
rollbackConfig: {
retries: { limit: 10, delay: "30 seconds", backoff: "exponential" },
timeout: "2 minutes",
},
}
裏側のしくみ
Workflows は、各ステップが補償ロジックを登録したかどうかを、durable な step history(ステップ履歴)に記録しているの。ロールバックのときは、この永続化された記録から「何が起きたか」を判断するよ。エンジンが再起動しても、replay モードで forward(前向き)のステップを再実行せずにハンドラを組み直せるんだって。賢いね!
失敗したらどうなる?
ロールバックのハンドラがリトライ上限まで使っても失敗した場合、Workflows はそのロールバックを failed として記録して、残りのハンドラを止めて、ワークフローは Errored 状態で終わるよ。
これからの予定
waitForEvent のロールバック対応、並列でのロールバック実行、そして Python 版 Workflows への対応が予定されているみたい。
まとめ
- Cloudflare Workflows が、ステップごとに打ち消し処理を書けるサガ・ロールバックに対応したよ
step.do()にロールバック関数を渡すだけで、ワークフロー失敗時に自動で巻き戻してくれる- 実行はステップ開始の逆順。対象は開始済み/完了済みでハンドラを持つステップ
- ロールバックも通常ステップと同じくリトライ・タイムアウト・ログが効く。関数は冪等にしておこう
- ロールバック失敗時はワークフローが
Errored状態に。今後は waitForEvent・並列実行・Python 対応も予定
複数ステップの処理で、お金や在庫みたいに「途中で失敗すると困る」ものを扱っている人には、とっても心強いアップデートだよ!