要点: 複数サービスにまたがる業務では、共有ロックではなく、再開できるワークフローが信頼性を作ります。
この記事の用語: Saga:複数ステップと補償処理からなるワークフロー。補償処理:完了済みステップの影響を業務的に取り消す処理。
現場の課題
決済、在庫確保、配送は一つのDBトランザクションにできません。応答を失っても処理済みの場合があり、続行、再試行、補償のどれを行うか判断できる設計が必要です。
単純な対策だけでは不十分な理由
共通トランザクションがないため、各ステップは応答を失っても成功済みかもしれません。Sagaは状態を保存し、再試行可能な操作と補償処理、人の判断が必要な状態を区別します。
処理フロー
ワークフロー開始→状態機械
Transition永続化→ステップ
冪等 呼び出し→補償処理
業務復旧
アーキテクチャ上の判断
重要FlowをOrchestrate
Sequence、期限、Support 操作を一元可視化します。
Act前にPersist
ステップ前にTransition Intentを保存します。
Semantic 冪等性
再試行 リクエスト IDではなくOrderや操作IDを使います。
さらに深く考える
状態機械はProduct モデル
各Stateの顧客向け意味をSupport、Financeと共有します。
Human Workも設計対象
Manual Taskに担当者、期限、検証結果、冪等 Resumeを持たせます。
実装手順
- 各ステップを実装する前に、業務状態、期限、終了条件を定義します。
- ステップごとに安定した操作IDを持たせ、再試行時は既存結果を照会します。
- 補償処理も独立した業務操作として、状態と監査ログを管理します。
コード例: Durable Order ワークフロー
workflow.save(state = 'RESERVING', version = 4)
reservation = inventory.reserve(orderId, operationId)
workflow.save(state = 'CHARGING', reservationId)
charge = payments.charge(orderId, operationId)
workflow.save(state = 'CONFIRMED', chargeId)再実行時は操作IDで結果を取得し、時刻など非決定値を未記録で再計算しません。
想定しておく障害
- 決済成功レスポンスが失われます。
- 補償処理が失敗し人手判断が必要です。
- 二つのワークフローが同じ業務上の集約を競合します。
監視すべきこと
| 指標 | 何が分かるか |
|---|---|
| State別ワークフロー Age | 停止業務フローを検出します。 |
| ステップ 試行・遅延 | 依存サービス不安定を示します。 |
| 補償処理・Manual率 | 例外コストを示します。 |
設計の検証方法
- 成功後レスポンスを落とし収束を確認します。
- 全Persist Stateで再起動します。
- 補償処理失敗時のアラートと手動解決を確認します。
本番導入の進め方
新ワークフローを既存経路の横で観測だけに使い、履歴を比較します。ステップを一つずつ移し、重複、サービス停止、再起動の訓練が通るまで照合レポートを残します。
本番前チェックリスト
- HistoryはDurableで再実行は決定的です。
- TimeoutはBusiness 期限を表します。
- Operatorが安全に再試行・Compensateできます。
まとめ
良いSagaは途中状態を見えるようにし、再開できます。分散システムを原子的に見せかけません。
