複数のサービス間で一連のローカル トランザクションを調整することで、分散システムのデータ整合性を維持します。 各サービスは、その操作を実行し、イベントまたはメッセージを介して次のステップをトリガーします。 ステップが失敗した場合、一連の補正トランザクションによって、完了した手順の変更が元に戻されます。
コンテキストと問題
トランザクション は、複数の操作を含めることができる作業単位を表します。 トランザクション内では、イベント は、エンティティに影響を与える状態変更を参照します。 コマンド は、アクションの実行または後続のイベントのトリガーに必要なすべての情報をカプセル化します。
トランザクションは、原子性、一貫性、分離性、持続性 (ACID) の原則に従う必要があります。
- アトミック性: すべての操作が成功するか、操作が成功しなかったか。
- 整合性: データは有効な状態から別の有効な状態に切り替わります。
- 分離: 同時実行トランザクション、シーケンシャル トランザクションと同じ結果が得られます。
- 持続性: 変更は、エラーが発生した場合でも、コミット後も保持されます。
1 つのサービスでは、トランザクションは 1 つのデータベース内で動作するため、ACID の原則に従います。 ただし、複数のサービスで ACID コンプライアンスを実現する方が複雑になる場合があります。
マイクロサービス アーキテクチャの課題
マイクロサービス アーキテクチャは、通常、各マイクロサービス に専用データベースを割り当てます。 この方法には、いくつかの利点があります。
- 各サービスは、独自のデータをカプセル化します。
- 各サービスは、特定のニーズに最適なデータベース テクノロジとスキーマを使用できます。
- 各サービスのデータベースは、個別にスケーリングできます。
- 1 つのサービスでのエラーは、他のサービスから分離されます。
これらの利点にもかかわらず、このアーキテクチャではサービス間のデータ整合性が複雑になります。 ACID のような従来のデータベースの保証は、複数の独立して管理されたデータ ストアに直接適用されるわけではありません。 これらの制限により、プロセス間通信または 2 フェーズ コミット プロトコルなどの従来のトランザクション モデルに依存するアーキテクチャは、多くの場合、Saga パターンに適しています。
ソリューション
Saga パターンは、トランザクションを一連のローカル トランザクション 分割することによって管理します。
各ローカル トランザクション:
- 1 つのサービス内でその作業をアトミックに完了します。
- サービスのデータベースを更新します。
- イベントまたはメッセージを使用して次のトランザクションを開始します。
ローカル トランザクションが失敗した場合、saga は一連の 補正トランザクション 実行して、前のローカル トランザクションが行った変更を元に戻します。
Saga パターンの主要な概念
補償可能なトランザクション は、取り消したり、反対の効果を持つ別のトランザクションによって補償したりできます。 サガのステップが失敗した場合、補償トランザクションは、補償対象トランザクションが行った変更を元に戻します。
ピボットトランザクションは、Saga における後戻りできない時点となります。 ピボット トランザクションが成功すると、補償可能なトランザクションはそれ以降意味を持たなくなります。 システムが一貫した最終状態を実現するには、後続のすべてのアクションを完了する必要があります。 ピボット トランザクションは、saga のフローに応じて、異なるロールを引き受けることができます。
元に戻せないトランザクションまたは互換性のないトランザクション 元に戻したり再試行したりすることはできません。
取り消し可能なトランザクションとコミットされた の境界は、ピボット トランザクションが最後に元に戻せるトランザクション、または取り消し可能なトランザクションであることを意味します。 あるいは、それは saga の最初の再試行可能な操作である場合もあります。
再試行可能なトランザクション ピボット トランザクションに従います。 再試行可能なトランザクションは冪等であり、一時的な障害が発生した場合でも、saga が最終状態に到達できるよう確実にするのに役立ちます。 彼らは最終的に一貫した状態を達成するためにサガを助ける。
Saga の実装アプローチ
2 つの一般的な saga 実装アプローチは、振り付け とオーケストレーション です。 各アプローチには、ワークフローを調整するための独自の課題とテクノロジのセットがあります。
振り付け
振付手法では、サービスは集中型コントローラーなしでイベントを交換します。 振り付けを使用すると、各ローカル トランザクションは、他のサービスでローカル トランザクションをトリガーするドメイン イベントを発行します。
| 振付の利点 | コレオグラフィの欠点 |
|---|---|
| サービスが少なく、調整ロジックを必要としない単純なワークフローに適しています。 | 新しい手順を追加すると、ワークフローが混乱する可能性があります。 各 saga 参加者が応答するコマンドを追跡するのは困難です。 |
| 調整に他のサービスは必要ありません。 | 互いのコマンドを使用する必要があるため、saga 参加者間で循環依存関係が発生するリスクがあります。 |
| 責任は saga 参加者全体に分散されるため、単一障害点は発生しません。 | 統合テストは、トランザクションをシミュレートするためにすべてのサービスを実行する必要があるため、困難です。 |
オーケストレーション
オーケストレーションでは、一元化されたコントローラーまたは オーケストレーターは、すべてのトランザクションを処理し、イベントに基づいて実行する操作を参加者に指示します。 オーケストレーターは saga 要求を実行し、各タスクの状態を格納および解釈し、補正トランザクションを使用して障害復旧を処理します。
| オーケストレーションの利点 | オーケストレーションの欠点 |
|---|---|
| 複雑なワークフローや新しいサービスを追加する場合に適しています。 | その他の設計の複雑さには、調整ロジックの実装が必要です。 |
| オーケストレーターがフローを管理するため、循環依存関係を回避します。 | オーケストレーターが完全なワークフローを管理するため、障害点が発生します。 |
| 責任を明確に分離することで、サービス ロジックが簡略化されます。 |
問題と考慮事項
このパターンを実装する方法を決定するときは、次の点を考慮してください。
デザイン思考のシフト: サガパターンを採用するには別の考え方が必要です。 複数のマイクロサービス間のトランザクション調整とデータ整合性に重点を置く必要があります。
デバッグ の複雑さ: デバッグ のサガは、特に参加するサービスの数が増えるにつれて複雑になる場合があります。
元に戻せないローカル データベースの変更: saga 参加者がそれぞれのデータベースに変更をコミットするため、 データをロールバックできません。
一時的な障害とべき等性の処理: システムは、一時的な障害を効果的に処理し、同じ操作を繰り返しても結果が変更されないようにする必要があります。 詳細については、べき等メッセージ処理を参照してください。
saga の監視と追跡の必要性: saga のワークフローの監視と追跡は、運用上の監視を維持するために不可欠なタスクです。
補正トランザクションの制限事項: 補正トランザクションが必ずしも成功するとは限らないため、システムが不整合な状態になる可能性があります。
sagas の潜在的なデータ異常
データの異常は、sagas が複数のサービスにわたって動作するときに発生する可能性がある不整合です。 各サービスは、参加要素データと呼ばれる独自のデータを管理するため、サービス間で組み込みの分離はありません。 このセットアップにより、部分的に適用された更新プログラムやサービス間の競合など、データの不整合や持続性の問題が発生する可能性があります。 一般的な問題は次のとおりです。
失われた更新: ある saga が別の saga による変更を考慮せずにデータを変更すると、更新が上書きされたり、更新が失われたりします。
ダーティリード: 別のサガが変更したデータをサガまたはトランザクションが読み取るが、その変更がまだ完了していない場合。
あいまい読み取り (繰り返し不可) : 読み取りの間に更新が行われるため、saga 内の異なる手順で不整合なデータが読み取られた場合。
データの異常に対処するための戦略
これらの異常を軽減または防止するには、次の対策を検討してください。
セマンティック ロック: saga の依存可能なトランザクションがセマフォを使用して更新が進行中であることを示す場合は、アプリケーション レベルのロックを使用します。
可換更新: 同じ結果を生成しながら、任意の順序で適用できるように、更新プログラムを設計します。 このアプローチは、サーガ間の競合を減らすのに役立ちます。
悲観的な見方: ダーティリードを排除するために、データ更新が再試行可能なトランザクション内で行われるよう、サーガの順序を組み替えます。 そうでないと、ある saga がダーティ データ、つまり uncommitted changes を読み取る一方で、別の saga がその更新をロールバックするための補償可能トランザクションを同時に実行するおそれがあります。
再読み取り値: 更新する前にデータが変更されていないことを確認します。 データが変更された場合は、現在の手順を停止し、必要に応じて saga を再起動します。
バージョン ファイル: レコードに対して実行されたすべての操作のログを保持し、競合を防ぐために正しい順序で実行されるようにします。
値に基づくリスクベースのコンカレンシー: 潜在的なビジネス リスクに基づいて適切なコンカレンシー メカニズムを動的に選択します。 たとえば、低リスクの更新には sagas を使用し、リスクの高い更新では分散トランザクションを使用します。
このパターンを使用する場合
このパターンは、次の場合に使用します。
- 厳密な結合を使用せずに、分散システムでデータの整合性を確保する必要があります。
- シーケンス内のいずれかの操作が失敗した場合は、ロールバックまたは補正する必要があります。
このパターンは、次の場合に適さない場合があります。
- トランザクションは密接に結合されます。
- 補正トランザクションは、以前の参加者で発生します。
- 循環依存関係があります。
次の手順
関連リソース
このパターンを実装する場合、次のパターンが関連する場合があります。
の振り付けパターン では、システムの各コンポーネントが、制御の中心点に依存するのではなく、ビジネス トランザクションのワークフローに関する意思決定プロセスに参加します。
補正トランザクション パターン 一連の手順によって実行される作業を元に戻し、1 つ以上のステップが失敗した場合に一貫した操作を最終的に定義します。 複雑なビジネス プロセスとワークフローを実装するクラウドでホストされるアプリケーションは、多くの場合、この 最終的な整合性モデルに従います。
再試行パターン を使用すると、失敗した操作を透過的に再試行してサービスまたはネットワーク リソースに接続しようとしたときに、アプリケーションで一時的なエラーを処理できます。 このパターンにより、アプリケーションの安定性を向上させることができます。
サーキット ブレーカー パターン は、リモート サービスまたはリソースに接続するときに、復旧に可変の時間がかかる障害を処理します。 このパターンにより、アプリケーションの安定性と回復性が向上します。
正常性エンドポイント監視パターン は、外部ツールが一定の間隔で公開されたエンドポイントを介してアクセスできる機能チェックをアプリケーションに実装します。 このパターンは、アプリケーションとサービスが正しく実行されていることを確認するのに役立ちます。