# Cloudflare Workflows がサガ・ロールバック対応！途中で失敗しても自動で巻き戻せるよ！

こんにちは、しぃちゃんだよ！今日はね、途中で失敗しても安心な「お片付けのしくみ」のお話だよ。Cloudflare Workflows に、失敗したときの巻き戻しを書けるサガ（saga）ロールバックがやってきたの。エンジニアさんが「どうやって作ったか」まで語ってくれる、じっくり読みたい一本だよ！

## なにが発表されたの？

Cloudflare の Blog（エンジニアリング記事）で、Cloudflare Workflows がサガ方式のロールバックに対応したことが紹介されたよ。

Cloudflare Workflows は、複数ステップのアプリを動かす「durable execution（持続実行）エンジン」。各ステップは外部システムを呼んだり、失敗をリトライしたり、再起動をまたいで状態を保存できるんだって。

今回のアップデートで、ひとつひとつの `step.do()` に「失敗したときに実行する打ち消し処理（compensating action）」を指定できるようになったよ。しかもこの記事は「どうやって実装したか」を語るエンジニアリングの読み物で、中身が濃いの！

## 今までどうだったの？

これまでは、途中のステップが失敗したときの後始末を、開発者が自分で書く必要があったの。

記事の例がわかりやすくてね。銀行 A から引き落として、銀行 B に入金して、確認メールを送る、という送金フローを考えてみて。もし B への入金が失敗したら、A の引き落としは自動では戻らないよね。お金が宙ぶらりんになっちゃう。

だから開発者は「どこまで成功したか」を try-catch で追いかけて、巻き戻しの順番を手で管理して……というのを、ステップの定義とは別の場所で組み立てる必要があったの。原文でも、補償ロジックをステップの外側で自前実装しないといけなかった、と説明されているよ。

## これで何が変わるの？

これからは、`step.do()` にロールバック用の関数を一緒に渡しておくだけ。ワークフローが最終的に失敗したら、Workflows が登録済みの打ち消し処理を自動で呼んでくれるの。

うれしいのは、ロールバック処理も通常のステップと同じしくみに乗ること。リトライもタイムアウトもログも、ちゃんと効くんだよ！補償ロジックが「アプリのコードの外にある見えない配線」じゃなくて、ステップの定義のすぐ隣に書けるようになるのが大きいね。

## 深く潜ってみよう

しくみをもう少し細かく見てみるね。

**API のかたち**

ロールバック関数は、`step.do()` にオプションとして渡すよ。

```javascript
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` に書けるよ。

```javascript
{
  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 対応も予定

複数ステップの処理で、お金や在庫みたいに「途中で失敗すると困る」ものを扱っている人には、とっても心強いアップデートだよ！
