MENU

サガパターン(Saga Pattern)入門|マイクロサービスの分散トランザクション管理をAWSで実装する実践ガイド

マイクロサービスに移行した後、「分散トランザクションをどう扱うか」で悩んでいませんか?オンプレのモノリシックなシステムであれば、RDBMSのACIDトランザクションで一発解決できていたものが、サービスを分割した途端に一気に難しくなります。注文サービスが成功して在庫サービスが失敗したとき、どうやってデータの整合性を保てばいいのか。「2フェーズコミットを使えば済む話では?」と考えた方も多いと思いますが、クラウドのマイクロサービス環境ではほとんど採用できません。

この記事では、分散トランザクションの現実的な解法として現在主流になっているSagaパターンについて、コレオグラフィ型・オーケストレーション型の違い、AWSでの具体的な実装手順(AWS Step Functions活用)、設計上のアンチパターンまでを現場目線で整理します。

目次

なぜSagaパターンが必要なのか?(分散トランザクションの背景)

オンプレのシステムでは、1本のRDBMSに対してBEGIN→COMMIT/ROLLBACKを発行することで、複数テーブルの操作を原子的に扱えました。ところがマイクロサービスでは、サービスごとにデータストアを持つ「データの独立性」が基本設計になります。それぞれ別のDBを持つサービスをまたいで従来のACIDトランザクションを張ることは、物理的にほぼ不可能です。

古い解法として「2フェーズコミット(2PC)」があります。分散コーディネーターが各参加者に「コミットしていい?」と確認し、全員OKなら一斉コミット、誰か一人でもNGならロールバックさせる仕組みです。

しかし2PCには致命的な弱点があります。
ロック時間が長い: フェーズ1でリソースをロックしたまま待機するため、高トラフィック時に詰まる
コーディネーター障害で止まる: コーディネーターがクラッシュすると参加者がロックされたまま宙ぶらりになる
クラウドサービスが非対応: Amazon DynamoDB・AWS Lambda・Amazon SQSなど現代のクラウドサービスは2PCに対応していない

Sagaパターンは「トランザクションを最初から小さく分割し、失敗したら補償トランザクション(Compensating Transaction)で巻き戻す」という考え方でこの問題を解決します。結果整合性(Eventual Consistency)で動くため、ロックを長時間保持する必要がなく、マイクロサービスに適しています。

Sagaパターンの2つの実装方式

Sagaパターンには、サービス間の連携方法によって大きく2種類の実装があります。

1. コレオグラフィ型(Choreography-based Saga)

各サービスがイベントを発行し合い、次のサービスがそのイベントを受け取って処理を進める方式です。中央の調整役は存在せず、サービス同士がイベントバスを介して自律的に動きます。

工場のベルトコンベアで各作業員が前工程の完了サインを受けて次の作業を始める、というイメージに近いです。

コレオグラフィ型の流れ(注文フローの例)
・注文サービス → 「注文受付完了」イベントを発行
・在庫サービス → 「注文受付完了」を受信 → 在庫引当 → 「在庫引当完了」イベントを発行
・決済サービス → 「在庫引当完了」を受信 → 決済処理 → 「決済完了」イベントを発行
・配送サービス → 「決済完了」を受信 → 配送手配開始

AWSでの実装には Amazon EventBridge または Amazon SQS + Amazon SNS を組み合わせます。

コレオグラフィ型のメリット・デメリット

観点 メリット デメリット
疎結合性 サービスが互いを直接知らない 全体の流れが追いにくい
スケーラビリティ サービス追加が容易 障害時のデバッグが難しい
実装コスト 中央コーディネーター不要 Saga全体フローを把握しにくい

2. オーケストレーション型(Orchestration-based Saga)

中央のオーケストレーター(司令塔)がSagaの全体フローを管理し、各サービスに「次にこれをやれ」と命令しながら進める方式です。

AWSでは AWS Step Functions がオーケストレーターとして最適です。ステートマシンとしてSagaの全フローを定義し、各ステップ(Lambda関数やAmazon ECSタスクなど)を順番に呼び出します。失敗時の補償トランザクションもStep Functionsのエラーハンドリングとして記述できます。

