こんにちは。 アソビューのPlatformSREチームの頭島です。
最近、社内にメール配信基盤を構築したことについてご紹介します。
アソビューで扱うメールには大きく2つありますが、今回は一括配信メールのメールマガジン配信機能をSaaSから社内基盤に移行した話です。
- 一括配信メール(メールマガジンなど)
- トランザクションメール(購入完了メールなど)
背景
コスト削減したい
社内基盤移行前は、CRMとメールマガジン配信機能を兼ねているSaaSを利用していました。
SaaSには以下を含めて機能が豊富で重宝していましたが、年々メール配信数増加に比例してSaaSの利用料金も増加しており、コストが無視できない金額になりました。
- リッチなエディタUI
- メール配信条件フィルター機能
- 顧客管理機能を利用して細かいデータを利用したフィルターが作成可能
- メール配信結果の確認機能
メール配信機能実装時の認知負荷を下げたい
アソビューではトランザクションメール、一部の一括配信メールは、複数のメール配信SaaSのAPIを利用して実現しています。 しかし、配信予約はSaaSA、リアルタイム配信はSaaSBなど、ユースケースによってサービスを使い分ける必要があります。
例えばエラー調査時にSaaS側のログを確認する場合には、各ユースケースでどのメール配信サービスを使っているのかを確認する必要があります。 また、新しい機能を実装する場合には各サービスの機能を調査して、どのサービスを利用するか検討する必要があります。
これらの認知負荷拡大やプロダクトチームの開発生産性低下を防ぎたいモチベーションがありました。
アーキテクチャ
要件
- 250万通程度のメールを2〜3時間程度で送信完了できること
- メール送信件数/失敗数/開封数/クリック数などのメール配信結果を取得できること
実際のアーキテクチャ
結果
250万通程度のメールを2〜3時間程度で送信完了できること
パフォーマンスが1.5倍になりました! 元々3時間前後かかっていたところ、1時間半弱程度で送信可能です。
運用開始以降、一度に数百万件配信されるメールマガジンを含め、1日あたり合計で約1,000万件のメール配信を実現しています。
コスト削減したい
メールマガジン配信費用としては、年間で約74%ほど削減できました!
アーキテクチャのポイントを3つご紹介します。
SESを複数リージョンで利用してリクエスト分散する
目的としては以下になります。
- 高スループットの実現
- スケーラビリティの担保
- レピュテーションリスクのコントロール
- 可用性
高スループットの実現
SESではリージョン毎に「日次送信クォータ」と「最大送信レート」の制限が存在します。 これらはクォータ引き上げ申請にて上限緩和できますが、要件を満たすためには限界がありました。 複数リージョンにリクエストを分散することで、理論上は元々のスループット×リージョン数のスループット向上を期待できます。
スケーラビリティの担保
このアーキテクチャでは、Regionを新規追加することで容易にスケールできます。 今後メール配信数が増加することが予想できたためこの仕組みを導入しました。
レピュテーションリスクのコントロール
メール配信においてIPアドレスのレピュテーション管理はつきものです。 今回SESではマネージドIPアドレスを利用しているため、バウンス率と苦情率を基準値未満に維持する必要があります。 特定のリージョンのレピュテーションが悪化した場合は、DynamoDBに登録している分散率を更新することでコントロールできます。
可用性
SESの特定リージョンで障害が発生した場合も、同じくDynamoDBに登録している分散率を更新することで障害の影響範囲を局所化できます。
Lambdaのスループット制御
このアーキテクチャでは、非同期でAPI GatewayがLambdaを呼び出し、Lambdaが水平スケールしてリクエストを捌きます。 そして、今回のメール送信部分の実装はSESv2のSendBulkTemplatedEmailAPIを利用して、1リクエストあたり50件のメールを送信しています。
ここの注意点が、SESの「最大送信レート」を超えないようにLambdaをスケールさせる必要があることです。
例えば、SESの「最大送信レート」が秒間100通、リージョンが2つの場合は、LambdaはSESに秒間4回のリクエストしか送信しないように制限する必要があります。 これを解決するために、以下の実装にしています。
LambdaのSESへのリクエスト回数を秒間あたり1回に制限する
guava の RateLimiter を利用して、Lambda1台あたり秒間1回のみ処理するように制御しました。
Lambdaの同時実行数を制限する
上記SESへのリクエスト回数の制限に加えて、Lambdaの同時実行数を制限することで、SESの「最大送信レート」のしきい値を超えず、かつ最大限のスループットを実現しました。
例えば、SESの「最大送信レート」が秒間100通、リージョンが3つの場合は、Lambdaの同時実行数は5台にします。
この場合は、50 * 5 = 250通
が各リージョンのSESに分散してリクエストします。
※リクエストのリージョン分散が偏る可能性を考慮して最大値より余裕のある送信数に設定します。
SES配信メトリクスの可視化
現状メトリクスの閲覧手段として、目的別に2つ用意しました。
- redash : プロダクトメンバー向けに、Athenaクエリを利用して気軽にメトリクスを調査できる環境
- Tableau : マーケティングメンバー向けに、例えばメールマガジン毎にメール送信件数/失敗数/開封数/クリック数などを確認できる環境
運用開始して得られた知見
バウンスリストの管理方法
初回のメールマガジン配信以降、数回にわたってバウンスレートが上昇する事象が発生しました。 原因は、SaaSで暗黙的に管理されていたバウンスリストを今回のメール配信基盤に適用できていないためでした。
暫定対応としてSaaSのバウンスリストを抽出して、SESのアカウントレベルのサプレッションリストに一括登録しました。 以降はバウンスレートは低い状態を維持できるようになっています。
ただし、今後はアプリケーション側、もしくはユースケース毎にバウンスリストを管理するレイヤーを別途用意する必要があると考えています。 理由としては、今後トランザクションメールを社内基盤に移行する場合を考慮すると、必ずメールを届ける必要があるユースケースが発生するためです。 SESのサプレッションリストはすべてのメール配信に適用されるため、必ずメール配信を試みることができる仕組みが必要です。
今後の展望
トランザクションメールを社内基盤に移行予定です。 一括配信メールとは性質が大きく違うため、ドメイン知識や業務フローをキャッチアップしながら、設計する必要がありそうです。 例えば以下のような考慮事項があります。
メール配信のリアルタイム性
トランザクションメールは、リアルタイムにかつ、確実にメール配信する必要があります。 リトライ機能、冪等性、先ほど挙げたバウンスリストの管理方法など含めて重要な部分になります。
検索機能
トランザクションメールはユーザーにとって重要な情報になるため、問い合わせ数が多い傾向があります。 問い合わせ対応にあたって、ユーザーへのメール配信結果の確認や、場合によってはメール内容を閲覧できる必要性があるかもしれません。
最後に
アソビューではより良いプロダクトを世の中に届けられるよう共に挑戦していくエンジニアを募集しています。 カジュアル面談もやっておりますので、お気軽にエントリーください! お待ちしております。 www.asoview.co.jp