Saga オーケストレーションパターン - AWS 規範ガイダンス

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

Saga オーケストレーションパターン

Intent

Saga オーケストレーションパターンでは、セントラルコーディネーター (オーケストレーター) を使用して、複数のサービスにまたがる分散トランザクションのデータ完全性を維持します。分散トランザクションでは、トランザクションが完了する前に複数のサービスを呼び出すことができます。異なるデータストアにサービスがデータを保存する場合、これらのデータストア間でデータ整合性を維持するのが難しい場合があります。

導入する理由

トランザクションとは、複数のステップを含む可能性のある単一の作業単位です。すべてのステップが完全に実行されるか、まったく実行されないため、データストアは整合した状態を保持します。アトミック性、整合性、分離性、耐久性 (ACID) という用語によって、トランザクションの特性が定義されます。リレーショナルデータベースは ACID トランザクションを提供してデータ整合性を維持します。

トランザクションの整合性を維持するため、リレーショナルデータベースでは 2 フェーズコミット (2PC) 方式を使用します。これは準備フェーズとコミットフェーズで構成されます。

  • 準備フェーズでは、調整プロセスはトランザクションを構成するプロセス (構成プロセス) に、トランザクションをコミットするかロールバックするかを約束するように要求します。

  • コミットフェーズでは、調整プロセスが構成プロセスにトランザクションのコミットを要求します。準備フェーズで構成プロセスがコミットに同意できない場合、トランザクションはロールバックされます。

database-per-service 設計パターン に従う分散システムでは、2 フェーズコミットはオプションではありません。これは、各トランザクションがさまざまなデータベースに分散され、リレーショナルデータストアの 2 フェーズコミットと同様にプロセスを調整できる単一のコントローラーが存在しないためです。この場合の解決策の 1 つは、Saga オーケストレーションパターンを使用することです。

適用対象

Saga オーケストレーションパターンは次の場合に使用します。

  • 複数のデータストアにまたがる分散トランザクションのデータ完全性と整合性がシステムに求められる場合。

  • データストアに ACID トランザクションを実行するための 2PC が用意されていないため、アプリケーションの境界内で 2PC を実装する作業が複雑な場合。

  • NoSQL データベース (ACID トランザクションが用意されていない) があり、1 つのトランザクションで複数のテーブルを更新する必要がある場合。

問題点と考慮事項

  • 複雑さ: 補償トランザクションや再試行によってアプリケーションコードが複雑になり、メンテナンスのオーバーヘッドが発生する可能性があります。

  • 結果整合性: ローカルトランザクションを順次処理することで結果整合性が保たれます。これは、強い整合性を必要とするシステムでは課題となることがあります。この問題を解決するには、整合性モデルに対するビジネスチームの期待を設定するか、強固な整合性を備えたデータストアに切り替えます。

  • 冪等性: Saga を構成するサービスには、予期しないクラッシュやオーケストレーターの障害によって一時的な障害が発生した場合に繰り返し実行できるように冪等性が必要です。

  • トランザクションの分離: Saga にはトランザクションの分離機能がありません。トランザクションの同時オーケストレーションによって、最新でないデータが発生する可能性があります。このようなシナリオに対処するために、セマンティックロックを使用することをお勧めします。

  • オブザーバビリティ: オブザーバビリティとは、実行とオーケストレーションのプロセスにおける問題をトラブルシューティングするための、詳細なログ記録とトレースを指します。これは、Saga を構成するサービスが増え、デバッグが複雑になる場合に重要になります。

  • レイテンシーの問題: Saga が複数のステップで構成されている場合、補償トランザクションによって全体の応答時間にレイテンシーが加わることがあります。このような場合は、同期呼び出しを避けてください。

  • 単一障害点: オーケストレーターはトランザクション全体を調整するので、単一障害点になる可能性があります。この問題から、Saga コレオグラフィパターンが望ましい場合もあります。

実装

高レベルのアーキテクチャ

以下のアーキテクチャ図では、Saga オーケストレーターは、注文サービス、在庫サービス、支払いサービスの 3 つで構成されています。トランザクションを完了するには、T1、T2、T3 の 3 ステップが必要です。Saga オーケストレーターは手順を認識し、必要な順序で実行します。ステップ T3 に失敗 (支払い失敗) すると、オーケストレーターは補償トランザクション C1 および C2 を実行してデータを初期状態に戻します。

Saga オーケストレーターの概要レベルのアーキテクチャ

トランザクションが複数のデータベースに分散されている場合、AWS Step Functions を使用して Saga オーケストレーションを実装できます。

AWS のサービスを使用した実装

サンプルソリューションでは、Step Functions の標準ワークフローを使用して Saga オーケストレーションパターンを実装しています。

Saga ワークフローを Step Functions で実装

顧客が API を呼び出すと、Lambda 関数が呼び出され、Lambda 関数で前処理が行われます。この関数は Step Functions ワークフローを開始し、分散トランザクションの処理を開始します。前処理が不要な場合は、Lambda 関数を使用せずに API Gateway から直接 Step Functions ワークフローを開始できます。

