背景・目的
最近、トランザクションについて考える機会があり、そのなかでアウトボックスパターンについて触れたので整理します。
まとめ
下記に特徴をまとめます。
| 特徴 | 説明 |
|---|---|
| トランザクションアウトボックスパターン | 単一オペレーションにデータベース書き込みオペレーションとメッセージまたはイベント通知の両方が含まれる場合に分散システムで発生する二重書き込みオペレーションの問題を解決する |
| 二重書き込みオペレーションとは | アプリケーションが 2 つの異なるシステムに書き込みを行う場合に発生する |
| マイクロサービスの例 | マイクロサービスが、データをデータベースに保持し、メッセージを送信して他のシステムに通知する必要がある場合など、これらのオペレーションのいずれかが失敗すると、データが不整合になる可能性がある |
| ユースケース | ・イベント駆動型アーキテクチャ ・アトミック性の保証 ・イベントソーシング |
概要
下記を基に整理します。
Intent
- トランザクションアウトボックスパターン
- 単一オペレーションにデータベース書き込みオペレーションとメッセージまたはイベント通知の両方が含まれる場合に分散システムで発生する二重書き込みオペレーションの問題を解決する
- 二重書き込みオペレーション
- アプリケーションが 2 つの異なるシステムに書き込みを行う場合に発生する
- 例:マイクロサービスがデータをデータベースに保持し、メッセージを送信して他のシステムに通知する必要がある場合など
- これらのオペレーションのいずれかが失敗すると、データが不整合になる可能性がある
Motivation
- DB更新とイベント通知はアトミック(全部成功 or 全部失敗)である必要がある
- 起こりうる不整合は下記の通り
- DB成功 → イベント失敗
- DB失敗 → イベント成功
Applicability
下記の場合に使用する
- イベント駆動型アーキテクチャ
- DB更新をトリガーにイベント通知
- アトミック性の保証
- 2つのサービス間のオペレーションを確実に成功させる
- イベントソーシング
- すべての状態変更をイベントとして記録
Issues and considerations
- 重複メッセージ対策
- イベント処理サービスが同じメッセージを複数回送信する可能性がある
- 処理済みメッセージIDを記録し、受信側を冪等に実装する
- 通知の順序
- DB更新順とイベント送信順を一致させる
- イベントストアを使用してデータストアをポイントインタイムで復元できるイベントソーシングパターンにとって重要
- 順序が正しくないと、データの品質が低下する可能性がある
- 通知の順序が保持されないと、結果整合性とデータベースのロールバックが問題をさらに悪化させる可能性がある
- ロールバック
- トランザクションがロールバックされた場合は、イベント通知を送信しない
- サービスレベルのトランザクション処理
- トランザクションがデータストアの更新を必要とするサービスにまたがる場合は、Saga オーケストレーションパターンを使用してデータストア全体のデータの完全性を維持する
Implementation
- Flight Serviceは、DBに書き込み、Payment Serviceにイベント通知を送信する
- Message brokerは、メッセージとイベントをPayment Serviceに伝える。Message brokerに障害が発生すると、Payment Serviceは更新を受信できなくなる
Flight databaseの更新に失敗しても通知が送信された場合、Payment Serviceはイベント通知に基づいて支払いを処理する。これがダウンストリームのデータ不整合の原因になる。
Implementation using AWS services
- マイクロサービスはLambdaを使用
- プライマリーデータベースは、RDS
- メッセージブローカーは、SQS
- トランザクションをコミットした後にフライトサービスに障害が発生すると、イベント通知が送信されない可能性がある
- トランザクションが失敗してロールバックされたにも関わらずイベント通知が送信され、Payment Serviceが支払い処理する場合がある
上記の問題対処するには、アウトボックステーブルまたは、CDCを使用する
Using an outbox table with a relational database
アウトボックステーブルには、フライトサービスからのすべてのイベントがタイムスタンプおよびシーケンス番号とともに保存される
1〜2. フライトテーブルが更新されると、同じトランザクションでアウトボックステーブルも更新される。
3〜4. 別のサービス(イベント処理サービス)が、アウトボックスから読み取り、SQSにイベントを送信する
- SQSは、更に処理するためにイベントに関するメッセージを支払いサービスに送信する
- SQSのスタンダードキューは、メッセージが少なくとも1会配信され、メッセージが失われないことを保証する
- 同じメッセージを複数回配信される可能性があるため、冪等は保証しておく必要がある
- フライトテーブルの更新または、アウトボックステーブルの更新が失敗した場合、トランザクション全体がロールバックされるので、ダウンストリームのデータに不整合は生じない
- 上記は、トランザクションアウトボックスアーキテクチャは、RDSデータベースを使用して実装されている
- イベント処理サービスがアウトボックステーブルを読み取ると、コミットされたトランザクションに含まれる行のみを認識し、イベントのメッセージをSQSキューに格納する
- SQSキューは、Payment Serviceにより読み込まれ処理される
- この設計では、タイムスタンプとシーケンス番号を使用して二重書き込みオペレーションの問題が解決され、メッセージとイベントの順序が保持される
Using change data capture (CDC)
- 一部のデータベースでは、変更されたデータをキャプチャするためのアイテムレベルの変更の発行がサポートされている
- 変更された項目を特定し、それに応じてイベント通知を送信できる
- これにより、更新を追跡するテーブルをもう 1 つ作成する手間が省ける
- フライトサービスによって開始されたイベントは、同じアイテムの別の属性に保存される
- DDBは、CDC更新をサポートするキーバリュー NoSQL
- DynamoDB Streamsに発行している
- DynamoDB Streams は、時系列シーケンスを使用して DynamoDB テーブル内のアイテムレベルの変更に関連する情報の流れをキャプチャする
- DynamoDB テーブルでストリームを有効にすることで、トランザクションアウトボックスパターンを実装できる
- フライトテーブルが更新されると、変更されたデータが DynamoDB Streams によってキャプチャされ、イベント処理サービスがストリームをポーリングして新しいレコードを探す
- 新しいストリームレコードが使用可能になると、Lambda 関数はイベントのメッセージを同期的に SQS キューに配置し、さらに処理する
- DynamoDB アイテムに属性を追加して、必要に応じてタイムスタンプとシーケンス番号をキャプチャし、実装の堅牢性を高めることができる
考察
今回、アウトボックスパターンを学びました。次回は、実際に動かしてみます。
参考







