はじめに
はじめまして。日立製作所 クラウドビジネス推進センタの西谷と申します。ひょんな思い付きから、技術投稿を始めることにしましたので、よろしくお願いします。
今回は、マイクロサービスの最難関とも呼ばれているトランザクション管理について連載していこうと思います。
以下のような連載を考えてますが、LGTMいっぱいくれたら執筆をもっとがんばろうと思います 笑。
- マイクロサービストランザクションの動向(今回)
- トランザクションの原則:ACID特性とCommitment Ordering1
- トランザクションの原則:ACID特性とCommitment Ordering2
- 合意理論からみる2Phase CommitとMicroserviceマイクロサービス
- 結果整合性は本当に整合するのか
ゴール
マイクロサービスでトランザクション管理する主流の考え方を大まかに理解する。
マイクロサービスとは何か
マイクロサービスとは、ビジネス機能にしたがった複数の小さなサービスでシステムを構成するアーキテクチャ・ソフトウェア開発技法です。その利点はおおよそ以下のとおりです。
-
アプリケーションやシステムを、役割/責務境界にそって小さいサービスに分割し、ネットワーク越しの軽量なプロトコルで結合する。その結果・・、
- サービスの理解やサービスの開発が簡単になる。
- システムの部分的な改善が容易になり、各サービスは独立してデプロイできる。
-
小さいサービスそれぞれの役割にそって、最適な開発言語、データベースを選択できる。
以上の利点によって、システムの開発サイクルを短縮でき、短いスパンでのサービスリリースを可能にします。昨今、企業は非常に変化の激しい状況に身をおいてますので、「変化に即座に対応できる能力」をITシステムに搭載させるという意味で、昨今マイクロサービスは注目を浴びているわけです。
ここまで書くと大変便利なアーキテクチャであり、話題になるのもうなずけますが、マイクロサービスを実現する技術は一部まだ成熟していないのが実情です。例えばマイクロサービスでデータを扱おうとすると、大変難しい課題に直面することがあります。
マイクロサービスでデータを扱うためのアーキテクチャ
ここではマイクロサービスでデータを扱う場合のアーキテクチャを紹介しようと思います。
ここでいう「データを扱う」というのは、ある種のデータストアからデータを取得したり、更新したりすることを意味します。
マイクロサービスではDatabase per Serviceというパターンが提唱されています。これは、ざっくり言えば、サービスごとにデータベースをわけ、特定のマイクロサービスからのみ参照・更新を受け付けるように構成することです。
そして、データベースの責務を凝縮させ、マイクロサービスの恩恵である改修の容易性やスケーラビリティをデータベースやデータベースにアクセスするマイクロサービスにも持たせることができるわけです。
ただし、データベースの責務を凝縮させるということは、責務ごとにデータベースとマイクロサービスのセットを用意することになるため、ここでデメリットもでてきます。例えば、複数データベースに関連するデータを矛盾なく更新しようとすると、単に更新するだけでは達成できません。
分散トランザクション
上記で述べた「複数データベースに関連するデータを矛盾なく更新する」行為を、分散トランザクションと呼びます。トランザクション(Transaction)とは「取引」を意味する言葉です。そしてその取引は複数のオペレーションを前提として成り立つことがあります。そのため、情報工学においてトランザクションとは「永続的なデータに対する不可分な一連の処理」を指す言葉でもあります。
例えば分散トランザクションを説明するときに、よく例に出るのは「銀行窓口で口座Aから口座Bにお金を移す」という行為です。銀行窓口は(実際はどうかはわかりませんが)口座Aから500円引き出し、口座Bに500円を振込むことで「お金を移す」という取引を完結させます。
このときなんらかの理由で振込が行われなかったり、振込に失敗したりすると口座Aから500円引き出しているのにもかかわらず、口座Bには入金していないという、お金の消失、つまり口座間残高の矛盾が発生するわけです。
大事なことですのでもう一度言いますが、分散トランザクションの目的は 「障害等の問題によって、複数データソースにまたがる関連データの矛盾が発生することを防ぐこと」 にあります。
なお、データベースが単一である場合はデータベースに対するローカルトランザクションで矛盾を防ぐことができますが、データベースが分かれるとローカルトランザクションだけでは矛盾を防ぐことができません。
ではマイクロサービスにおいて、分散トランザクションを実現する主流な方法は何か、ということになるのですが、microservice.ioは「Sagaパターン」と呼ばれるデザインパターンを用いることを推奨しています。
Sagaパターン
Sagaパターンとは、ローカルトランザクションを連結しSagasと呼ばれるある種のワークフローを形成するデザインパターンです。
この連結の中のどこかでローカルトランザクションが失敗した場合、「補償トランザクション」と呼ばれるデータの巻き戻し処理を行うことで、一部失敗し、一部成功するような矛盾を防止します。
このデザインパターンを、分散トランザクションに適用しようとしているということです。この仕組みの詳細を知りたい場合は、microservice.ioの紹介ページやazureのreference architecture等をご参照ください。
なお、こちらは1987年のSagas論文に基づいており、マイクロサービス特有の考え方というわけではなく新しいわけでもありません。過去にも、Oasis Business Transactionとして、標準化されたことがあります(参照)。
Sagaパターンには矛盾防止能力があるのか?
さて、microservice.io のChris RichardsonさんはQCONSF2017にて「SagaはACIDのうちのIが欠落している」旨の発言をされています(スライド33P参照)。これはSagaパターンそれ自体は、データに対する排他制御を必須としていないからです。
ただ、セマンティックロックのような手法を使って排他制御を実装すれば、SagaパターンでもACID特性を達成できるとのことです。
排他制御は必須なのでしょうか?そうじゃないのでしょうか?
おそらくSagaパターンをマイクロサービスに適用し分散トランザクションを実現しようとするエンジニアにとって、ここは知りたいポイントなのではないかと思います。しかし、排他制御のような実際の機能は、ACID特性の何を実現するために存在してるのか、あまり明らかになっていないのが実情のように思います。
おわりに
マイクロサービスでトランザクション管理する現在の動向について、ざっくり説明しました。まとめると以下です。
- 2021年1月時点で、Sagaパターンと呼ばれるデザインパターンを分散トランザクションに用いることが提唱されている
- ただ、Sagaパターンが矛盾防止の能力を保持しているかどうかは不明な点が残っている
というわけで、次回は分散トランザクションを実現するうえで、システムが保持しなければならない性質や、それを実現した機能の依存関係をもう少し見ていこうと思います。