saga 設計パターンは、分散トランザクション シナリオでマイクロサービス間のデータの一貫性を管理する方法です。 saga はトランザクションのシーケンスです。この saga によって各サービスが更新され、次のトランザクション ステップをトリガーするメッセージまたはイベントが発行されます。 また、ステップが失敗すると、その前のトランザクションを無効にする補正トランザクションを実行されます。
コンテキストと問題
"トランザクション" は、ロジックまたは作業の 1 つのユニットで、複数の操作で構成されることもあります。 トランザクション内では、"イベント" は、エンティティに対して発生する状態変更です。アクションの実行や後のイベントのトリガーに必要な情報はすべて、"コマンド" によってカプセル化されます。
トランザクションには、"原子性、一貫性、分離性、持続性 (ACID) " が必要です。 1 つのサービス内のトランザクションは ACID ですが、サービス間のデータの一貫性を維持するには、サービス間のトランザクション管理戦略が必要です。
マルチサービス アーキテクチャの場合:
- "原子性" はすべて発生するか、まったく発生しない操作の集合で、分割も削減もできません。
- "一貫性" とは、ある有効な状態から別の有効な状態にのみ、トランザクションによってデータが取り込まれることを意味します。
- "分離性" により、同時実行トランザクションによって生成されるデータの状態が、順番に実行されるトランザクションによって生成される状態と同じであることが保証されます。
- "持続性" により、システム障害または停電が発生しても、コミットされたトランザクションが、コミットされたまま維持されることが保証されます。
マイクロサービスごとのデータベース モデルは、マイクロサービス アーキテクチャに多くのメリットをもたらします。 ドメイン データをカプセル化すると、各サービスが最適なデータ ストアの種類とスキーマを使用し、必要に応じて独自のデータ ストアをスケーリングできます。また、他のサービスの障害からサービスを切り離すことも可能です。 ただし、サービス固有のデータベース間でデータの一貫性を確保すると、問題も発生します。
2 フェーズコミット (2PC) プロトコルのような分散トランザクションには、トランザクションを続行する前に、トランザクション内のすべての参加要素についてコミットまたはロールバックが必要です。 しかし、NoSQL データベースやメッセージ ブローカーなど、参加要素の実装の中には、このモデルをサポートしていないものがあります。
プロセス間通信 (IPC) の同期と可用性も分散トランザクションの制限です。 オペレーティング システムが提供する IPC を使用すると、個別のプロセスがデータを共有できます。 分散トランザクションをコミットするには、参加しているすべてのサービスが使用できなければならないため、システム全体の可用性が低下する可能性があります。 IPC またはトランザクション制限があるアーキテクチャ実装は、saga パターンの候補です。
解決策
saga パターンによるトランザクション管理には、"ローカル トランザクション" のシーケンスが使用されます。 ローカル トランザクションは、saga 参加要素によって実行されるアトミックの作業工数です。 各ローカル トランザクションによってデータベースが更新され、saga 内の次のローカル トランザクションをトリガーするメッセージまたはイベントが発行されます。 ローカル トランザクションが失敗した場合、saga は、前のローカル トランザクションによって行われた変更を元に戻す一連の "補正トランザクション" を実行します。
saga パターンの場合:
- "補正可能なトランザクション" は、逆の効果を持つ別のトランザクションを処理することによって元に戻される可能性があるトランザクションです。
- "ピボット トランザクション" は saga の続行/中止ポイントです。 ピボット トランザクションがコミットされると、saga は完了するまで実行されます。 ピボット トランザクションは、saga の補正も再試行もできないトランザクション、補正可能な最後のトランザクション、または再試行可能な最初のトランザクションです。
- "再試行可能なトランザクション" はピボット トランザクションに続くトランザクションで、成功が保証されています。
saga の実装アプローチとして一般的なのは、"コレオグラフィ" と "オーケストレーション" の 2 つです。 それぞれのアプローチに、ワークフローを調整するための独自の課題とテクノロジがいくつかあります。
コレオグラフィ
コレオグラフィは saga を調整する方法です。この方法では、参加要素が一元的な制御ポイントを使わずにイベントを交換します。 コレオグラフィを使用すると、他のサービスでローカル トランザクションをトリガーするドメイン イベントが、各ローカル トランザクションによって発行されます。
メリット
- 必要な参加要素がわずかで、調整ロジックを必要としないシンプルなワークフローに適しています。
- 追加のサービス実装とメンテナンスは必要ありません。
- saga の参加要素全体に責任が分散されるため単一障害点がありません。
デメリット
- どの saga 参加要素がどのコマンドをリッスンしているかを追跡するのが難しいため、新しいステップを追加すると、ワークフローが混乱する可能性があります。
- saga 参加要素がお互いのコマンドを使用する必要があるため、参加要素間で依存関係が循環するリスクがあります。
- トランザクションをシミュレートするには、すべてのサービスが実行されている必要があります。このため統合テストが困難です。
オーケストレーション
オーケストレーションは saga を調整する方法です。この方法では、一元管理されたコントローラーが、どのローカル トランザクションを実行するかを saga 参加要素に伝えます。 saga オーケストレーターは、すべてのトランザクションを処理して、イベントに基づいて、実行する操作を参加要素に通知します。 また、オーケストレーターは、saga 要求を実行し、各タスクの状態を格納および解釈して、補正トランザクションを使って障害復旧を処理します。
メリット
- 多数の参加要素が含まれる、または時間経過に従って新しい参加要素が追加される複雑なワークフローに適しています。
- プロセスのすべての参加要素を制御する場合、またアクティビティのフローを制御する場合に適しています。
- オーケストレーターは一方的な saga 参加要素に依存しているため、依存関係は循環しません。
- saga 参加要素が、他の参加要素のコマンドを認識する必要がありません。 懸念事項が明確に分離されているため、ビジネス ロジックがシンプルです。
デメリット
- 設計の複雑さが増すため、調整ロジックを実装する必要があります。
- オーケストレーターの管理対象が完全なワークフローなので、追加の障害点が発生します。
問題と注意事項
saga パターンを実装するときは、以下の点を考慮してください。
- saga パターンを実装する場合は、トランザクションを調整し、複数のマイクロサービスにまたがるビジネス プロセスのデータの一貫性を維持する方法を、新しい考え方で検討する必要があります。このため、最初は大変かもしれません。
- saga パターンのデバッグは特に難しく、参加要素が増えると複雑さがさらに増します。
- saga 参加要素の変更のコミット先はローカル データベースです。このためデータをロールバックできません。
- 一時的に発生する可能性がある一連の障害を、実装によって処理できる必要があります。また、副作用を軽減し、データの一貫性を確保するための "べき等性" も必要です。 べき等性とは、最初の結果を変更せずに、同じ操作を複数回繰り返すことができることを意味します。 詳細については、メッセージを処理するときに状態を一緒に更新する場合のべき等の保証に関するガイダンスを参照してください。
- 可観測性を実装して、saga ワークフローを監視および追跡することをお勧めします。
- 参加要素データの分離が欠如していると、持続性に問題が生じます。 saga の実装には、異常を減らすための対策が含まれている必要があります。
- 補正トランザクションは常に機能するとは限りません。
適切な対策が講じられていないと、次の異常が発生する可能性があります。
- "更新データの喪失"。ある saga が、別の saga による変更を読み取らずに書き込みを行った。
- "ダーティ リード"。saga が更新をまだ完了しないうちに、トランザクションまたは saga がその更新を読み取った。
- あいまい/反復不能読み取り。読み取りと読み取りの間にデータ更新が発生したため、別の saga ステップが別のデータを読み取った。
次の異常軽減策または防止策をお勧めします。
- "セマンティック ロック"。アプリケーション レベルのロックです。saga の補正可能なトランザクションが、セマフォを使って更新が進行中であることを示します。
- "可換更新"。任意の順序で実行し、同じ結果を生成できます。
- "ペシミスティック ビュー"。ダーティ データを読み取る saga があれば、補正可能なトランザクションを実行して操作をロールバックする saga もあります。 ペシミスティック ビューにより、saga は、基になるデータが再試行可能なトランザクションで更新されるように並べ替えられるため、ダーティ リードの可能性が排除されます。
- "値の再読み取り"。データが変更されていないことを確認してから、レコードを更新します。 レコードが変更されている場合は、ステップが中止され、saga は再起動される可能性があります。
- "バージョン ファイル"。レコードが到着したときに操作を記録し、正しい順序で実行します。
- "値ごと"。各要求のビジネス リスクに基づいて、同時実行メカニズムを動的に選択します。 リスクの低い要求は saga を優先し、リスクの高い要求は分散トランザクションを優先します。
このパターンを使用する状況
saga パターンは、以下を行う必要がある場合に使用します。
- 密結合を使用せずに分散システムでデータの一貫性を確保する。
- シーケンスのいずれかの操作が失敗した場合に、ロールバックまたは補正を行う。
saga パターンは、以下の状況には適していません。
- 密結合トランザクション。
- 以前の参加要素で発生した補正トランザクション。
- 循環依存関係。
例
サーバーレスのオーケストレーション ベースの saga は、成功したワークフローと失敗したワークフローを含む送金シナリオをシミュレートするオーケストレーション アプローチを使用した saga 実装のリファレンスです。
次のステップ
- 分散データ
- Chris Richardson。 2018: 「マイクロサービス パターン」。 パブリケーションの作成。
関連資料
このパターンを実装する場合は、次のパターンも役に立つことがあります。
- コレオグラフィ: 中央の制御ポイントに依存するのではなく、システムの各コンポーネントが、ビジネス トランザクションのワークフローに関する意思決定プロセスに参加するようにします。
- 補正トランザクション: 1 つ以上のステップが失敗した場合に、一連のステップで実行された作業を元に戻し、整合性がある操作を最終的に定義します。 複雑なビジネス プロセスとワークフローを実装するクラウド ホスト型アプリケーションは、多くの場合、この "最終的整合性モデル" に従います。
- 再試行: 一時的な障害をアプリケーションが処理できるようにします。アプリケーションがサービスまたはネットワーク リソースに接続しようとしたときに失敗した操作を透過的に再試行します。 再試行により、アプリケーションの安定性を向上させることができます。
- サーキット ブレーカー: リモート サービスまたはリソースとの接続時に、復旧に要する時間が一定しないエラーを処理します。 サーキット ブレーカーにより、アプリケーションの安定性と回復性を向上させることができます。
- 正常性エンドポイントの監視: 公開されたエンドポイントを通じて外部ツールが定期的にアクセスできる機能チェックをアプリケーションに実装します。 正常性エンドポイントの監視は、アプリケーションとサービスが正常に実行されていることを確認するのに役立ちます。