Mirai Translate TECH BLOG

株式会社みらい翻訳のテックブログ

Outboxパターンについて

みらい翻訳のバックエンドエンジニアのtoshと申します。

私が所属しているチームは主に社内のDXの向上をミッションとしているチームで、現在はマイクロサービス化に取り組むための基盤の構築やルールの整備をしています。

マイクロサービス化を進める上で事前に検討しておくべきことの一つとして、サービス間の通信方法が挙げられると思います。

一般的にはRESTのような同期通信をするケースも多いかと思いますが、メッセージングを利用して非同期で連携する仕組みがあると様々なメリットがあります。本稿ではこちらについては詳細は解説しませんが、こちらのブログなどが参考になります。

マイクロサービスとメッセージングのなぜ [概要編] - 赤帽エンジニアブログ

上記の記事によると、メッセージングシステムやイベント駆動アーキテクチャの採用により、サービス間を疎結合に保ち、システム全体の安定性の向上が見込めるとのことです。

このような事情があり、弊社ではメッセージングシステムを共通基盤として構築しています。

実際にメッセージングシステムを利用する際の使い方の一つとして、Outboxパターンを利用すると便利なことがあると考えているため紹介します。

概要

Outboxパターンは、メッセージングシステムを利用する上でのトランザクション制御の信頼性を高めるための設計パターンです。

信頼性を高めるための方法として、メッセージングシステムに直接書き込むのではなくいったんDBに書き込むことにより、他のDB書き込みとのトランザクション同期を取りやすくなります。また、可用性が高く実装難易度の低いRDBMSのメリットを享受しつつ、メッセージングシステムを利用することができます。

コンテキスト

あるシステム間の連携にメッセージングシステムを使用する際に、多くの場合データベースの更新とメッセージングシステムへのイベントの送信と2種類の更新処理を実行します。

例えばECサイトのようなシステムで、注文システムが在庫システムデータを連携する処理を例に考えてみます。

  1. 注文アプリケーションが注文データを注文DBに書き込む

  2. メッセージングシステムに在庫アプリケーションに連携するメッセージを書き込む

このような処理をしたい際に問題になるのが、上記の1の処理と2の処理をトランザクショナルに実行することが難しい点です。DBへの書き込みが成功した場合のみメッセージング処理をさせなければいけないですし、メッセージの送信に失敗した場合は確実にトランザクションロールバックさせたいはずです。

DBの1トランザクションで両方の処理を行う場合、トランザクション内で外部システムの連携を行うことはトランザクションの信頼性の低下と処理時間の長期化の原因となり、このような実装は極力避けるべきと考えます。

2フェーズコミットを実装すればできないこともないですが、外部システム同士の結合度が上がってしまいます。また、DBの書き込みとメッセージングが必要な全ての箇所で2フェーズコミットを実装するのはあまり現実的ではないと思います。そのため、2フェーズコミットもあまり現実的な選択肢ではないと考えます。

 

課題

  • DBの更新処理が成功した時だけメッセージを送信し、DBがロールバックされた時にメッセージを送信したくない

  • 上記の実装のために2フェーズコミット(分散トランザクション)を使いたくない

Outboxパターンとは

Outboxテーブルと呼ばれるメッセージを書き込む専用のテーブルと、DBに書き込まれたデータをメッセージングシステムに送信するシステムを用意します(以降このシステムをメッセージリレーと呼びます)。

Outboxテーブルは更新対象のデータと同一DBに用意します。

実際の処理では、データの更新と同一トランザクション内でOutboxテーブルにメッセージを書き込みます。メッセージリレーがOutboxテーブルに書き込まれたデータをメッセージングシステムに送信します。

このような実装により、データの更新とOutboxテーブルへの書き込みがコミットされた時のみメッセージが送信されます。また、DBがロールバックされた場合にはメッセージは送信されません。

 

Outboxパターンのイメージ図



Outboxパターンの利点

先ほどあげた2つの課題を解消してくれます。

  • DBの更新処理が成功した時だけメッセージを送信し、DBがロールバックされた時にメッセージを送信したくない

→    DBの更新処理が成功した時のみメッセージを送信することができます

→    2フェーズコミットを利用する必要はなく、DBの更新処理のみで実現可能です

 

また、別の視点として

  • 仮にメッセージングシステムに障害が発生していても、DBが稼働しているのならデータの更新処理とOutboxテーブルへの書き込み(実質メッセージの書き込み)処理を成功させることができます。この時、メッセージングシステムが復旧後、正常にメッセージング処理が行われます

  • 一般的にメッセージングシステムへの書き込みのコードを書くよりもDBへの書き込み処理の方が実装が容易です

  • 信頼性の高い大元のデータがDBにあるため、データの損失や破損のリスクが低いです。また、障害発生時の対応も容易です。

     

 

Outboxパターンのデメリット

  • メッセージングリレーを挟むため、送信先にメッセージが到達するまでの時間が増えます

→    リアルタイムに近い処理を要求される場合は選択肢から外れます

→    大量の書き込みが発生する場合、Outboxパターンを使わない場合と比較してパフォーマンスが落ちます

 

総括

Outboxパターンのメリット・デメリットを上げました。上記の通り、1つの処理の中でDBの更新処理とメッセージング処理のどちらも行う場合、有力な選択肢となります。

反対に、高い書き込みパフォーマンスが要求される場合やメッセージング処理単体の場合はシンプルなメッセージングシステムへの書き込みの方が有力な選択肢となり得るかと思います。