オーケストレーション型のメリット・デメリット

観点 メリット デメリット
可視性 全体フローが一目でわかる オーケストレーターへの依存が生まれる
デバッグ 失敗箇所がすぐ特定できる オーケストレーターが単一障害点になりうる
管理 フロー変更が一箇所で完結 サービス間の独立性がやや下がる

サービス数が5以上になるか、複雑な補償ロジックが必要な場合はオーケストレーション型のほうが圧倒的に運用しやすいです。サービス数が少なくシンプルなフローであればコレオグラフィ型も選択肢になります。

AWSでのSaga実装(AWS Step Functionsを使ったオーケストレーション)

注文処理Sagaを例に、AWS Step Functionsでの実装手順を解説します。

1. ステートマシン設計

Step FunctionsのAmazon States Language(ASL)でSagaのフローを記述します。注文→在庫引当→決済→配送の順に進み、失敗したら逆順で補償トランザクションを実行します。

# AWS Step Functions - Sagaステートマシン定義(抜粋) { "Comment": "Order Saga Orchestrator", "StartAt": "ReserveInventory", "States": { "ReserveInventory": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:reserve-inventory", "Next": "ProcessPayment", "Catch": [ { "ErrorEquals": ["InventoryNotAvailable"], "Next": "CancelOrder" } ] }, "ProcessPayment": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:process-payment", "Next": "ArrangeShipping", "Catch": [ { "ErrorEquals": ["PaymentFailed"], "Next": "ReleaseInventory" } ] }, "ReleaseInventory": { "Type": "Task", "Comment": "補償トランザクション: 在庫を元に戻す", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:release-inventory", "Next": "CancelOrder" }, "CancelOrder": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:cancel-order", "End": true }, "ArrangeShipping": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:arrange-shipping", "End": true } } }

2. 補償トランザクションの実装

Sagaパターンで最も重要なのが補償トランザクションの設計です。各ステップには必ず「元に戻す処理」を対で用意します。

補償トランザクションの対応関係

通常処理 補償トランザクション
在庫引当(ReserveInventory) 在庫解放(ReleaseInventory)
決済処理(ProcessPayment) 決済取消・返金(RefundPayment)
配送手配(ArrangeShipping) 配送キャンセル(CancelShipping)

補償トランザクションは「元の状態に戻す」のではなく「現在の状態を前向きに巻き戻す」操作であることに注意が必要です。在庫引当の補償は「引当前の在庫数を復元」ではなく「引当済みフラグを解除して在庫数を加算する」という積極的な操作です。

3. 冪等性(Idempotency)の確保

Sagaの各ステップは、同じリクエストが複数回届いても結果が変わらない「冪等性」を持たせる必要があります。ネットワーク障害などで同じメッセージが2回届くことは十分あり得るためです。

# Lambda関数での冪等性実装例(AWS SDK for Python) import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('saga-idempotency-keys') def lambda_handler(event, context): idempotency_key = event['idempotencyKey'] # 処理済みチェック response = table.get_item(Key={'idempotencyKey': idempotency_key}) if 'Item' in response: # すでに処理済みなら保存済みの結果を返すだけ return response['Item']['result'] # 実際の在庫引当処理 result = reserve_inventory(event['orderId'], event['items']) # 処理結果をDynamoDBに保存(TTL付き: 24時間後に自動削除) table.put_item(Item={ 'idempotencyKey': idempotency_key, 'result': result, 'ttl': 86400 }) return result

料金の仕組み(AWS Step Functions)

AWS Step Functionsの料金体系は2種類のワークフロータイプによって異なります(2026年6月時点)。

ワークフロータイプ 料金単位 東京リージョン(ap-northeast-1)単価 向いているSagaの用途
Standard Workflows 状態遷移1回あたり $0.000025/遷移 注文・決済など長時間Saga(最大1年)
Express Workflows 実行回数 + 実行期間 $1.00/100万実行 + $0.00001667/GB-秒 高頻度・短時間Saga(最大5分)

注文Sagaのような業務フローでは通常 Standard Workflows を使います。1回の注文Sagaが10ステップの状態遷移で構成されるとすると、1回あたり$0.00025(約0.04円)です。月1万件の注文なら料金は2.5ドル程度と非常に安価です。

