SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Developers Summit 2025 セッションレポート

整合性を守り抜く非同期処理アーキテクチャ設計

【14-D-7】データの整合性を保つ非同期処理アーキテクチャパターン

データベースとメッセージキューの整合性をどう保つか

 非同期処理の設計において3つ目のテーマとなるのが、「データベースとメッセージキューの整合性をどう保つか」である。データベースに処理結果を保存すると同時に、イベント通知のためにメッセージを送信する――このような構成では、2つの処理の間にトランザクションが張られないため、一方だけが成功してしまうリスクが生じる。

 たとえば、データベースへの保存は成功したが、メッセージ送信には失敗した場合、外部の処理が発火せずに不整合が起こる。逆に、メッセージ送信が成功してもデータベースへのコミットに失敗すれば、「なかったはずの処理」が先に進んでしまう可能性がある。

非同期処理における一方の処理失敗による整合性の破綻リスク
非同期処理における一方の処理失敗による整合性の破綻リスク

 こうした問題に対して有効なのが、Transactional Outboxパターンだ。木田氏はこの手法について、『Designing Data-Intensive Applications』でも紹介されていると触れたうえで、次のように説明する。

 「『outbox』という専用のテーブルを用意し、通常のデータ保存と同じトランザクション内でイベントを書き込む。メッセージの送信はその後に行う——つまり、『保存→送信』というステップを明確に分けることで、整合性を担保することができる」(木田氏)

 この方法には、実装上さらに2つのパターンがある。

ポーリングパブリッシャー

 ワーカーやバッチジョブを使ってoutboxテーブルを定期的にポーリングし、中に溜まったイベントを検出してメッセージングキューに送信する。シンプルな実装で始めやすい一方、レイテンシやポーリング間隔の設計が課題となる。

トランザクションログテーリング

 より高度な手法として、データベースのトランザクションログ(たとえばMySQLのbinlogやPostgreSQLのWAL)をテーリングし、変更をリアルタイムに検知してメッセージを送信する方法もある。こちらは高精度・低レイテンシな処理が可能だが、実装のハードルはやや高い。

 木田氏は、「最近では、こうした仕組みを支えるプロダクトやフレームワークも増えてきており、Kafka連携やDynamoDB Streamsなどを活用すれば、導入のハードルも下がってきている」と付け加える。

 もうひとつ重要な論点が、重複メッセージの処理だ。たとえばAmazon SQSのようなキューでは、At-Least-Once(少なくとも1回の配信保証)が前提となっており、条件によっては同じメッセージが複数回届いてしまうことがある。

 このときに重要なのが、冪等性(べきとうせい:idempotency)の設計である。たとえば「ユーザー登録メールを送る」といった処理であれば、1回送っても3回送っても最悪の影響は小さい。しかし「ユーザーのポイントを加算する」「在庫を減らす」などの処理では、二重実行が致命的な問題を引き起こしかねない。

 そのため、もう一つのアプローチとして挙げられたのが「重複メッセージを破棄する」仕組みだ。木田氏は次のように語る。

 「メッセージにはユニークなIDを付けて、処理前にデータベースへ保存する。その際にユニーク制約をかけておけば、2度目の処理時には制約違反でエラーが発生し、処理を止めることができます」(木田氏)

 メッセージングとデータ保存の両立は非同期処理において避けて通れないテーマだ。Transactional Outboxパターンはその要となる設計アプローチであり、併せて冪等性やユニーク性をいかに担保するかまで含めて考えることが、堅牢な非同期処理設計には欠かせないのだ。

信頼性を高めるスキーマ定義と、4つの実践ポイント

 最後に取り上げられたテーマは、非同期処理における「メッセージのスキーマ定義」である。

 非同期メッセージングでは、JSON形式を用いてイベントを表現するケースが多く見られる。たとえば「カード発行リクエスト」というイベントであれば、イベントIDやユーザーIDといった属性が含まれる。このような構造に対して、木田氏はJSON Schemaを使ってスキーマを明示的に定義する重要性を説いた。

 スキーマを定義しておけば、送信側・受信側の双方でバリデーションを実行できるようになり、メッセージの構造的な正当性が担保される。結果として、非同期処理全体の信頼性を高めることにつながる。

 各言語向けにスキーマ定義用のライブラリも充実しており、たとえばRubyではjson-schemaというGemが利用可能である。木田氏は、実際のコード例として、スキーマとイベントを定義し、ユーザーIDの欠落によってバリデーションエラーが発生する動作を示した。

 木田氏が紹介した4つのポイントはいずれも、非同期処理に内在する複雑さを構造的に捉え、整合性を維持しながら業務の拡張に対応するうえで重要な視点である。すべてを一度に取り入れるのは難しくとも、1つずつ実践することで、開発の質と速度の両立に近づくことができるだろう。

 「今日の内容の中から、明日すぐにでも実践できるものを一つでも持ち帰ってもらえたら」。そう語り終えた木田氏の言葉には、実践に裏打ちされた確かな手応えがにじんでいた。

この記事は参考になりましたか?

Developers Summit 2025 セッションレポート連載記事一覧

もっと読む

この記事の著者

夏野 かおる(ナツノ カオル)

 博士。本業は研究者。副業で編集プロダクションを経営する。BtoB領域を中心に、多数の企業案件を手がける。専門はテクノロジー全般で、デザイン、サイバーセキュリティ、組織論、ドローンなどに強みを持つ。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山出 高士(ヤマデ タカシ)

雑誌や広告写真で活動。東京書籍刊「くらべるシリーズ」でも写真を担当。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

CodeZine編集部(コードジンヘンシュウブ)

CodeZineは、株式会社翔泳社が運営するソフトウェア開発者向けのWebメディアです。「デベロッパーの成長と課題解決に貢献するメディア」をコンセプトに、現場で役立つ最新情報を日々お届けします。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/21421 2025/10/28 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング