はじめに
ジョブが作成されてからLambdaが実行されるまでの間の時間(滞留時間)を取得する
Lambdaを作成していた時に
現在日時を取得するためにdatetimeを使用していたのですが、
そこで「AWS Lambdaは関数インスタンスを再利用」というのに見事にハマったので、
記事にしてみたいと思います。
最初に作成していたコード
まずLambdaが実行された現在日時を取得するための
コードを簡略化したものが以下になります。
from datetime import datetime
now = datetime.now()
def lambda_handler(event, context):
method_a()
def method_a():
method_b()
def method_b():
method_c()
def method_c():
# xはジョブの作成日時を表しています。
time = now - x
print(data)
- ジョブの作成時刻を
x
- 現在時刻を
now
- 滞留時間を
time
として表しています。
時刻を表すnowをグローバル変数として宣言して
どのメソッドでも使えるようにしてました。
#ローカルでは成功したけど…
ローカルでは滞留時間を取得できたので、
そのままビルドデプロイしてLambdaを作成しました。
最初のLambdaのテストも成功して問題はありませんでした。
よし大丈夫だ!もう一回テストして確認しよう…
あれ!?滞留時間がおかしい!!!!!!
1回目で上手く取得できていた滞留時間が
2回目以降上手く取得できませんでした。
何回やってもtime
がおかしい。
そこでまたビルドデプロイして
Lambdaを更新させてみたところ上手くいきました。
しかし、また2回目以降はダメでした。
原因は
そこで原因調査のために色々調べていると
こちらの記事に出会いました!
ずばり原因は、lambda_handler外で宣言したグローバル変数のnow
でした。
どうやらLambdaはパフォーマンス向上のために
関数のインスタンスを再利用するみたいです。
https://aws.amazon.com/jp/lambda/faqs/
つまり一回目に取得したnow
の日時がキャッシュされ
2回目以降も同じ値が再利用されていたみたいです。
常にLambdaの関数インスタンスは新規で作成されているものだと
思っていたので、全く気づきませんでした!!
lambda_handler内にグローバルに変数を置きたい
原因を理解したところで
とりあえずnow
をlambda_handler
内に置きました。
これでLambdaが実行されるたびに新しくnow
の日時を取得してきてくれます。
from datetime import datetime
def lambda_handler(event, context):
now = datetime.now()
method_a(now)
def method_a(now):
method_b(now)
def method_b(now):
method_c(now)
def method_c(now):
# xはジョブの作成日時を表しています。
time = now - x
print(data)
この形でも実行できるのですが、
こんなにメソッドを経由して渡すのはかっこ悪いので
以下のように修正しました。
from datetime import datetime
def lambda_handler(event, context):
global now
now = datetime.now()
method_a()
def method_a():
method_b()
def method_b():
method_c()
def method_c():
# xはジョブの作成日時を表しています。
time = now - x
print(data)
これで無事、2回目以降も
ちゃんとnow
が実行した日時を取得してくれるようになり
time
も上手く取得できました。
まとめ
Lambdaの関数インスタンスの再利用については
初耳だったので、とても勉強になりました。
あとは、グローバル変数はとても便利ですが
思わぬバグを引き起こす原因になりやすいということを
身を持って実感しました。
使い方には気を付けていこうと思います。