各ステップでAWS Lambda関数を呼び出す場合は、Lambda側の料金も加算されます。Lambda料金は実行時間とメモリ量で決まるため、補償トランザクション関数も含めてLambdaの実行コストを合算して見積もります。

設計上の注意点とアンチパターン

Sagaパターンを正しく使うために、よくある失敗パターンを整理します。

① 補償できないトランザクションに注意
すべての操作が補償可能とは限りません。例えば「メール通知の送信」はすでに届いたメールをなかったことにできません。このような操作はSagaの最後に置くか、べき等性の高い設計(「通知済みフラグを確認してからのみ送信」など)に変更することを検討します。

② ピボット操作を正しく特定する
Sagaの途中には「ここを過ぎたら補償できない」という「ピボット操作」が存在します。注文Sagaでは「配送会社への手配確定」がそれに当たります。ピボット操作より前は補償で巻き戻せますが、後ろは基本的に巻き戻せないため、設計時に明示しておく必要があります。

③ 分離問題(ダーティリード)への対処
Sagaが実行中(まだ完了していない)の状態でも、他のサービスがその中間状態を参照できてしまいます。在庫引当直後に在庫数がゼロになった状態を他のユーザーが見てしまう、いわゆる「ダーティリード」です。セマンティックロック(処理中フラグ)や楽観的ロックで対策します。

④ Sagaを2PCの代替として乱用しない
Sagaは結果整合性のモデルです。厳密な強整合性(Strong Consistency)が求められる金融系トランザクションに安易に使うと、ビジネス要件を満たせない場合があります。「補償では戻せない操作かどうか」の洗い出しを、必ず設計段階でやります。

よくあるトラブルと対処法

補償トランザクション自体が失敗した場合
補償トランザクション自体が失敗するケースは見落とされがちです。Step Functionsでは Retry(再試行回数と間隔)を補償ステップにも設定し、最終的に失敗した場合は Amazon SNS でアラートを飛ばしてオペレーターが手動介入できるようにします。

Step Functionsの実行履歴が消えた
Standard Workflowsは実行履歴を90日間保持しますが、Express Workflowsは Amazon CloudWatch Logs に自分でエクスポートしないと消えます。Sagaのデバッグには実行ログが必須なので、Express Workflowsを使う場合はCloudWatch Logsへのログ出力を必ず有効化します。

Lambda関数のタイムアウトとSagaの整合性
AWS Lambdaのデフォルトタイムアウトは3秒です。Step FunctionsのタスクステートにHeartbeatSecondsを設定して「Lambda側からまだ動いていると知らせる」仕組みを使わないと、Step Functionsが勝手にタイムアウト扱いにして補償フローを始めてしまうことがあります。

本記事のまとめ

Sagaパターンのポイントを整理します。

項目 コレオグラフィ型 オーケストレーション型
中央管理 なし(イベントで連携) あり(Step Functionsなど)
可視性 低い(全体が追いにくい) 高い(フロー全体が一目瞭然)
向いている規模 シンプルなフロー・少数サービス 複雑なフロー・多数サービス
AWSでの実装 Amazon EventBridge / SQS + SNS AWS Step Functions

Sagaパターンを正しく実装するには「補償トランザクションの設計」「冪等性の確保」「ピボット操作の特定」の3点が核心です。オンプレ時代にRDBMSのトランザクション管理に慣れていたエンジニアほど、この「補償で戻す」という発想の転換に戸惑いますが、一度理解してしまえばマイクロサービス設計の基本パターンとして繰り返し使えます。

クラウド上のLinuxサーバー構築については、姉妹サイトLinuxMaster.JPで詳しく解説しています。

マイクロサービスの分散設計、もっと体系的に学びたいですか?

Sagaパターンをはじめとするクラウドネイティブなアーキテクチャ設計は、一冊の本を読んだだけでは現場で使えるようになりません。
オンプレの経験を活かしながら、現場で使えるクラウドスキルを体系的に身につけたい方へ、メルマガで実践的なクラウド活用ノウハウをお届けしています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次