<p>こんにちわ、サイト管理者のわかっち (<a href="https://twitter.com/wakatchi_tech">@wakatchi_tech</a>) です。</p>
<div class="wp-block-vk-blocks-balloon vk_balloon vk_balloon-position-right vk_balloon-type-speech vk_balloon-animation-none"><div class="vk_balloon_icon"><figure><img class="vk_balloon_icon_image vk_balloon_icon_image-type-normal " src="https://wakatchi.dev/wp-content/uploads/2022/07/eye_ganseihirou_computer_man.png" alt=""/><figcaption class="vk_balloon_icon_name">質問者</figcaption></figure></div><div class="vk_balloon_content_outer"><div class="vk_balloon_content has-background-color has-pale-cyan-blue-background-color "><span class="vk_balloon_content_before has-text-color has-pale-cyan-blue-color"></span><span class="vk_balloon_content_after "></span>
<p>マイクロサービスでシステム作ったらトランザクション管理がしんどいです。</p>
</div></div></div>
<p>こんな質問をいただきました。</p>
<p>マイクロサービスアーキテクチャでシステムを構築した際、更新対象が複数のサービスをまたがる場合は、トランザクションの扱いが途端に難しくなります。なかでも、障害発生時に各サービス間の処理をロールバックするためには補償(補正)トランザクションが必要になり、複雑なトランザクション制御が求められます。</p>
<p>補償トランザクションとは、処理の途中で失敗した場合に、それを取り消すことで実行結果を打ち消す処理のことです。補償トランザクションの実装は、打ち消す処理を提供するサービスと、それを呼び出すサービスの双方に負担があり、設計や実装が複雑になりがちです。</p>
<p>トランザクションには、1つのトランザクション内で1つのリソース(DBなど)処理のみ行うローカルトランザクションと、1つのトランザクション内で複数のリソース処理を行うグローバルトランザクションがあります。グローバルトランザクションは分散トランザクションとも呼ばれ、その実装としては<a href="https://pubs.opengroup.org/onlinepubs/009680699/toc.pdf" data-type="link" data-id="https://pubs.opengroup.org/onlinepubs/009680699/toc.pdf">X/Open XA</a>やWS-Transactionなどが知られています。</p>
<p><span data-color="#f78da7" style="background: linear-gradient(transparent 60%,rgba(247, 141, 167, 0.7) 0);" class="vk_highlighter">マイクロサービスアーキテクチャは、ローカルトランザクションを勧める一方で、グローバルトランザクション(分散トランザクション)は推奨しません。</span>分散トランザクションは、複雑で不具合の原因になる可能性が高いためです。マイクロサービスアーキテクチャで推奨しているのは、分散トランザクションではなく、複数リソースの結果整合性を利用することです。複数リソースの結果整合性を利用する方法として、<strong><span data-color="#fcb900" style="background: linear-gradient(transparent 60%,rgba(252, 185, 0, 0.7) 0);" class="vk_highlighter">TCC(Try-Confirm/Cancel)</span></strong>パターンと<strong><span data-color="#fcb900" style="background: linear-gradient(transparent 60%,rgba(252, 185, 0, 0.7) 0);" class="vk_highlighter">Sagaパターン</span></strong>と呼ばれるアーキテクチャがあります。</p>
<p>この記事は、TCCパターンとSagaパターンが、マイクロサービスのトランザクション特性に合わせてどう使われるかをまとめていきます。</p>
<div class="wp-block-vk-blocks-table-of-contents-new vk_tableOfContents vk_tableOfContents-style-default tabs"><div class="tab"><div class="vk_tableOfContents_title">目次</div><input type="checkbox" id="chck1"/><label class="tab-label vk_tableOfContents_openCloseBtn button_status button_status-open" for="chck1" id="vk-tab-label">CLOSE</label><ul class="vk_tableOfContents_list tab_content-open">
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-2">
<a href="#vk-htags-d3e69690-18ce-4346-902d-2d1ff1748cb8" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">1. </span>
マイクロサービスをまたがるトランザクションのタイプ
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-3">
<a href="#vk-htags-535bc8ea-5254-4b17-8f74-18e7b2fb7ea7" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">1.1. </span>
サービス連携の方式
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-4">
<a href="#vk-htags-deffe342-c1b4-4aeb-9b37-328ac90d74c1" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">1.1.1. </span>
リクエスト・リプライ
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-4">
<a href="#vk-htags-8e50eb06-9f5f-4bf0-b364-e5599e5d1097" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">1.1.2. </span>
イベントドリブン
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-3">
<a href="#vk-htags-8a060b00-c8f4-4d08-b9e6-285f65893503" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">1.2. </span>
サービス間の関連付け
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-4">
<a href="#vk-htags-a94eb6b9-5695-4545-8d9b-4569c2fab056" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">1.2.1. </span>
オーケストレーション
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-4">
<a href="#vk-htags-9ec7d869-8b95-46e0-9a30-91b86a01030c" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">1.2.2. </span>
コレオグラフィ
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-2">
<a href="#vk-htags-f1fd8317-6234-4d4a-96f2-c50d5a63a782" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">2. </span>
TCCパターン
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-3">
<a href="#vk-htags-4143dd69-c394-4007-a4da-975e5b704b2b" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">2.1. </span>
TCCパターンによるオーケストレーション
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-3">
<a href="#vk-htags-fbff7f3d-6614-44fe-9e8f-547fe19aaf53" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">2.2. </span>
TCCパターンによるコレオグラフィ
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-3">
<a href="#vk-htags-ad08127e-b51d-4192-b130-6f28040fd8b7" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">2.3. </span>
TCCパターンを実装する際の留意点
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-4">
<a href="#vk-htags-278ef56b-e087-458a-9aa2-005bfb213873" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">2.3.1. </span>
Try/Confirm/CancelのAPIを実装する
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-4">
<a href="#vk-htags-2f7cc7cd-5a26-43b1-a956-64add9cc0242" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">2.3.2. </span>
処理完了に2倍かかることがある
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-4">
<a href="#vk-htags-b4918433-827c-4533-92b3-af61739445e1" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">2.3.3. </span>
サービスを最終状態ではなく仮(保留)状態を経由する
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-2">
<a href="#vk-htags-36796de0-61e9-4582-b05b-8184fa84af32" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">3. </span>
Sagaパターン
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-3">
<a href="#vk-htags-fdcc4af6-e8be-41e5-8fd4-af18251e921b" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">3.1. </span>
Sagaパターンによるオーケストレーション
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-3">
<a href="#vk-htags-8c910649-c476-4902-8af5-f41a416a428f" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">3.2. </span>
Sagaパターンによるコレオグラフィ
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-3">
<a href="#vk-htags-aec4d351-1151-46ff-8a10-e76c9da195df" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">3.3. </span>
Sagaパターンのロールバック
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-3">
<a href="#vk-htags-4d8d2442-aae8-4a2e-974d-df629c30b740" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">3.4. </span>
Sagaパターンを実装する際の留意点
</a>
</li>
<li class="vk_tableOfContents_list_item vk_tableOfContents_list_item-h-2">
<a href="#vk-htags-4eda22ae-d380-4959-ac28-9730cb52cf7b" class="vk_tableOfContents_list_item_link">
<span class="vk_tableOfContents_list_item_link_preNumber">4. </span>
まとめ
</a>
</li>
</ul></div></div>
<h2 class="wp-block-heading" id="vk-htags-d3e69690-18ce-4346-902d-2d1ff1748cb8">マイクロサービスをまたがるトランザクションのタイプ</h2>
<figure class="wp-block-image size-large"><img src="https://wakatchi.dev/wp-content/uploads/2022/07/5-1024x683.png" alt="" class="wp-image-758"/></figure>
<p>マイクロサービスをまたがるトランザクションは、サービス連携方法とサービス間の関連付けで分類できます。サービス連携方法には、<strong><span data-color="#fcb900" style="background: linear-gradient(transparent 60%,rgba(252, 185, 0, 0.7) 0);" class="vk_highlighter">リクエスト・リプライ</span></strong>と<strong><span data-color="#fcb900" style="background: linear-gradient(transparent 60%,rgba(252, 185, 0, 0.7) 0);" class="vk_highlighter">イベントドリブン</span></strong>の2つの方式があります。サービス間の関連付けには、<strong><span data-color="#fcb900" style="background: linear-gradient(transparent 60%,rgba(252, 185, 0, 0.7) 0);" class="vk_highlighter">オーケストレーション(中央集権型)</span></strong>と<strong><span data-color="#fcb900" style="background: linear-gradient(transparent 60%,rgba(252, 185, 0, 0.7) 0);" class="vk_highlighter">コレオグラフィ(分散型)</span></strong>があります。さて、それぞれどのような特徴があるのでしょうか。</p>
<h3 class="wp-block-heading" id="vk-htags-535bc8ea-5254-4b17-8f74-18e7b2fb7ea7">サービス連携の方式</h3>
<h4 class="wp-block-heading" id="vk-htags-deffe342-c1b4-4aeb-9b37-328ac90d74c1">リクエスト・リプライ</h4>
<ul class="wp-block-list">
<li>REST通信による連携方式です。</li>
<li>自身の処理の一部を、他のサービスに依頼します。</li>
</ul>
<h4 class="wp-block-heading" id="vk-htags-8e50eb06-9f5f-4bf0-b364-e5599e5d1097">イベントドリブン</h4>
<ul class="wp-block-list">
<li>イベントの発生によって特定のサービス処理が駆動される連携方式です。</li>
<li>既に発生した事柄を他のサービスに通知することで、発生した事象に応じた処理を実行させます。</li>
</ul>
<h3 class="wp-block-heading" id="vk-htags-8a060b00-c8f4-4d08-b9e6-285f65893503">サービス間の関連付け</h3>
<h4 class="wp-block-heading" id="vk-htags-a94eb6b9-5695-4545-8d9b-4569c2fab056">オーケストレーション</h4>
<ul class="wp-block-list">
<li>中央の指揮者がサービスの呼び出しをコントロールします。</li>
<li>各サービスは中央からのリクエストによってロジックを実行し、実行結果をレスポンスとして中央に返す、リクエスト・リプライの方式になります。</li>
<li>同期通信に適しています。</li>
</ul>
<h4 class="wp-block-heading" id="vk-htags-9ec7d869-8b95-46e0-9a30-91b86a01030c">コレオグラフィ</h4>
<p>個々のサービスが自律的に動き、あらかじめ定義された他のサービスに働きかけます。</p>
<ul class="wp-block-list">
<li>個々のサービスは、定められた条件に従って自律して稼動し、条件に合致した他のサービスにデータを送ります。</li>
<li>非同期通信に適しています。</li>
</ul>
<p>サービス連携の方式方法を縦軸、サービス間の関連付けを横軸にトランザクションのタイプを分類すると、TCCパターンとSagaパターンをこのように図示できます。</p>
<figure data-wp-context="{"imageId":"68b16818a4515"}" data-wp-interactive="core/image" class="wp-block-image size-full wp-lightbox-container"><img data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="https://wakatchi.dev/wp-content/uploads/2022/07/microservices-tx-pattern-saga-tcc-tx-pattern-1.webp" alt="マイクロサービスにおけるトランザクション管理の図解(日本語)。TCC(Try-Confirm-Cancel)とSagaパターンの比較。中央に『TCC』『Saga』のラベルがあり、上部に『リクエスト・リプライ』、左右に『オーケストレーション(中央集権型)』『コレオグラフィ(分散型)』、下部に『イベントドリブン』と記載。" class="wp-image-2012"/><button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="拡大する"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
</svg>
</button></figure>
<p>それでは、TCCパターンとSagaパターンとはどのようなものでしょうか。</p>
<h2 class="wp-block-heading" id="vk-htags-f1fd8317-6234-4d4a-96f2-c50d5a63a782">TCCパターン</h2>
<figure class="wp-block-image size-large"><img src="https://wakatchi.dev/wp-content/uploads/2022/07/10-1024x683.png" alt="" class="wp-image-759"/></figure>
<p>TCCパターンは<strong>Try operations, Confirmation, and Cancellation</strong>の略で、コーディネーターとサービス群で構成するアーキテクチャです。TCCパターンは、アメリカで発表された論文をもとにしたアーキテクチャで、一部のECサイトやクラウドベンダでは実装されているようです。</p>
<div class="wp-block-vk-blocks-balloon vk_balloon vk_balloon-position-left vk_balloon-type-think vk_balloon-animation-none"><div class="vk_balloon_icon"><figure><img class="vk_balloon_icon_image vk_balloon_icon_image-type-normal " src="https://wakatchi.dev/wp-content/uploads/2022/07/cropped-wakatchi_icon_2.jpg" alt=""/><figcaption class="vk_balloon_icon_name">わかっち</figcaption></figure></div><div class="vk_balloon_content_outer"><div class="vk_balloon_content has-background-color has-luminous-vivid-amber-background-color vk_balloon_content_fullwidth"><span class="vk_balloon_content_before "></span><span class="vk_balloon_content_after "></span>
<p>論文「<em>Life beyond Distributed Transactions</em>」では、Tentative(暫定)-Confirm/Cancelとなっているけど、今ではTentativeはTryと表現することが多いようだね。</p>
</div></div></div>
<h3 class="wp-block-heading" id="vk-htags-4143dd69-c394-4007-a4da-975e5b704b2b">TCCパターンによるオーケストレーション</h3>
<p>コーディネーターに該当するサービスは、サービス群を呼び出しながら一連の処理を行います。サブルーチンを呼び出しながら処理するプログラムのイメージに似ています。</p>
<figure data-wp-context="{"imageId":"68b16818a4d11"}" data-wp-interactive="core/image" class="wp-block-image size-full wp-lightbox-container"><img data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="https://wakatchi.dev/wp-content/uploads/2022/07/microservices-tx-pattern-saga-tcc-orchestration-1.webp" alt="マイクロサービスのオーケストレーションを示す図。RESTを介してコーディネータがサービスA、サービスB、サービスCを管理し、それぞれが個別のデータベースに接続する構造を示す。" class="wp-image-2020"/><button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="拡大する"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
</svg>
</button><figcaption class="wp-element-caption">マイクロサービスのオーケストレーションアーキテクチャ(RESTベース)</figcaption></figure>
<p>TCCパターンは、TryフェーズとConfirm/Cancelフェーズの2フェーズによってトランザクションを制御します。</p>
<div class="wp-block-vk-blocks-border-box vk_borderBox vk_borderBox-background-transparent has-text-color has-vk-color-primary-dark-color is-style-vk_borderBox-style-solid-kado-tit-tab"><div class="vk_borderBox_title_container has-background has-vk-color-primary-dark-background-color"><i class=""></i><h4 class="vk_borderBox_title">Tryフェーズ</h4></div><div class="vk_borderBox_body">
<ol class="wp-block-list is-style-vk-numbered-circle-mark">
<li>コーディネータが各サービスに要求し、仮の状態を登録させます。</li>
</ol>
</div></div>
<div class="wp-block-vk-blocks-border-box vk_borderBox vk_borderBox-background-transparent has-text-color has-luminous-vivid-orange-color is-style-vk_borderBox-style-solid-kado-tit-tab"><div class="vk_borderBox_title_container has-background has-luminous-vivid-orange-background-color"><i class=""></i><h4 class="vk_borderBox_title">Confirm/Cancelフェーズ</h4></div><div class="vk_borderBox_body">
<ol class="wp-block-list is-style-vk-numbered-circle-mark">
<li>サービスの仮登録が全て完了した場合、サービスに正式登録を要求し、一連の処理が成功した旨を呼び出し元に返します。</li>
<li>Tryフェーズでいずれかのサービスがタイムアウトしたり登録できなかった場合、コーディネーターはキャンセル要求を送信します。</li>
<li>いずれかのサービスがタイムアウトしたり確定できなかった場合、コーディネーターは、成功するまで確認を再試行するか、一定回数再試行した後でバッチ処理、または、手動により修正します。</li>
</ol>
</div></div>
<div class="wp-block-image">
<figure data-wp-context="{"imageId":"68b16818a59b6"}" data-wp-interactive="core/image" class="aligncenter size-full wp-lightbox-container"><img data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="https://wakatchi.dev/wp-content/uploads/2022/07/tcc_orchestration-1.webp" alt="TCCオーケストレーションのシーケンス図。コーディネータがサービスAとサービスBに対してTryフェーズとConfirm/Cancelフェーズを実行。" class="wp-image-2027"/><button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="拡大する"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
</svg>
</button><figcaption class="wp-element-caption">TCC(Try-Confirm/Cancel)オーケストレーションのフロー</figcaption></figure></div>
<h3 class="wp-block-heading" id="vk-htags-fbff7f3d-6614-44fe-9e8f-547fe19aaf53">TCCパターンによるコレオグラフィ</h3>
<p>TCCパターンによるコレオグラフィは、コーディネータから呼び出されたサービスが、さらに別のサービスを呼び出す(ネストしたサービスの呼び出し)ケースが該当します。ネストして呼び出されたサービスのトランザクション制御は、オーケストレーションと同様です。</p>
<div class="wp-block-image">
<figure data-wp-context="{"imageId":"68b16818a5dbc"}" data-wp-interactive="core/image" class="aligncenter size-full wp-lightbox-container"><img data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="https://wakatchi.dev/wp-content/uploads/2022/07/microservices-tx-pattern-saga-tcc-choreography-1.webp" alt="RESTを用いたマイクロサービスのコーディネーションを示す図。コーディネータがサービスA、B、Cとやり取りし、それぞれのデータベースにアクセスしている。" class="wp-image-2034"/><button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="拡大する"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
</svg>
</button><figcaption class="wp-element-caption">RESTベースのマイクロサービスにおけるコーディネーションの流れ</figcaption></figure></div>
<h3 class="wp-block-heading" id="vk-htags-ad08127e-b51d-4192-b130-6f28040fd8b7">TCCパターンを実装する際の留意点</h3>
<h4 class="wp-block-heading" id="vk-htags-278ef56b-e087-458a-9aa2-005bfb213873">Try/Confirm/CancelのAPIを実装する</h4>
<p>TCCパターンでは、一連の処理を制御するコーディネーター(オーケストレーター)を用意し、複数リソース(サービス)の呼び出し制御を実装します。呼び出されるサービスは、Try/Confirm/Cancelの3つのAPIを実装する必要があります。</p>
<p></p>
<h4 class="wp-block-heading" id="vk-htags-2f7cc7cd-5a26-43b1-a956-64add9cc0242">処理完了に2倍かかることがある</h4>
<p>TCCパターンは、処理完了に2倍かかる場合があるので注意が必要です。これは、TCCパターンでは各サービス間で2回通信し、すべてのサービスからの試行要求の応答を受信したときにのみ確認フェーズを開始するためです。</p>
<h4 class="wp-block-heading" id="vk-htags-b4918433-827c-4533-92b3-af61739445e1">サービスを最終状態ではなく仮(保留)状態を経由する</h4>
<p>TCCパターンは、サービスを最終状態ではなく仮(保留)状態を経由するという特徴があり、キャンセル処理が簡単です。例えば、電子メールサービスの送信要求では、電子メールを送信準備としてマークし、確認要求で電子メールが送信されます。それに対応するキャンセル要求はマークするだけになります。</p>
<p>後述するSagaパターンは、トランザクションによって電子メールが送信され、対応する補償トランザクションで取消を説明する別の電子メールが送信されることになります。</p>
<h2 class="wp-block-heading" id="vk-htags-36796de0-61e9-4582-b05b-8184fa84af32">Sagaパターン</h2>
<figure class="wp-block-image size-large"><img src="https://wakatchi.dev/wp-content/uploads/2022/07/9-1024x683.png" alt="シンプルなアイコン画像で、書類のデザインの上に「SAGA」と書かれた緑色のバナーが重なっている。" class="wp-image-760"/></figure>
<p>Sagaパターンはイベント駆動のアーキテクチャです。Sagaパターンはマイクロサービス特有のものではなく、SOAでも採用されていたアーキテクチャです。また、Sagaパターンの元になる考え方は古く、<a href="https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf">1987年の論文</a>が元になっています。</p>
<p>Sagaパターンは、ロールバックを行うことができない代わりに、補償という考え方を提供しています。ロールバックを行うことができないので、代わりにインタラクションの操作を逆向きにした取り消し操作により、擬似的なロールバックを行います。</p>
<p>Sagaパターンは以下の流れで処理します。</p>
<div class="wp-block-vk-blocks-border-box vk_borderBox vk_borderBox-background-transparent has-text-color has-luminous-vivid-orange-color is-style-vk_borderBox-style-solid-kado-tit-onborder"><div class="vk_borderBox_title_container"><i class=""></i><h4 class="vk_borderBox_title">Sagaパターンの処理の流れ</h4></div><div class="vk_borderBox_body">
<ol class="wp-block-list is-style-vk-numbered-circle-mark">
<li>サービスは処理を行い、次のサービスに新しいイベントを送信します(下図の1、2)</li>
<li>イベントを受信したサービスは、処理を行い、次のサービスに新しいイベントを送信します(下図の3、4)</li>
<li>分散トランザクションのロールバックは自動ではありません。別の操作/イベントを実装する必要があります(下図のa〜d)</li>
</ol>
</div></div>
<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><a href="https://wakatchi.dev/wp-content/uploads/2022/07/2abeb692f4af38db13d0922416fc01eb.jpg"><img src="https://wakatchi.dev/wp-content/uploads/2022/07/2abeb692f4af38db13d0922416fc01eb-1024x479.jpg" alt="" class="wp-image-347" style="aspect-ratio:2.137787056367432;width:763px;height:auto"/></a></figure></div>
<p>【出展】</p>
<div style="border-color:#0000001f;border-radius:5px;border-width:1px; padding-top:var(--wp--preset--spacing--20);padding-right:var(--wp--preset--spacing--20);padding-bottom:var(--wp--preset--spacing--20);padding-left:var(--wp--preset--spacing--20);" class="wp-block-vk-blocks-blog-card has-border-color is-layout-flow wp-block-blog-card-is-layout-flow">https://thinkit.co.jp/article/14639?page=0%2C1(2018-11-13)</div>
<div class="wp-block-vk-blocks-spacer vk_spacer vk_spacer-type-margin-top"><div class="vk_block-margin-md--margin-top"></div></div>
<h3 class="wp-block-heading" id="vk-htags-fdcc4af6-e8be-41e5-8fd4-af18251e921b">Sagaパターンによるオーケストレーション</h3>
<p>オーケストレーションによるSagaパターンのアプローチは、コーディネーターとなるサービスが、Sagaの意思決定とビジネスロジックの順序付けを集中化します。各サービスに何をすべきか、いつ行うのかをコントロールするコーディネーター(Sagaオーケストレーター)を定義します。コーディネーターは、どのサービスを実行すべきかをMQのイベント/戻りイベントで各サービスと通信します。</p>
<p>なお、コーディネーターの各サービスへの要求にはREST通信の場合もあります。処理結果は、MQの戻りイベントで待ち受けます。</p>
<figure data-wp-context="{"imageId":"68b16818a82a6"}" data-wp-interactive="core/image" class="wp-block-image size-full wp-lightbox-container"><img data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="https://wakatchi.dev/wp-content/uploads/2025/03/microservices-tx-pattern-saga-saga-orchestration.webp" alt="マイクロサービスのイベント駆動型トランザクション(オーケストレーション方式)のフロー図" class="wp-image-2040" style="aspect-ratio:1.7777777777777777"/><button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="拡大する"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
</svg>
</button><figcaption class="wp-element-caption">マイクロサービスアーキテクチャにおけるオーケストレーション型Sagaパターンのイベント駆動トランザクションフロー</figcaption></figure>
<div class="wp-block-vk-blocks-spacer vk_spacer vk_spacer-type-margin-top"><div class="vk_block-margin-xl--margin-top"></div></div>
<div class="wp-block-vk-blocks-border-box vk_borderBox vk_borderBox-background-transparent has-text-color has-luminous-vivid-orange-color is-style-vk_borderBox-style-solid-kado-tit-onborder"><div class="vk_borderBox_title_container"><i class=""></i><h4 class="vk_borderBox_title">ユーザの注文を受注する簡単なシーケンス</h4></div><div class="vk_borderBox_body">
<ol class="wp-block-list is-style-vk-numbered-circle-mark">
<li>コーディネーター(受注サービス、Sagaオーケストレーター)は、注文のリクエストを受けると受注データ作成のトランザクションを開始し、受注データを仮登録します。</li>
<li>コーディネーターは在庫サービスに注文イベントを送信します。</li>
<li>在庫サービスは注文イベントを待ち受け、在庫を更新し、引当完了のイベントで応答します。</li>
<li>コーディネーターは引き当て完了のイベントを待ち受け、顧客サービスに支払い実行のイベントを送信します。</li>
<li>顧客サービスは支払い実行のイベントを待ち受け、決済を処理し、決済完了のイベントで応答します。</li>
<li>コーディネータは決済完了のイベントを待ち受け、受注を確定します。</li>
</ol>
</div></div>
<figure data-wp-context="{"imageId":"68b16818a8cd1"}" data-wp-interactive="core/image" class="wp-block-image size-large wp-lightbox-container"><img data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="https://wakatchi.dev/wp-content/uploads/2022/07/saga_orchestration-1-752x1024.webp" alt="マイクロサービスのSagaオーケストレーションのシーケンス図" class="wp-image-2049"/><button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="拡大する"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
</svg>
</button><figcaption class="wp-element-caption">マイクロサービスアーキテクチャにおけるSagaオーケストレーション</figcaption></figure>
<div class="wp-block-vk-blocks-spacer vk_spacer vk_spacer-type-margin-top"><div class="vk_block-margin-md--margin-top"></div></div>
<h3 class="wp-block-heading" id="vk-htags-8c910649-c476-4902-8af5-f41a416a428f">Sagaパターンによるコレオグラフィ</h3>
<p>Sagaパターンのコレオグラフィでは、各サービスは他のサービスのイベントを生成したり待ち受けたりして、処理するべきかどうかを決定します。コレオグラフィは、各サービスが単一のローカルトランザクションになります。 最初のトランザクションは、システム操作に対応する外部要求によって開始され、その後の各ステップは、前のトランザクションの完了によってトリガーされます。</p>
<figure data-wp-context="{"imageId":"68b16818a92bc"}" data-wp-interactive="core/image" class="wp-block-image size-full wp-lightbox-container"><img data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="https://wakatchi.dev/wp-content/uploads/2022/07/microservices-tx-pattern-saga-saga-choreography-1.webp" alt="マイクロサービスのSagaコレオグラフィートランザクションパターン。イベント駆動型メッセージキュー(MQ)を使用。" class="wp-image-2059"/><button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="拡大する"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
</svg>
</button></figure>
<div class="wp-block-vk-blocks-border-box vk_borderBox vk_borderBox-background-transparent has-text-color has-luminous-vivid-orange-color is-style-vk_borderBox-style-solid-kado-tit-onborder"><div class="vk_borderBox_title_container"><i class=""></i><h4 class="vk_borderBox_title">ユーザの注文を受注する簡単なシーケンス</h4></div><div class="vk_borderBox_body">
<ol class="wp-block-list is-style-vk-numbered-circle-mark">
<li>コーディネーター(受注サービス、Sagaオーケストレーター)は、注文のリクエストを受けると受注データ作成のトランザクションを開始し、受注データを仮登録します。</li>
<li>コーディネーターは在庫サービスに注文イベントを送信します。</li>
<li>在庫サービスは注文イベントを待ち受け、在庫を更新し、引当完了のイベントを送信します。</li>
<li>顧客サービスは引当完了のイベントを待ち受け、決済を処理し、決済完了のイベントを送信します。</li>
<li>コーディネータは決済完了のイベントを待ち受け、受注を確定します。</li>
</ol>
</div></div>
<figure data-wp-context="{"imageId":"68b16818a9bd9"}" data-wp-interactive="core/image" class="wp-block-image size-large wp-lightbox-container"><img data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="https://wakatchi.dev/wp-content/uploads/2022/07/saga_choreography-1-915x1024.webp" alt="マイクロサービスにおけるSagaパターンのオーケストレーション型のシーケンス図" class="wp-image-2066"/><button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="拡大する"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
</svg>
</button><figcaption class="wp-element-caption">Sagaパターンのオーケストレーション型による分散トランザクション管理のフロー</figcaption></figure>
<p>なお、注文の状態を追跡する必要がある場合、受注サービスはすべてのイベントを監視してその状態を更新する場合があります。</p>
<div class="wp-block-vk-blocks-spacer vk_spacer vk_spacer-type-margin-top"><div class="vk_block-margin-md--margin-top"></div></div>
<h3 class="wp-block-heading" id="vk-htags-aec4d351-1151-46ff-8a10-e76c9da195df">Sagaパターンのロールバック</h3>
<p>例えば、取引中に決済が失敗した場合は、処理を取り消すためのイベントを発行してロールバックを図ります。</p>
<div class="wp-block-vk-blocks-border-box vk_borderBox vk_borderBox-background-transparent has-text-color has-luminous-vivid-orange-color is-style-vk_borderBox-style-solid-kado-tit-onborder"><div class="vk_borderBox_title_container"><i class=""></i><h4 class="vk_borderBox_title">Sagaパターンのロールバック</h4></div><div class="vk_borderBox_body">
<ol class="wp-block-list is-style-vk-numbered-circle-mark">
<li>顧客サービスは 決済エラーのイベントを送信します。</li>
<li>受注サービスと在庫サービスの両方が決済エラーのイベントを待ち受けます。
<ul class="wp-block-list">
<li>在庫サービスは引当済みの在庫を取り消します。</li>
<li>受注サービスは、受注の状態を失敗として更新します。</li>
</ul>
</li>
</ol>
</div></div>
<figure data-wp-context="{"imageId":"68b16818aa7cc"}" data-wp-interactive="core/image" class="wp-block-image size-large wp-lightbox-container"><img data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="https://wakatchi.dev/wp-content/uploads/2022/07/saga_choreography_rollback-1-721x1024.webp" alt="「コーディネータ」注文サービス、在庫サービス、顧客サービス、MQを含むシーケンス図。決済処理中にエラーが発生した場合の処理が示されている。" class="wp-image-2072"/><button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="拡大する"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
</svg>
</button><figcaption class="wp-element-caption">分散トランザクションにおけるロールバック処理</figcaption></figure>
<p>イベントを受信するすべての消費者(Consumer)が参照するトランザクションをすぐに知ることができるように、発生するイベントに各トランザクションに共通の識別子を定義することが重要になります。</p>
<p></p>
<h3 class="wp-block-heading" id="vk-htags-4d8d2442-aae8-4a2e-974d-df629c30b740">Sagaパターンを実装する際の留意点</h3>
<p>Sagaパターンによる処理は、イベントを消費(Consume)することが起点となり、別のイベントを発行(Publish)することで、次のサービスに処理が伝搬されます。一連の処理はこれをチェーンし、最後の処理が一番最初の処理に完了イベントをPublishして終了します。Publishするイベントは、リトライを考慮し、各サービスが管理するデータベースにも保持します。</p>
<figure class="wp-block-image size-large"><a href="https://wakatchi.dev/wp-content/uploads/2022/07/Richardson-microservices-part5-local-transaction-e1449727484579.png"><img src="https://wakatchi.dev/wp-content/uploads/2022/07/Richardson-microservices-part5-local-transaction-e1449727484579-1024x638.png" alt="" class="wp-image-349"/></a></figure>
<p>【出展】</p>
<p><a href="https://www.nginx.com/blog/event-driven-data-management-microservices/">https://www.nginx.com/blog/event-driven-data-management-microservices/</a></p>
<p><strong><span data-color="#fcb900" style="background: linear-gradient(transparent 60%,rgba(252, 185, 0, 0.7) 0);" class="vk_highlighter">Sagaパターンはサービス間は疎結合で柔軟であり、ビジネスロジックへの影響が局所化される点では、TCCパターンよりも優れていると言えます。</span></strong>一方で、<span data-color="#f78da7" style="background: linear-gradient(transparent 60%,rgba(247, 141, 167, 0.7) 0);" class="vk_highlighter">補償トランザクションの実現可能性を考慮する必要</span>があり、チェーンが長くなり過ぎないよう注意が必要です。</p>
<h2 class="wp-block-heading" id="vk-htags-4eda22ae-d380-4959-ac28-9730cb52cf7b">まとめ</h2>
<p>今回は、TCCパターンとSagaパターンという、なかなか聞き慣れないアーキテクチャに触れてみました。いかがだったでしょうか?一部の先進的な企業から情報が発信されつつありますが、まだこれらパターンの導入事例は少なく、まだ普及段階ではないかもしれません。</p>
<p>昨今はKubernetesによるコンテナオーケストレーションやistioなどによるサービスメッシュが普及してきており、マイクロサービスアーキテクチャの導入が比較的容易になりつつありますが、それら技術はトランザクション制御を容易にするものではありません。マイクロサービスアーキテクチャの実現に向けては、引き続きトランザクションには十分な考慮が必要になるでしょう。</p>
<div class="wp-block-vk-blocks-balloon vk_balloon vk_balloon-position-left vk_balloon-type-think vk_balloon-animation-none"><div class="vk_balloon_icon"><figure><img class="vk_balloon_icon_image vk_balloon_icon_image-type-normal " src="https://wakatchi.dev/wp-content/uploads/2022/07/cropped-wakatchi_icon_2.jpg" alt=""/><figcaption class="vk_balloon_icon_name">わかっち</figcaption></figure></div><div class="vk_balloon_content_outer"><div class="vk_balloon_content has-background-color has-luminous-vivid-amber-background-color "><span class="vk_balloon_content_before "></span><span class="vk_balloon_content_after "></span>
<p>特に基幹システムのマイクロサービス化に向けては、トランザクション制御が極めて重要になるよ</p>
</div></div></div>
<p>本記事が、マイクロサービス導入に向けて少しでもお役に立てれば幸いです。</p>
<p>最後までお読みいただき、ありがとうございました!</p>
こんにちわ、サイト管理者のわかっち (@wakatchi_tech ) です。
質問者
マイクロサービスでシステム作ったらトランザクション管理がしんどいです。
こんな質問をいただきました。
マイクロサービスアーキテクチャでシステムを構築した際、更新対象が複数のサービスをまたがる場合は、トランザクションの扱いが途端に難しくなります。なかでも、障害発生時に各サービス間の処理をロールバックするためには補償(補正)トランザクションが必要になり、複雑なトランザクション制御が求められます。
補償トランザクションとは、処理の途中で失敗した場合に、それを取り消すことで実行結果を打ち消す処理のことです。補償トランザクションの実装は、打ち消す処理を提供するサービスと、それを呼び出すサービスの双方に負担があり、設計や実装が複雑になりがちです。
トランザクションには、1つのトランザクション内で1つのリソース(DBなど)処理のみ行うローカルトランザクションと、1つのトランザクション内で複数のリソース処理を行うグローバルトランザクションがあります。グローバルトランザクションは分散トランザクションとも呼ばれ、その実装としてはX/Open XA やWS-Transactionなどが知られています。
マイクロサービスアーキテクチャは、ローカルトランザクションを勧める一方で、グローバルトランザクション(分散トランザクション)は推奨しません。 分散トランザクションは、複雑で不具合の原因になる可能性が高いためです。マイクロサービスアーキテクチャで推奨しているのは、分散トランザクションではなく、複数リソースの結果整合性を利用することです。複数リソースの結果整合性を利用する方法として、TCC(Try-Confirm/Cancel) パターンとSagaパターン と呼ばれるアーキテクチャがあります。
この記事は、TCCパターンとSagaパターンが、マイクロサービスのトランザクション特性に合わせてどう使われるかをまとめていきます。
マイクロサービスをまたがるトランザクションは、サービス連携方法とサービス間の関連付けで分類できます。サービス連携方法には、リクエスト・リプライ とイベントドリブン の2つの方式があります。サービス間の関連付けには、オーケストレーション(中央集権型) とコレオグラフィ(分散型) があります。さて、それぞれどのような特徴があるのでしょうか。
REST通信による連携方式です。
自身の処理の一部を、他のサービスに依頼します。
イベントの発生によって特定のサービス処理が駆動される連携方式です。
既に発生した事柄を他のサービスに通知することで、発生した事象に応じた処理を実行させます。
中央の指揮者がサービスの呼び出しをコントロールします。
各サービスは中央からのリクエストによってロジックを実行し、実行結果をレスポンスとして中央に返す、リクエスト・リプライの方式になります。
同期通信に適しています。
個々のサービスが自律的に動き、あらかじめ定義された他のサービスに働きかけます。
個々のサービスは、定められた条件に従って自律して稼動し、条件に合致した他のサービスにデータを送ります。
非同期通信に適しています。
サービス連携の方式方法を縦軸、サービス間の関連付けを横軸にトランザクションのタイプを分類すると、TCCパターンとSagaパターンをこのように図示できます。
それでは、TCCパターンとSagaパターンとはどのようなものでしょうか。
TCCパターンはTry operations, Confirmation, and Cancellation の略で、コーディネーターとサービス群で構成するアーキテクチャです。TCCパターンは、アメリカで発表された論文をもとにしたアーキテクチャで、一部のECサイトやクラウドベンダでは実装されているようです。
わかっち
論文「Life beyond Distributed Transactions 」では、Tentative(暫定)-Confirm/Cancelとなっているけど、今ではTentativeはTryと表現することが多いようだね。
コーディネーターに該当するサービスは、サービス群を呼び出しながら一連の処理を行います。サブルーチンを呼び出しながら処理するプログラムのイメージに似ています。
マイクロサービスのオーケストレーションアーキテクチャ(RESTベース)
TCCパターンは、TryフェーズとConfirm/Cancelフェーズの2フェーズによってトランザクションを制御します。
Tryフェーズ
コーディネータが各サービスに要求し、仮の状態を登録させます。
Confirm/Cancelフェーズ
サービスの仮登録が全て完了した場合、サービスに正式登録を要求し、一連の処理が成功した旨を呼び出し元に返します。
Tryフェーズでいずれかのサービスがタイムアウトしたり登録できなかった場合、コーディネーターはキャンセル要求を送信します。
いずれかのサービスがタイムアウトしたり確定できなかった場合、コーディネーターは、成功するまで確認を再試行するか、一定回数再試行した後でバッチ処理、または、手動により修正します。
TCC(Try-Confirm/Cancel)オーケストレーションのフロー
TCCパターンによるコレオグラフィは、コーディネータから呼び出されたサービスが、さらに別のサービスを呼び出す(ネストしたサービスの呼び出し)ケースが該当します。ネストして呼び出されたサービスのトランザクション制御は、オーケストレーションと同様です。
RESTベースのマイクロサービスにおけるコーディネーションの流れ
TCCパターンでは、一連の処理を制御するコーディネーター(オーケストレーター)を用意し、複数リソース(サービス)の呼び出し制御を実装します。呼び出されるサービスは、Try/Confirm/Cancelの3つのAPIを実装する必要があります。
TCCパターンは、処理完了に2倍かかる場合があるので注意が必要です。これは、TCCパターンでは各サービス間で2回通信し、すべてのサービスからの試行要求の応答を受信したときにのみ確認フェーズを開始するためです。
TCCパターンは、サービスを最終状態ではなく仮(保留)状態を経由するという特徴があり、キャンセル処理が簡単です。例えば、電子メールサービスの送信要求では、電子メールを送信準備としてマークし、確認要求で電子メールが送信されます。それに対応するキャンセル要求はマークするだけになります。
後述するSagaパターンは、トランザクションによって電子メールが送信され、対応する補償トランザクションで取消を説明する別の電子メールが送信されることになります。
Sagaパターンはイベント駆動のアーキテクチャです。Sagaパターンはマイクロサービス特有のものではなく、SOAでも採用されていたアーキテクチャです。また、Sagaパターンの元になる考え方は古く、1987年の論文 が元になっています。
Sagaパターンは、ロールバックを行うことができない代わりに、補償という考え方を提供しています。ロールバックを行うことができないので、代わりにインタラクションの操作を逆向きにした取り消し操作により、擬似的なロールバックを行います。
Sagaパターンは以下の流れで処理します。
Sagaパターンの処理の流れ
サービスは処理を行い、次のサービスに新しいイベントを送信します(下図の1、2)
イベントを受信したサービスは、処理を行い、次のサービスに新しいイベントを送信します(下図の3、4)
分散トランザクションのロールバックは自動ではありません。別の操作/イベントを実装する必要があります(下図のa〜d)
【出展】
https://thinkit.co.jp/article/14639?page=0%2C1(2018-11-13)
オーケストレーションによるSagaパターンのアプローチは、コーディネーターとなるサービスが、Sagaの意思決定とビジネスロジックの順序付けを集中化します。各サービスに何をすべきか、いつ行うのかをコントロールするコーディネーター(Sagaオーケストレーター)を定義します。コーディネーターは、どのサービスを実行すべきかをMQのイベント/戻りイベントで各サービスと通信します。
なお、コーディネーターの各サービスへの要求にはREST通信の場合もあります。処理結果は、MQの戻りイベントで待ち受けます。
マイクロサービスアーキテクチャにおけるオーケストレーション型Sagaパターンのイベント駆動トランザクションフロー
ユーザの注文を受注する簡単なシーケンス
コーディネーター(受注サービス、Sagaオーケストレーター)は、注文のリクエストを受けると受注データ作成のトランザクションを開始し、受注データを仮登録します。
コーディネーターは在庫サービスに注文イベントを送信します。
在庫サービスは注文イベントを待ち受け、在庫を更新し、引当完了のイベントで応答します。
コーディネーターは引き当て完了のイベントを待ち受け、顧客サービスに支払い実行のイベントを送信します。
顧客サービスは支払い実行のイベントを待ち受け、決済を処理し、決済完了のイベントで応答します。
コーディネータは決済完了のイベントを待ち受け、受注を確定します。
マイクロサービスアーキテクチャにおけるSagaオーケストレーション
Sagaパターンのコレオグラフィでは、各サービスは他のサービスのイベントを生成したり待ち受けたりして、処理するべきかどうかを決定します。コレオグラフィは、各サービスが単一のローカルトランザクションになります。 最初のトランザクションは、システム操作に対応する外部要求によって開始され、その後の各ステップは、前のトランザクションの完了によってトリガーされます。
ユーザの注文を受注する簡単なシーケンス
コーディネーター(受注サービス、Sagaオーケストレーター)は、注文のリクエストを受けると受注データ作成のトランザクションを開始し、受注データを仮登録します。
コーディネーターは在庫サービスに注文イベントを送信します。
在庫サービスは注文イベントを待ち受け、在庫を更新し、引当完了のイベントを送信します。
顧客サービスは引当完了のイベントを待ち受け、決済を処理し、決済完了のイベントを送信します。
コーディネータは決済完了のイベントを待ち受け、受注を確定します。
Sagaパターンのオーケストレーション型による分散トランザクション管理のフロー
なお、注文の状態を追跡する必要がある場合、受注サービスはすべてのイベントを監視してその状態を更新する場合があります。
例えば、取引中に決済が失敗した場合は、処理を取り消すためのイベントを発行してロールバックを図ります。
Sagaパターンのロールバック
顧客サービスは 決済エラーのイベントを送信します。
受注サービスと在庫サービスの両方が決済エラーのイベントを待ち受けます。
在庫サービスは引当済みの在庫を取り消します。
受注サービスは、受注の状態を失敗として更新します。
分散トランザクションにおけるロールバック処理
イベントを受信するすべての消費者(Consumer)が参照するトランザクションをすぐに知ることができるように、発生するイベントに各トランザクションに共通の識別子を定義することが重要になります。
Sagaパターンによる処理は、イベントを消費(Consume)することが起点となり、別のイベントを発行(Publish)することで、次のサービスに処理が伝搬されます。一連の処理はこれをチェーンし、最後の処理が一番最初の処理に完了イベントをPublishして終了します。Publishするイベントは、リトライを考慮し、各サービスが管理するデータベースにも保持します。
【出展】
https://www.nginx.com/blog/event-driven-data-management-microservices/
Sagaパターンはサービス間は疎結合で柔軟であり、ビジネスロジックへの影響が局所化される点では、TCCパターンよりも優れていると言えます。 一方で、補償トランザクションの実現可能性を考慮する必要 があり、チェーンが長くなり過ぎないよう注意が必要です。
今回は、TCCパターンとSagaパターンという、なかなか聞き慣れないアーキテクチャに触れてみました。いかがだったでしょうか?一部の先進的な企業から情報が発信されつつありますが、まだこれらパターンの導入事例は少なく、まだ普及段階ではないかもしれません。
昨今はKubernetesによるコンテナオーケストレーションやistioなどによるサービスメッシュが普及してきており、マイクロサービスアーキテクチャの導入が比較的容易になりつつありますが、それら技術はトランザクション制御を容易にするものではありません。マイクロサービスアーキテクチャの実現に向けては、引き続きトランザクションには十分な考慮が必要になるでしょう。
わかっち
特に基幹システムのマイクロサービス化に向けては、トランザクション制御が極めて重要になるよ
本記事が、マイクロサービス導入に向けて少しでもお役に立てれば幸いです。
最後までお読みいただき、ありがとうございました!