Step Functions を使用すると、Saga オーケストレーションパターンの実装に内在する単一障害点の問題が軽減されます。Step Functions には耐障害性が組み込まれており、各 AWS リージョンの複数のアベイラビリティーゾーンにわたってサービス容量を維持して、個々のマシンやデータセンターの障害からアプリケーションを保護します。これにより、サービス自体とサービスが運用するアプリケーションワークフローの両方の高可用性が確保されます。

Step Functions ワークフロー

Step Functions ステートマシンでは、パターン実装の意思決定ベースの制御フロー要件を設定できます。Step Functions ワークフローは、注文、在庫更新、支払い処理のための個々のサービスを呼び出してトランザクションを完了し、さらに処理を進めるためにイベント通知を送信します。Step Functions ワークフローは、トランザクションを調整するオーケストレーターとして機能します。ワークフローにエラーがある場合、オーケストレーターは補償トランザクションを実行して、サービス間でデータの完全性が維持されるようにします。

次の図は、Step Functions ワークフロー内で実行される手順を示します。Place OrderUpdate InventoryMake Payment の各ステップは成功した場合の径路を示しています。注文が行われ、在庫が更新され、支払いが処理されてから、呼び出し元に Success 状態が返されます。

Revert PaymentRevert InventoryRemove Order の各 Lambda 関数は、ワークフローのいずれかのステップが失敗したときにオーケストレーターが実行する補償トランザクションを示しています。ワークフローが Update Inventory ステップで失敗した場合、オーケストレーターは Revert Inventory および Remove Order のステップを呼び出してから、呼び出し元に Fail 状態を返します。これらの補償トランザクションにより、データの完全性を確実に維持できます。在庫は元のレベルに戻り、注文は元に戻されます。

Saga Step Functions ワークフロー

「サンプルコード」

以下のサンプルコードは、Step Functions を使用して Saga オーケストレーターを作成する方法を示しています。完全なコードを表示するには、この例のGitHubリポジトリを参照してください。

タスク定義

var successState = new Succeed(this,"SuccessState"); var failState = new Fail(this, "Fail"); var placeOrderTask = new LambdaInvoke(this, "Place Order", new LambdaInvokeProps { LambdaFunction = placeOrderLambda, Comment = "Place Order", RetryOnServiceExceptions = false, PayloadResponseOnly = true }); var updateInventoryTask = new LambdaInvoke(this,"Update Inventory", new LambdaInvokeProps { LambdaFunction = updateInventoryLambda, Comment = "Update inventory", RetryOnServiceExceptions = false, PayloadResponseOnly = true }); var makePaymentTask = new LambdaInvoke(this,"Make Payment", new LambdaInvokeProps { LambdaFunction = makePaymentLambda, Comment = "Make Payment", RetryOnServiceExceptions = false, PayloadResponseOnly = true }); var removeOrderTask = new LambdaInvoke(this, "Remove Order", new LambdaInvokeProps { LambdaFunction = removeOrderLambda, Comment = "Remove Order", RetryOnServiceExceptions = false, PayloadResponseOnly = true }).Next(failState); var revertInventoryTask = new LambdaInvoke(this,"Revert Inventory", new LambdaInvokeProps { LambdaFunction = revertInventoryLambda, Comment = "Revert inventory", RetryOnServiceExceptions = false, PayloadResponseOnly = true }).Next(removeOrderTask); var revertPaymentTask = new LambdaInvoke(this,"Revert Payment", new LambdaInvokeProps { LambdaFunction = revertPaymentLambda, Comment = "Revert Payment", RetryOnServiceExceptions = false, PayloadResponseOnly = true }).Next(revertInventoryTask); var waitState = new Wait(this, "Wait state", new WaitProps { Time = WaitTime.Duration(Duration.Seconds(30)) }).Next(revertInventoryTask);

ステップ関数とステートマシンの定義

var stepDefinition = placeOrderTask .Next(new Choice(this, "Is order placed") .When(Condition.StringEquals("$.Status", "ORDER_PLACED"), updateInventoryTask .Next(new Choice(this, "Is inventory updated") .When(Condition.StringEquals("$.Status", "INVENTORY_UPDATED"), makePaymentTask.Next(new Choice(this, "Is payment success") .When(Condition.StringEquals("$.Status", "PAYMENT_COMPLETED"), successState) .When(Condition.StringEquals("$.Status", "ERROR"), revertPaymentTask))) .When(Condition.StringEquals("$.Status", "ERROR"), waitState))) .When(Condition.StringEquals("$.Status", "ERROR"), failState)); var stateMachine = new StateMachine(this, "DistributedTransactionOrchestrator", new StateMachineProps { StateMachineName = "DistributedTransactionOrchestrator", StateMachineType = StateMachineType.STANDARD, Role = iamStepFunctionRole, TracingEnabled = true, Definition = stepDefinition });

GitHub リポジトリ

このパターンのサンプルアーキテクチャの完全な実装については、https://github.com/aws-samples/saga-orchestration-netcore-blog の GitHub リポジトリを参照してください。

ブログの参考情報

関連情報

動画

次の動画では、 を使用して Saga オーケストレーションパターンを実装する方法について説明します AWS Step Functions。