- はじめに
- enpayウォレットとは
- 負荷試験実施の目的
- 負荷試験のシナリオ
- 想定される負荷を見積もる
- 実装方法
- 計測するメトリクス
- 結果
- 想定のリクエストを送る
- 想定の5倍のリクエストを送る
- 想定の10倍のリクエストを送る
- 今後の課題
はじめに
株式会社エンペイでバックエンドエンジニアをやっている佐野です!
この記事はエンペイ Advent Calendar 2023 の12/1分の投稿です。
enpayウォレットをリリースする前に実施した負荷試験の取り組みについて紹介します。
enpayウォレットとは
enpayウォレットとは、集金業務支援サービス「enpay(エンペイ)」を導入している教育施設からの請求に対して、かんたんにお支払いができるモバイルアプリです。
enpayウォレットで支払うと、支払い金額の0.5%が自己負担なく教育施設に還元されます。
詳しくはこちらのリリースノートをご覧ください。
負荷試験実施の目的
想定するユーザー数に耐えられるインフラの設定、 バックエンドのコードになっているかを判断するために実施しました。
具体的には以下の3つを大まかな観点としました。
- 想定されるピーク時の負荷がかかった時に十分なパフォーマンスが得られること
- 想定以上の負荷がかかった時の挙動が問題にならないこと
- システムの限界値が許容範囲であること
アプリのコア機能であるチャージと支払いの機能をテストすることで、高負荷時でもスムーズな決済が行われることを確認しました。
負荷試験のシナリオ
明確に問題が起こるであろうAPIがわかっている場合は、そのAPIに対して負荷をかけていけば価値のあるデータが取れるはずです。
負荷試験を実施した時点ではenpayウォレットはリリースしていないため、どのAPIを実行した時に問題が発生するか、どれくらいの負荷をかけると問題になるのか分かりませんでした。
そこで実際に多くのユーザーがチャージ~支払いを実施すると仮定し、その時のシステムの挙動をテストすることにしました。
ユーザーがチャージ~支払いの画面に遷移し、画面のボタンを押下し決済を完了するまでにどのようなリクエストが呼ばれるのかを全て洗い出しました。
リクエストを送ることで負荷が集中するシナリオを再現しました。
想定される負荷を見積もる
enpayウォレットはenpayでの支払いで使用するアプリのため、enpayのデータベースに保持している過去の支払い履歴が役に立つと考えました。
enpayがサービスを開始してから支払いが集中した時の決済の回数を年-月-日-時間-分単位で割り出しました。
enpayでの支払いにはPayPay・LINEPay・クレジットカード等の様々な決済手段があります。 将来的にenpayウォレットが決済手段の全体の何割ほど利用されるかの予測を事業責任者の方に確認し、割り出した回数に割合をかけることで、秒間リクエスト数を試算しました。
このリクエスト数を元に負荷試験を実施しました。
実装方法
リリースまでに時間が限られていたこと、実装のしやすさを考慮してGoのスクリプトをローカルから実行することにしました。
以下は指定された秒数内に指定された回数のリクエストを均等な間隔で実行するコード例のサンプルです。
/*
userList 負荷試験に使うユーザーのスライス
seconds 負荷をかける期間
calls チャージ~支払いの実行回数
*/
func exec(userList []User, seconds, calls int) {
interval := time.Duration(float64(seconds) * float64(time.Second) / float64(calls))
ticker := time.NewTicker(interval)
defer ticker.Stop()
var wg sync.WaitGroup
counter := 0
for counter < calls {
select {
case <-ticker.C:
valueIndex := counter % len(userList)
wg.Add(1)
go sendRequests(&wg, userList[valueIndex])
counter++
}
}
wg.Wait()
}
// チャージ~支払いまでに発生するリクエストを実行する関数
func sendRequests(wg *sync.WaitGroup, user User) {
defer wg.Done()
// リクエスト処理...
}
上記のコードを実行するには事前にユーザー、ユーザーのアクセストークンを取得する必要があります。
事前データを用意するスクリプトを作成し何度でもテストできるような仕組みを作りました。
計測するメトリクス
enpayウォレットは複数のECS Service・Lambda関数、データベースはRDSを使用しています。
CloudWatch ダッシュボードを使用して負荷をかけた時に著しくパフォーマンスが低下している箇所がないかを確認しました。
- ECS Service
- メモリ使用率
- CPU使用率
- Lambda
- Durations
- Errors
- RDS
- CPU使用率
- コネクション数
ECS Serviceのレイテンシ ・エラーの計測にはDatadogを使用しました。
結果
想定のリクエストを送る
秒間リクエスト数を計算するで出した値を元に、想定するリクエスト数を送った時の挙動を確認します。
以下はECS Serviceごとのメトリクスです。
06:05頃にCPU使用率が少しだけ増加していますがその後すぐに低下しているため正常に動いていることが分かります。
RDS, Lambdaのメトリクスも大きく変化せず問題のない結果となりました。
想定の5倍のリクエストを送る
次に想定の5倍のリクエスト送ってみます。
ECSのサービスごとのメトリクスには大きな変化は見られませんでした。
以下はRDS, RDSProxyのコネクションのメトリクスです。
06:15頃から若干コネクション数が増えていることが分かります。
その他のLambda, ECSのメトリクスに異常はみられませんでした。
想定の10倍のリクエストを送る
最後に想定の10倍のリクエストを送った時は、500エラーを検知することができました。
ここは外部のサービスに接続している箇所で、同時にリクエストできる回数が決まっています。
想定のリクエストの10倍を送った場合でも、ECS, RDS, Lambdaのメトリクスに異常は見られませんでした。
そのため、enpayウォレットの負荷の限界は現状外部サービスに依存していることが分かりました。
想定の10倍の負荷のため外部APIのスループットの引き上げは実施しませんでした。
今後ユーザー数の増加が見込まれるため定期的に負荷試験を実施して、安定して稼働することを確認していきたいです。
今後の課題
現状開発者のローカルからリクエストを送る仕組みになっています。
リクエスト数や負荷をかけるトータルの時間が伸びていくと開発作業に影響が出ることや、マシンの性能に依存するため、負荷試験用のインスタンスをAWSに設置し、インスタンス上で負荷試験を実施できる構成にしたいと考えています。
明日はsanfrecce_osakaさんが書きます!お楽しみに!