ある機能で、ユーザーからこんな問い合わせがありました。
「予約確定ボタンを押すと、しばらく画面が固まる」
調査を始めたものの、
- エラーは発生していない
- CloudWatchにも異常はない
- RDSの負荷も高くない
ログを見る限り、どこにも問題は見当たりませんでした。
最終的に原因の特定に役立ったのが AWS X-Ray で、改善につながったのが Lambda Provisioned Concurrency でした。
システム構成
当時の構成はシンプルなものでした。
モバイルアプリ
↓
API Gateway
↓
Lambda
↓
RDS
平均すると、APIの応答時間は約3秒。
決して極端に遅いわけではありませんが、予約操作としては待ち時間が長く感じられる状態でした。
最初に疑ったこと
まずは一般的な原因から確認しました。
- SQLが遅いのではないか
- RDSの負荷が高いのではないか
- Lambdaのメモリ不足ではないか
- ネットワークに問題があるのではないか
それぞれ確認しましたが、どれも大きな問題は見つかりません。
Lambdaのメモリを増やしてみても、体感はほとんど変わりませんでした。
X-Rayでリクエストを確認
そこで、Lambdaのトレーシングを有効にしてX-Rayで確認しました。
トレースを見ると、処理時間の内訳が可視化されます。
その結果、意外なところで時間が使われていることが分かりました。
| 処理 | 時間 |
|---|---|
| API Gateway → Lambda | 約40ms |
| Lambda実行 | 約120ms |
| Init Duration | 約1.8秒 |
Lambdaの処理自体はそれほど時間がかかっていません。
ほとんどの待ち時間は、実行前の初期化処理でした。
原因はコールドスタート
Lambdaは一定時間アクセスがないと実行環境が破棄されます。
その状態でリクエストが来ると、新しい実行環境を作成するための初期化が発生します。
今回のAPIはアクセス頻度がそれほど高くなく、このコールドスタートの影響を受けやすい状態でした。
アプリケーションの処理が遅いのではなく、実行を始めるまでに時間がかかっていたことが原因でした。
Provisioned Concurrencyを設定
改善策として、Provisioned Concurrencyを設定しました。
実行環境を事前に起動した状態で保持するため、コールドスタートを避けられます。
今回は必要なリクエスト数を考慮し、Provisioned Concurrencyを設定しました。
改善結果
設定後は次のような変化がありました。
| 項目 | 変更前 | 変更後 |
|---|---|---|
| 平均応答時間 | 約3秒 | 約0.9秒 |
| Init Duration | 約1.8秒 | ほぼ0ms |
| ユーザー体感 | 待ち時間が目立つ | スムーズに完了 |
数値だけでなく、実際に操作したときの体感も大きく改善しました。
X-Rayが役立った理由
CloudWatchでは、
「Lambdaの実行時間」
は確認できます。
一方で、
「どこで時間を使っているか」
までは分かりません。
X-Rayでは、
- API Gateway
- Lambda
- データベース
- 外部サービス
など、それぞれの処理時間を確認できるため、ボトルネックの切り分けがしやすくなります。
今回はInit Durationが大半を占めていることが分かったことで、原因を特定できました。
学んだこと
今回の経験で印象に残ったのは、
Lambdaの実行時間だけを見ても十分ではない
ということです。
実装やSQLに問題がなくても、コールドスタートによってユーザー体験が悪くなることがあります。
サーバーレス環境では、
実行時間だけでなく、
- 初期化時間
- アクセス頻度
- 実行環境のライフサイクル
まで含めて設計することが大切だと感じました。
まとめ
今回の遅延の原因は、Lambdaの処理そのものではなく、コールドスタートによる初期化時間でした。
AWS X-Rayで処理時間の内訳を確認したことで原因を特定でき、Provisioned Concurrencyを導入することで応答時間を改善できました。
パフォーマンス改善では、全体の処理時間だけを見るのではなく、「どこで時間を使っているのか」を把握することが重要だと改めて感じた事例でした。