Cloudflare WorkflowsがSaga Rollbackを実装 — 手動補償ロジックはもう不要
CloudflareがWorkflows向けにSaga Rollbackをリリース。各step.do()呼び出しにインラインで補償ロジックを定義可能に。手動のcatchブロックや逆順のアンドゥコードはもう不要。

Cloudflare WorkflowsがSagaパターンをネイティブでサポートするようになった。各ステップをtry-catchでラップし、何をアンドゥするかを手動で追跡する代わりに、rollback関数をstep.do()のオプションとして渡せる。後続のステップが失敗すると、Workflowsはステップ開始順の逆順でロールバックハンドラを実行する — 分散トランザクションにまさに必要な機能だ。
Saga Rollbackが重要な理由
長時間実行されるワークフローは、しばしば複数の外部システムに触れる。資金移動では、口座Aから引き落とし、口座Bに入金し、通知を送信するかもしれない。入金が失敗した場合、単に引き落としを元に戻すだけでは不十分で、補償アクション(逆入金)が必要になる。これがSagaパターンだ。今回のリリース以前は、開発者はこの補償を手動で実装し、どのステップが成功したかを追跡し、catchブロックに逆順のロジックを書く必要があった。エラーが発生しやすく、冗長だった。
今では次のように書ける:
await step.do("debit-bank-a", () => bankA.debit(from, amount), {
rollback: async ({ output }) => bankA.credit(from, amount, output.id),
});
await step.do("credit-bank-b", () => bankB.credit(to, amount), {
rollback: async ({ output }) => bankB.debit(to, amount, output.id),
});
await step.do("notify", () => notifyBoth(from, to, amount));ロールバックハンドラは前方操作のすぐ隣に配置される。別途catchブロックは不要、手動の順序付けも不要。残りはWorkflowsが処理する。
主要な設計判断
チームはfluent API(step.do(...).rollback(...))も検討したが、却下した。問題は、step.do()がPromiseを返し、WorkersがPromiseパイプライン(将来の値が解決される前にそのメソッドを呼び出せるCap'n Protoの機能)をサポートしていることだ。fluent APIだと、.rollback()がステップ定義の一部なのか、出力に対するメソッドなのかが曖昧になる。また、ステップの実行が遅延する — Workflowsは.rollback()がアタッチされるかどうかを確認するために待つ必要が生じる。現在のオプションベースのAPIは、ステップの実行を即時かつ予測可能に保つ。
ロールバックは、ワークフローが致命的に失敗しようとしている場合にのみトリガーされる。エラーをキャッチして続行する場合、ロールバックは実行されない。失敗したステップ自体も、ハンドラを登録していればロールバックの対象となる — ステップが失敗する前に外部システムと部分的にやり取りしている可能性があるため重要だ。ロールバックハンドラはoutput(undefinedの場合もある)を受け取るため、何をアンドゥするかを判断できる。
並列ステップの場合、ロールバック順序は完了順ではなく、ステップ開始順の逆順を使用する。これにより、ステップが開始順とは異なる順序で完了した場合の曖昧さを回避できる。
結論
これは、一般的な分散システムパターンに対する堅牢で実用的なAPIだ。Cloudflare Workers上でマルチステップワークフローを構築しているなら、Sagaの実装はずっと楽になる。ドキュメントに示されている冪等性キーパターンは必ず従うべきだ — 前方操作とロールバック操作の両方を冪等にし、リトライ時の二重適用を防ぐこと。
ディスカッション
0 件のコメント
最初のコメントを投稿しましょう。