KARTE MessageにおけるFCMのSendAll API廃止対応について

KARTE MessageにおけるFCMのSendAll API廃止対応について

自己紹介

初めまして、プレイドでエンジニアインターンをしている小林雅史と申します。プレイド内ではあだ名であるmercy(マーシー)で呼ばれています。

立命館大学の修士1年生で、大学は情報理工学部、大学院は情報理工学研究科とコンピュータやプログラミングについて学び、研究しています。プログラミングは大学入学と同時に授業で取り組んできました。プログラミングに関しては授業だけでなく、ハッカソンなどでさまざまなアプリケーションを作成しました。特にハックツハッカソンが大好きで2度優勝しています。また大学生向けのバス時刻表アプリを先輩から引き継ぎ運用していたりもしています。3年生の時には学生IT団体watnowの代表として企業様とのイベント団体内ハッカソンを立ち上げたりとコミュニティの運営も行いました。

プレイドでは2023年の4月からインターンとして勤務していて、最初は認証基盤の開発を行いました。その後Redisの監視を簡単にDatadogで行うための社内向けツールを開発し、2023年10月ごろからKARTE Messageというプロダクトの開発を行うチームに加わり、開発を行っています。

KARTE Messageについて

KARTE Messageは、データを元にメールやプッシュ通知、LINE メッセージの送信をノーコードで行うことができるため、マーケターなどの非エンジニアの方でも簡単に大規模なコンテンツ配信が可能なマーケティングオートメーションツールとなっています。

課題

KARTE Messageの機能であるスマホアプリ向けのプッシュ通知配信では月間1億通を超える規模の配信を行っています。

今回、プッシュ通知に利用しているFCMのsendAll APIが2024年の6月20日に廃止されることが決定し、この期日までに今後も引き続き使用できるAPIに乗り換える必要がありました。

また、これまで使用していたsendAll APIでは、最大500件の通知をバッチ的に送信することができましたが、これと完全に互換性のあるAPIは現在用意されておらず、1通1通を個別に送信する必要があります。

今回私はこのリプレース作業を担当することになりました。

テスト送信の実装・切り替え

最初に、移行時のリスクが小さいテスト送信から置き換えることとなりました。本番送信もテスト送信も1通1通を個別に送信するロジックは同じであり、違いは送信数です。そのため、テスト送信の実装を先に行いリリースすることで、バグがあった際の影響範囲をテスト送信のみに抑えつつ新しい実装の信頼性を検証することができます。テスト送信の置き換えで行った作業は次の通りです。

  1. 既存のコードとドキュメントを読み理解する
  2. テスト送信の実装
  3. テスト送信の実装の置き換え

1. 既存のコードとドキュメントを読み理解する

既に動いているシステムをリプレースするため、まずは既存のコードを理解する必要がありました。コードを読むだけではなかなかコードの振る舞いを把握するのが難しいので、単体テストを新たに追加しました。

2. テスト送信の実装

リプレースする際のリスクを最小にするために、新規実装では既存の実装と同じケースの単体テストを書き、既存実装、新規実装ともにテストケースと結果が同じになることを確認することで、実装ミスがないかを検証しました。また、新規実装では、Goの実装が約1200行なのに対してテストコードは約5600行あります。テストはローカル環境で実行することができ、キャッシュがない状態でも30秒ほどで終了します。また、これらのテストはGithubActionsで自動で実行されており、生産性向上につながっています。

3. テスト送信の実装の置き換え

無事に実装が終わりテストも通ったら本番環境のテスト送信をリプレースしました。これまでの業務でバグの修正や新機能の実装をした事はあったものの、今正常に動いているものを全く新しいコードで作成されたシステムに置き換える経験は初めてでとても緊張しました。リリースの実行は社員さんに行ってもらい、検証環境で最終動作確認を行いました。テスト送信のリプレース後には、ユーザ企業様からバグ報告が数件ありました。その中でもGoへのリクエストのJSONのフィールドにnullがあった場合、Goのjson packageではnilに変換されるという仕様があり、nilのハンドリングがうまくできていないコードでバグが発生したのが印象的です。いくつかのバグを修正し、無事テスト送信の実装をsendAll APIからsend APIに切り替えることができました。

本番配信の実装・負荷試験・切り替え

テスト送信の置き換えが終了しバグの修正も終わると、本番配信も新しいAPIに置き換えます。本番配信の置き換えで行った作業は次の通りです。

  1. 本番送信の実装
  2. 負荷試験
  3. カナリアリリース

1. 本番送信の実装

本番送信の実装に関しては本番送信とテスト送信のロジックを共通化したため、基本的にテスト送信の実装を流用する形で実装しました。

2. 負荷試験

本番配信では、KARTE Messageを使用されている企業様が大量のアプリユーザに向けてプッシュ通知を送信します。また、お客様が設定した速度で正しく配信する必要があります。そのため、アプリケーションに負荷がかかっても正常に動作することを検証する必要がありました。KARTE MessageはインフラにGCPのGKEを使用しているため、正しい速度で負荷がかかっても送信できる適切なk8sのスケーリング設定を見つける必要がありました。

