マイクロサービス では、1つのビジネストランザクションが複数サービスのデータ更新にまたがることが多い。 単一データベースのローカルトランザクション(ACID)のようには扱えないため、代替の整合性戦略が必要になる。
2相コミット(2PC)とその限界¶
分散トランザクションを実現する古典的な手法。コーディネーターが全参加者に「準備できるか(Prepare)」を問い合わせ、 全員がYesならCommit、誰か1人でもNoならRollbackを指示する。
参加者はPrepare後、コーディネーターの指示が来るまでロックを保持し続ける必要がある(可用性が低下する)
コーディネーターが単一障害点になる
マイクロサービス間(特に異なるデータストア技術)をまたぐ2PCの実装は現実的に困難なことが多い
これらの理由から、マイクロサービスの文脈では2PCの代わりに Saga パターン が使われることが多い。
Sagaパターン¶
1つの大きなトランザクションを、各サービスが担当する ローカルトランザクションの連鎖 として実行する。 途中で失敗した場合は、それまでに成功したステップを打ち消す 補償トランザクション(Compensating Transaction) を逆順に実行することで、全体の整合性を(結果整合性として)保つ。
# 注文フロー: 在庫予約 → 決済 → 配送手配
# いずれかが失敗したら、それ以前の成功済みステップを補償する
def place_order_saga(order):
reserve_inventory(order) # Step 1
try:
charge_payment(order) # Step 2
except PaymentFailed:
release_inventory(order) # Step 1 の補償
raise
try:
arrange_shipping(order) # Step 3
except ShippingFailed:
refund_payment(order) # Step 2 の補償
release_inventory(order) # Step 1 の補償
raise補償トランザクションは「なかったことにする」処理であり、真の意味でのロールバックではない(例:一度送った確認メールを 取り消すことはできず、代わりに「キャンセルのお知らせ」を送るなど)。そのため、各ステップはべき等かつ 補償可能であるように設計する必要がある。
オーケストレーション型 vs コレオグラフィ型¶
オーケストレーション型(Orchestration):中心となるオーケストレーターが各サービスに指示を出し、 Sagaの進行を一元管理する。フローが追いやすい反面、オーケストレーターが複雑化しやすい。
コレオグラフィ型(Choreography):中心の管理者を置かず、各サービスがイベント駆動アーキテクチャ でイベントを発行・購読し合いながら、自律的に次のステップへ連鎖させる。サービス間の結合は弱いが、 全体のフローが複数サービスのイベントハンドラに分散し、追跡が難しくなりやすい。
# コレオグラフィ型の例: 各サービスが前段のイベントを購読して自律的に動く
class InventoryService:
def on_order_created(self, event):
self.reserve(event.order_id)
publish("InventoryReserved", order_id=event.order_id)
class PaymentService:
def on_inventory_reserved(self, event):
self.charge(event.order_id)
publish("PaymentCompleted", order_id=event.order_id)サービス数が少ないうちはコレオグラフィ型がシンプルだが、ステップ数が増えるとフローの全体像が見えにくくなるため、 オーケストレーション型に移行することも多い。