フューチャー技術ブログ

非同期設計ガイドラインを公開しました

はじめに

こんにちは。TIG(Technology Innovation Group)の亀井です。

フューチャー社内の有志メンバーで 非同期設計ガイドライン を作成し、公開しました!

本記事では、ガイドライン策定の背景や、ガイドラインで取り上げている設計のポイントをピックアップしてご紹介します。

本ガイドライン策定の背景

かつて非同期処理といえば、専門的なメッセージングミドルウェアを必要とする、一部のミッションクリティカルなシステムで採用される特別な技術でした。フューチャーでも独自のミドルウェアフレームワークを構築して、大量データをリアルタイムで処理するような仕組みを数々の工夫を凝らして実装してきました。

一方で、昨今ではAWS SQSなどのクラウドネイティブなサービスの登場により、応答時間の長い処理のオフロードなどを目的に非同期処理を取り入れることは珍しくなくなりました。

しかし、非同期特有の難しさは依然として存在しており、「処理のトレース」「デバッグ」「リラン」が困難である点や、データストアにまたがる場合のデータ整合性担保、障害発生時のリカバリなど、同期処理にはない複雑さが伴います。

本ガイドラインでは、非同期導入のメリットを享受しつつ、これらの「本質的な難しさ」を回避、または適切に管理するための実務的な設計論点と指針を提供することを目的としています。

本ガイドラインの対象読者

本ガイドラインは、バックエンドシステムにおいて、Web APIやバッチ処理からのトリガーによる非同期メッセージング(キュー)を用いた処理を設計・実装するエンジニア・アーキテクトを対象としています。

ガイドラインの内容紹介

ガイドラインは多岐にわたる論点をカバーしていますが、ここでは特に議論になりやすいポイントをいくつか抜粋して紹介します。

1. 非同期化の判断基準と使い分け

「なんとなく非同期にする」のではなく、明確な判断基準を持つことを推奨しています。ガイドラインでは以下の3つの観点を提示しています。

  • 処理時間と応答性: ファイル生成など、同期的に待つとUXが低下する場合
  • 負荷の平準化: 突発的な大量アクセスなどバースト的な負荷を直接受けずに平準化したい場合
  • レジリエンスの向上: 外部システムへの依存を切り離し、メイン処理の継続性を高めたい場合

一方で、これらに該当しない場合やリソースの増強や運用で回避できるケースでは、システム全体の複雑性を下げるために「同期処理」を選択することも合理的であり、安易な非同期化を避けることも推奨としています。

2. 論理構成方針

スケーラビリティや障害分離の観点から、「1業務タスク = 1キュー = 1コンシューマー」 という構成を推奨しています。

複数の異なる業務(例: メール送信と決済処理)を1つのキューに混在させると、障害時の切り分けが困難になったり、業務ごとの優先度に応じた流量制御(スロットリング)ができなくなるため業務タスクの単位でキューおよびコンシューマーを分けることを推奨としています。
管理するリソース数は増えますが、AWS Lambdaにおける「予約済み同時実行数」などのリソース制限を有効活用するためにも、シンプルな構成にしておく方がよいでしょう。

3. データ整合性とトランザクションアウトボックスパターン

非同期処理で頻出する課題に「DB更新とメッセージ送信の整合性」があります。DBのコミットには成功したがメッセージ送信に失敗する(あるいはその逆)といった事態を防ぐため、トランザクションアウトボックスパターン の採用是非についても触れています。

トランザクションアウトボックスパターン

  1. 各コンシューマーは自身の担う業務ロジック処理と、処理完了を示すステータス更新を1トランザクションで実施する。
  2. 後続コンシューマーへのメッセージ連携は、別のトランザクションで行う。

これにより、1→2の順序性の担保と、2単体でのリトライが可能となる。

ガイドラインでは、ロストメッセージやファントムメッセージの対策として、ステータス管理テーブルを用いたアプローチや、プロデューサー側での登録とコンシューマー側でのチェックによる整合性担保について比較・解説しています。

用語 状態 発生する問題
ロストメッセージ DBコミット成功 / キュー送信失敗 メッセージが消失し、後続処理が動かない
ファントムメッセージ キュー送信成功 / DBコミット失敗 存在しないデータを処理しようとしてエラーになる

4. 順序保証とFIFOキュー

厳密な順序保証が必要な場合、SQS FIFOキューなどの利用が検討されますが、スループットの低下や「Head-of-Line (HOL) ブロッキング」のリスクが伴います。

本ガイドラインでは、「可能な限り順序制御を必要としない設計(冪等性の確保や、メッセージの独立性)」 を目指すことを推奨しています。その上で、どうしても順序保証が必要な場合のグルーピング戦略(MessageGroupIdの設計)についても言及しています。

5. エラーハンドリングとDLQ

処理失敗時のメッセージ退避先であるDead Letter Queue(DLQ)について、「二段構え」 の構成を推奨しています。

  1. アプリケーション制御: バリデーションエラーなど、既知のエラーはアプリが即時にDLQへ退避させる(ブロッキングを最小化するため)
  2. インフラ制御: クラッシュなど予期せぬエラーは、インフラ(SQSのmaxReceiveCountなど)の機能で救済する

これにより、FIFOキュー利用時のブロッキング時間を最小化しつつ、予期せぬ障害時にもメッセージをロストしない堅牢性を確保します。

まとめ

非同期設計ガイドラインは、現代の分散システム開発において避けては通れない「非同期処理」の設計判断を支援するために作成しました。

Webフロントエンド設計ガイドライン や バッチ設計ガイドライン と同様に、本ガイドラインも社内外のフィードバックを受けて継続的にアップデートしていく予定です。

GitHub上でのIssueやPRも大歓迎ですので、ぜひご覧ください。

関連ガイドライン