メッセージ配信機能を作った話と、例外処理の考え方について

こんにちは、YOUTRUSTの今井(YOUTRUST / X)です。 今回は、先日メッセージ配信機能を実装したので、その機能の実装内容・工夫したこと・実装から学んだことについてご紹介します。

実装背景

YOUTRUSTでは、これまでユーザー様やコミュニティオーナー様に対し、イベント参加者やキャンペーン当選者へのご案内をYOUTRUST公式アカウントからユーザー様お一人お一人にメッセージで配信していました。この配信作業の業務効率改善を目的に、社内メンバーが利用する管理画面からメッセージを配信できるように実装しました。

要求・要件

YOUTRUST公式アカウントから、複数ユーザー様へメッセージを配信したい。

上記の要求を満たすために、以下の要件で実装を進めていきました。

  • 社内メンバーが利用する管理画面(以下Admin)より、メッセージを配信する
  • メッセージ配信元は、YOUTRUST公式アカウントなど特定のアカウントのみに限定する
    • ※YOUTRUSTでは、つながりのあるユーザーに対してのみメッセージできます
  • メッセージ配信対象は、管理画面で指定できるようにする
  • 誤配信の防止を目的に、この配信機能を利用できるユーザーを一部の社内メンバーに限定する

実装

処理の流れを解説していきます

1. Adminの「チャット配信登録画面」より、配信情報をDBに保存

Admin画面で配信操作者が、配信元ユーザー、配信対象ユーザー、本文などの配信に必要な情報を登録し、データベースに保存します。

2. Adminの「配信実行ボタン」を押し、配信実行APIをリクエスト

配信を実行するためにPOSTリクエストを送信します。この時、配信済みメッセージの再送を防止するため、配信対象メッセージのステータスを確認します。(配信メッセージテーブル内にstatusカラムを持たせて管理)

メッセージのステータスは以下の通りです。ステータスを細かく設定したのですが、理由については後述します。

  • 未配信
  • 配信準備中
  • 配信実行中
  • 配信完了
  • 配信失敗

3. メッセージ一斉配信ジョブを実行

  • 上記APIリクエストを受け取ったタイミングで、配信対象メッセージのステータスを配信準備中に更新
  • 配信準備中にステータス更新後、メッセージ一斉配信ジョブをエンキュー
  • 将来的に予約配信を実装することも見据えて、ジョブを実行するようにしました

4. メッセージ一斉配信ジョブにて、ECSタスクを呼び出しメッセージ配信処理を開始

  • 配信処理が実行されたタイミングで、配信対象メッセージのステータスを配信実行中に更新
  • ループ処理で各ユーザーに対してメッセージを配信

実装上工夫したこと

全ての配信対象ユーザーに対して、配信処理を逐次実行

配信処理の実行手段として、以下の2つを検討しました。議論の中で生まれたそれぞれのメリット・デメリットがこちらです。

①配信対象それぞれに対して非同期ジョブで並列実行

  • メリット
    • 並列実行により全体の配信時間が短縮される
  • デメリット
    • 全ての配信が終了したことを判別するために、各ユーザーの配信結果を取得する必要があり、コードが複雑になる

②全配信対象に対して逐次実行

  • メリット
    • 全ての配信対象に対して逐次的に配信処理を実行することで、全ての配信が終了したことの判別が容易になる
  • デメリット
    • 配信対象のユーザー数分だけ、配信時間が増加する

今回は配信時間を厳密に守る必要がなかったため、配信時間がかかることを許容し、コードが複雑にならない②逐次配信を選択しました。

配信処理結果の検知

配信操作者が配信処理結果を正確に把握するために、ステータスを5つに分けて定義しました。「配信実行したけど、配信成功したのかな?」など、操作者の不安を解消するのが目的です。

以下のように各処理の段階で、ステータスの更新を実行してから、次の処理に進むように実装しました。

  • 未配信(pending):初期設定のステータス。
  • 配信準備中(preparing):メッセージ一斉配信ジョブをエンキューするタイミングで更新。ECSタスクの起動まで一定時間かかるため、その間のステータス。
  • 配信実行中(running):ECSタスクが起動したタイミングで更新。
  • 配信完了(completed):ECSタスクが正常終了したタイミングで更新。
  • 配信失敗(failed):ECSタスクが失敗したタイミングで更新。

例外処理

配信対象のユーザー数によっては、配信処理に30分以上かかると見積もり段階で想定していました。この処理中に配信が失敗した場合の対応について工夫をしています。

結論として、想定内の失敗ケースと想定外の失敗ケースで対応を分けました。

  • 想定内の失敗ケースの場合
    • エラーログを残し、配信処理は継続。
    • 例:配信対象のユーザー様が既に退会済の場合など
  • 想定外の失敗ケースの場合
    • エラーログを残し、配信処理を中断しロールバック

今回の実装の過程で、先輩から失敗には以下の2種類あることを教わりました。先輩が野球を例に説明してくださり、とても分かりやすかったのでご紹介します。

野球で例えた場合

  • 想定内の失敗→プレー中の選手のエラー。選手の落球など。試合は続行できる。
  • 想定外の失敗→試合中に落雷。場合によらず、試合は中断した方がいい。

実装の中で学んだこと

良い設計・実装を行うためには、とにかくエッジケースを沢山考慮に入れることが重要ということを学びました。エンジニアとして1年業務を経験し、正常系の処理の設計・実装をすることは慣れてきましたが、例外処理については苦手意識がありました。今回の機能を実装する中で、先輩から例外処理の考慮漏れに関するレビューを多くいただき、学びになりました。

最後に

YOUTRUSTではエンジニアを積極的に採用しています!

様々なポジションで求人を出しているので、ご興味のある方は是非以下の募集をご覧ください!

herp.careers