はじめに
AWS資格試験の勉強をすると、しばしばLambdaのパフォーマンスを改善する方法として、 「(DB接続等の処理を)Lambdaハンドラー外に移動する」というやり方を目にします。
これが何故パフォーマンスの改善につながるのかを自分なりにまとめてみました。
結論
- Lambda関数が「ウォームスタート」で開始した場合、ハンドラー外に書いたコードを再利用できるから
Lambdaのコールドスタートとウォームスタート
いきなり「ウォームスタート」というワードが出てきましたが、対になる言葉として「コールドスタート」も存在します。
これらのワードを理解するために、Lambda関数が呼び出された際の処理の流れを理解する必要があります。
Lambda関数が呼び出されると何が起きる?
Lambda関数が呼び出されると、AWS側でLambda関数の「実行環境」の準備が開始し、実行環境が起動した後にハンドラーコードが実行されます。
具体的には、以下の処理が実行されます。
- PythonやNode.js等の環境の起動
- 実行環境へコードをダウンロード
- ハンドラー外に記述された初期化コードの実行
- ハンドラーコードの実行
コールドスタートとは?
Lambda関数が始めて呼び出されたり、長らく起動していない状態から久しぶりに呼び出された場合は、実行環境の準備からハンドラーコードの実行までの一連の処理が必要となります。
この時、「コールドスタート」でLambda関数が呼び出されたことになります。
ウォームスタートとは?
一方で、一度Lambda関数の呼び出しが完了すると、実行環境は一定時間保持されます。
実行環境が保持されている間にLambda関数が呼び出された場合は、実行環境の準備が不要となりハンドラーコードの実行のみ行われます。
この時、「ウォームスタート」でLambda関数が呼び出されたことになります。
ここまでのまとめ
改めて、コールドスタートとウォームスタートの処理の違いをまとめます。
-
コールドスタート(初回または久しぶりにLambda関数が呼び出された場合)
- PythonやNode.js等の環境の起動
- 実行環境へコードをダウンロード
- ハンドラー外に記述された初期化コードの実行
- ハンドラーコードの実行
-
ウォームスタート(実行環境が保持されている時にLambda関数が呼び出された場合)
- ハンドラーコードの実行
ハンドラーコードは、コールドスタート/ウォームスタートどちらの場合でも必ず実行されます。
一方で、ハンドラー外のコードは、ウォームスタートで呼び出された場合なら、コード(実行環境)の再利用ができます。
そのため、DB接続等の処理をハンドラー外に記載・移動することは、パフォーマンスの改善に繋がる可能性があるということになります。
このことは、『AWS Lambda 関数を使用するためのベストプラクティス』(https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/best-practices.html)にも記載されています。
ここまでの参考文献
- https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtime-environment.html
- https://aws.amazon.com/jp/blogs/news/operating-lambda-performance-optimization-part-1/
動作確認
実際にハンドラー外にコードを記載すれば、コード(実行環境)を再利用できるか検証します。
検証に際し、以下のサイトを大変参考にさせていただきました。
- 『Lambdaのhandler外(メソッド以外)のコードは「コールドスタート時の1回だけ実行される」という話』 (https://dev.classmethod.jp/articles/lambda-outside-handler-running-first/)
実行するPythonコード
ローカルPCのPythonとLambda上で以下のコードを実行してみました。
from datetime import datetime
now_time_outside = datetime.now()
def lambda_handler(event, context):
now_time_inside = datetime.now()
print(f"now_time_outside: {now_time_outside}")
print(f"now_time_inside: {now_time_inside}")
# ローカルPC上の場合は以下の通り記載
# def lambda_handler():
# now_time_inside = datetime.now()
# print(f"now_time_outside: {now_time_outside}")
# print(f"now_time_inside: {now_time_inside}")
# lambda_handler()
ローカルPCでの実行結果
$ python3 -V
Python 3.10.12
# 1回目
$ python3 lambda_local.py
now_time_outside: 2025-04-06 23:20:37.826077
now_time_inside: 2025-04-06 23:20:37.826091
# 2回目
$ python3 lambda_local.py
now_time_outside: 2025-04-06 23:20:41.226870
now_time_inside: 2025-04-06 23:20:41.226884
# 3回目
$ python3 lambda_local.py
now_time_outside: 2025-04-06 23:20:45.833986
now_time_inside: 2025-04-06 23:20:45.834002
ローカルPCのPythonでは、コードを実行する度に、ハンドラー内/外両方のdatetime.now()が計算されています。
Lambda上での結果
ランタイムにPython 3.10を選択の上、その他はデフォルトでLambda関数を作成し、コードのデプロイ→テストを実行します。
-
1回目
-
2回目
-
3回目
Lambda上で実行した場合は、ハンドラー内で定義した「now_datetime_inside」が実行される度に計算されています。
一方で、ハンドラー外で定義した「now_time_outside」は、1回目の実行結果である「2025-04-06 14:37:22.671652」が2回目、3回目の実行結果にも現れています。
上記より、ウォームスタート(2回目以降)で起動した場合は、確かにハンドラー外で定義したコードを含む実行環境が再利用されることがわかりました。
また、ハンドラー内のコードは毎回必ず実行されることも確認できました。
以上より、ハンドラー外にコードを記載・移動することで、コード(実行環境)を再利用できる可能性がある = パフォーマンスの改善に繋がる可能性があると言えそうです。
最後に
今回は、Lambdaのハンドラー外にコードを書く意義について調べてみました。
資格勉強をしているとよく目にするやり方なので、何となく理解していたつもりでしたが、手を動かすとより理解が深まった気がします。
今後はただ資格を取るだけでなく、理解があいまいな分野を地道に検証しながら、試験勉強していこうかと思います。