ここでは、分散システムのトランザクション管理の手法について次の観点で説明します。
ACIDとBASE
分けることのできない一連の情報処理の一単位をトランザクションといいますが、CAP定理
でいうところのCとA(C:一貫性とA:可用性)を重視したトランザクション管理手法がACIDで、AとP(A:可用性とP:分断耐性)を重視したトランザクション管理手法がBASEになります。
2相コミット(Two-Phase Commit)
2相コミット(Two-Phase Commit)とは、コンピュータネットワークやデータベースにおいて、分散システム内の全ノードがトランザクションのコミットに合意するための分散アルゴリズムです。
2相コミットは次のように動作します。
まず、あるノードを調整者(coordinator)とし、ネットワーク上の他のノードを参加者(cohorts)とします。
2相コミットは、まず、調整者は全参加者に「コミットしてもよいか」というメッセージを送ります。
これを投稿フェーズといいます。
例えば、注文管理サービス、在庫管理サービス、請求管理サービスというマイクロサービスから構成される分散システムがあり、注文管理サービスが注文を登録するとき、在庫管理サービスに該当する商品の在庫の引当を依頼し、請求管理サービスに注文に対する代金の受取を依頼するとします。
この場合、まず、調整者である注文管理サービスは、参加者である在庫管理サービスと請求管理サービスに、それぞれ在庫の引当が可能か、代金の受取が可能か聞きます。
次に、調整者が全参加者から「合意」メッセージを受け取った場合、全参加者に「コミットしなさい」というメッセージを送ります。
これをコミットフェーズといいます。
上の例の場合、参加者である在庫管理サービスと請求管理サービスが、それぞれ在庫の引当が可能で、代金の受取が可能である場合、注文管理サービスは、在庫管理サービスと請求管理サービスに、それぞれ在庫の引当と代金の受取を依頼します。
そして、在庫管理サービスが在庫を引当て、かつ、請求管理サービスが代金を受け取った時点で、注文管理サービスはトランザクションが完了したとみなします。
もし、在庫管理サービスが在庫を引当後、請求管理サービスが代金を受け取れなかった場合は、注文管理サービスは在庫管理システムにトランザクションのロールバックを要求します。
さて、2相コミットの問題は、分散システム全体の一貫性を保つため、調整者が全参加者から「合意」メッセージを受け取るまで、参加者の状態が変更されないようにロックする必要があるということです。
上の例の場合、もし、何らかの事情で在庫管理サービスからの応答が遅延した場合、その間、注文管理サービスや請求管理サービスの状態がロックされ、それらのサービスの可用性を下げることになります。
2相コミットは、CAP定理でいうとCとP(C:一貫性とP:分断耐性)は確保できるかもしれませんが、可用性(Availability)を大幅に下げるのです。
Saga
Sagaは、2相コミットと異なり、複数のノードを長期間ロックする必要がないようにデザインされた分散システムのトランザクション管理アルゴリズムです。
BASEには、結果整合性(Eventual Consistency)という、最終的には整合性(一貫性)が保証されるという性質がありますが、Sagaは、結果整合性を保持することで、CAP定理のAとP(A:可用性とP:分断耐性)を保証するソリューションの一つです。
Sagaの場合、調整者と参加者という明確な役割がなく、それぞれのノードが独立してトランザクションを実行します。
なので、トランザクションを遂行する上で、他のノードの影響を受けて長時間ロックされることはなく可用性を保証することができます。
それでは、注文を登録するタイミングで在庫の引当と代金の受取をしたいなどのように複数のトランザクション間にまたいで一貫性を保持したい場合はどうすればよいのでしょうか。
次のようなビジネスプロセスを考えてみましょう。
これは、顧客からの注文を受けて行う一連のアクションフローです。
次に、これらの処理をシステムで自動化することで業務を改善する場合、次のようなアクションフローを設計することができます。
このアクションフローは、注文の登録から商品の発送指示までをシステムが自動的に行う流れになっています。
ここでは、このアクションフローを実現する分散システムの構成を次のように考えます。
- 注文管理サービス
注文処理を担うマイクロサービス。
アクションフローの「注文の登録を」行う。 - 在庫管理サービス
在庫管理を担うマイクロサービス。
アクションフローの「在庫の引当」を行う。 - 請求管理サービス
代金の請求や回収を担うマイクロサービス。
アクションフローの「代金の受取」を行う。 - 顧客管理サービス
顧客へのポイント付与を担うマイクロサービス。
アクションフローの「ポイントの付与」を行う。 - 出荷管理サービス
出荷処理を担うマイクロサービス。
アクションフローの「商品の発送指示」を行う。
これらのマイクロサービスを、分散システムを構成するノードとしてSagaを設計すると次のようになります。
注文管理サービスがオーケストレーターになって、各マイクロサービスに指示を出している仕組になっています。
注文管理サービスは、すべての指示が問題なく処理されたら注文の登録をコミットするものとします。
さて、ここで、最後の商品の発送指示でエラーになったらどうなるでしょうか。
在庫の引当からポイントの付与までのトランザクションはコミットされています。
Sagaでは、トランザクション境界のすべての処理がうまくいかず、途中でエラーになった場合、コミットされた処理を取り消す処理をすることでロールバックします。
この取り消し処理のことを補償トランザクション(Compensating transaction)といいます。
それから、上の仕組は、注文管理サービスがオーケストレーターになって、各マイクロサービスに指示を出している形になっていますが、次の図のように各マイクロサービスがイベント駆動で自律的に動く仕組にすることもできます。
このように非同期メッセージングを介して働く仕組をコレオグラフィーベースのSagaといいます。
コレオグラフィーとはダンスの振り付けのことで、分散システムを構成する各ノードが、あらかじめ設計された振り付けに従って動くことからこのように呼ばれます。
上の図の場合、各マイクロサービスが非同期メッセージングを介して処理を実行しています。
このとき、相関ID(Correlation ID)を発行して、注文データや、在庫引当データなど各トランザクションの属性として記録することで、同じフローで発生したトランザクションを識別できるようにします。
例えば、もし請求管理サービスが代金の受取に失敗した場合、
請求管理サービスは、相関IDが付与された代金受取失敗メッセージを注文管理サービスと在庫管理サービスに通知することで、これらのサービスは、同じ相関IDのトランザクションに対する補償トランザクションを実行して、それをロールバックします。
さて、コレオグラフィーベースのSagaに対して、特定のノードがオーケストレーターになって指揮するSagaをオーケストレーションベースのSagaといいます。
オーケストレーションベースのSagaは、処理のフローがわかりやすく、比較的ロールバックも簡単ですが、リクエスト/レスポンス呼び出しによって各マイクロサービスをコントロールケースが多く、その場合、オーケストレーターが他のノード(マイクロサービス)のドメイン知識を持つ可能性も高く、ノード(マイクロサービス)間を密結合にするというデメリットがあります。
一方、コレオグラフィーベースのSagaの場合、非同期メッセージングによって、自律したノード(マイクロサービス)同士が繋がるので、ノード(マイクロサービス)間を疎結合に保つことができますが、全体のプロセスが複雑になり、ロールバックを制御するのが難しくなります。
なので、一つの開発チームがSaga全体の実装を所有している場合、つまり、Saga全体をコントロールできる場合は、オーケストレーションベースのSagaを採用し、開発チームがノード(マイクロサービス)間で別れており、それぞれが独立している場合は、コレオグラフィーベースのSagaを採用するという方針がよいかと思います。
最後に、Sagaを設計するときは、ビジネスプロセスの設計が必要です。
Sagaは、あるべきビジネスプロセスを実現するソリューションの有効な手段になるのです。
[…] ヴォーン・ヴァーノンの実践ドメイン駆動設計という書籍では、ドメイン駆動設計(DDD)の構成要素である集約ごとにイベントストアを設け、イベントソーシングを実現することを推奨しています。 また、Sam Newmanのマイクロサービスアーキテクチャという書籍では、DDDの集約ごとにマイクロサービスを設けることにより、マイクロサービスの一貫性を保ちやすく、依存関係を最小限にできると説明しています。 集約は、ドメインオブジェクトのライフサイクルを管理する単位であるので、この単位にコマンド処理のマイクロサービスを設け、イベントソーシングによって集約のイベントを管理することで、イベントソーシングのメリットである独立性、拡張性、可用性などを高めることができます。 なお、集約の状態を変化させる操作など、特定のドメインにおける重要な出来事のことをドメインイベントといいます。 複数のマイクロサービスを跨いだ分散トランザクションは、Sagaによって管理することができます。 […]
[…] CQRSの適用 次に、ドメインをコマンド・クエリ責務分離(CQRS)パターンのコマンド処理とクエリ処理に分け、それぞれをマイクロサービスにします。 例えば、販売管理ドメインの場合、コマンド処理のマイクロサービスとして受注管理サービス、クエリ処理のマイクロサービスとして受注照会サービスを設定します。 なお、マイクロサービスを跨いだ分散システムのトランザクション管理にはSagaを適用します。 […]