負荷試験の環境には本番環境とは完全に切り離された検証環境を使用しました。検証環境は本番環境と同じシステムが動作しており、スケーリングの設定などが違うだけで本番環境と同じようにシステムが動作します。

負荷試験に関しては、社員さんからは時間かかってもいいからとりあえずやってみよう!といった感じで言われたので、ほぼ初めてGKEとその設定が書かれたk8s用のyamlファイルと戦うこととなりました。本番配信は、データベース、配信サーバ(今回作成したもの)、配信準備サーバなどさまざまなシステムが連携して動くことで実行されます。サーバやデータベースの状況はリアルタイムでdatadogで確認しました。

負荷試験の目標値は16667rps(時速6000万)となりました。これはAPI置き換え前のシステムで過去に負荷試験をした際の最大値となっています。

最初はスケーリングの設定を何も変えずに、2.7prs、277rps、1667rps、5555rpsと上げて行きました。すると、時速5555rpsの試験では設定した速度で配信されず1100rpsほどでしか配信できませんでした。ここから数時間悩んで色々設定を見ているうちに、配信サーバのスケーリング設定が問題になっていることに気づきました。最大node数とpod数が非常に小さい数に設定されていて、一定以上の負荷に耐えられないようになっていました。そこで、podの最大数を500、nodeの最大数を250に設定し再度負荷試験を行ったところ、16667rpsを達成できました。この時、配信サーバのpod数は100台ほどにスケーリングしていました。

目標値をクリアしたので、次に最適な設定を探します。KARTE Messageではサービスの性質上、リクエストのスパイクが発生しやすいです。また、深夜帯などは配信がなくサーバに負荷がかからない状態になります。そのため、スパイクに耐えられる最小pod数と、最大負荷に耐えられる最大pod数を見つける必要があります。そこで、1podあたりどれくらいの配信数を捌けるのかを調査しました。調査するために、pod数を20台固定にし少しづつ少ない配信数から負荷試験を行い、限界の配信速度を見つけ出しました。20台のpodで2700万/hの配信を行うことができました。pod1台で約135万/hの配信が可能です。そこで、6000万/hの配信に耐えられるpod数より少し余裕を持たせた50を、最大pod数としました。

負荷試験だけで10営業日ほどかかりました、負荷試験の勘所やk8sの特性を理解するのに時間を要しました。初めてやることは、手探りで頑張ってみることも大事ですが、社員さんなど経験がある人にある程度、勘所を先に教わっておくともっと効率良くできたかなと感じました。学生が簡単に経験することができない、k8sを用いたシステムの負荷試験ができたことは非常に良い経験となりました。

3. カナリアリリース

負荷試験も終わり、ついに本番配信を新しいAPIに置き換えます。全ユーザに一斉に適用してしまうと、もしもバグなどがあった場合に影響範囲が広くなってしまうため、一部のお客様から順次新しいAPIを適用しました。APIの切り替えは環境変数で指定した特定のお客様の時のみ新しいAPIが実行されるように実装を行いました。環境変数で切り替えを行うことで、もし新しいAPIの実装に問題があった際に、修正やロールバックしたアプリケーションをデプロイすることなく、環境変数を切り替えるだけで既存のAPIの実装に戻せるため、環境変数を活用したカナリアリリースを行いました。カナリアリリースは、置き換え前と比べて、悪化している指標がないか確認しながら約1ヶ月かけて全てのお客様の配信APIを新しいAPIに切り替えました。

リプレース結果

リプレースは実装を始めてから約5ヶ月で終了しました。結果として、新しいAPIに切り替えたことによる大きな障害はありませんでした。そして、既存実装よりもエラー率も下がりました。

最後に

今回のプッシュ通知配信で使用するAPIのリプレースの実装、試験、移行は全て自分の手で行いました。もちろん社員さんの助けなしではできなかったです。今回のプロジェクトを通して長期インターンでしか体験できない貴重な経験ができました。また、自分は京都に住んでいるのでリモート勤務だったですが、地方に住んでいる学生についてはリモートでも受け入れてくれる環境は非常にありがたかったです。他のインターン生が行ったことについては、ぜひプレイドのエンジニアブログをご覧ください[1][2][3][4][5]。このような経験をしてみたい方、プレイドに興味を持った方は下記からプレイドのエンジニアインターンに応募してみてください!

[1] インターンシップで全社のMongoDBをアップデートした話

[2] プレイドインターン体験記: はじめての開発インターンを通じて得た学び

[3] プレイドでの開発インターン: 3ヶ月のフルタイムエンジニアとしての挑戦を経て

[4] プレイドのインターン体験記:分散システムとプロダクト貢献意識の両面からの学び

[5] MongoDB AtlasからBigQueryへのリアルタイムレプリケーション

記事をシェア