こんにちは。 エキサイト株式会社の三浦です。
AWSには、サーバーレスで処理を実行できる「AWS Lambda」というサービス(以降Lambda)があります。 Lambdaでは、実行の起点となるメソッド(handler)を指定することで、Lambdaの実行イベントが走ったときにそのhandlerメソッドが実行される、という流れになっています。
そのためコードの実装は基本的にはそのhandlerメソッドに書くことになるのですが、実は一部の状況ではあえてhandlerメソッドの外にコードを書くことがあります。
今回は、そういった状況で、handlerメソッドの内外で終了処理( exit
)を実行したときの挙動の違いについて説明していきます。
Lambdaとは
LambdaはAWSのサービスの1つで、公式ページでは以下のように説明されています。
AWS Lambda は、サーバーレスでイベント駆動型のコンピューティングサービスであり、サーバーのプロビジョニングや管理をすることなく、事実上あらゆるタイプのアプリケーションやバックエンドサービスのコードを実行することができます。
Lambdaでは様々な言語でコードを書くことができるのですが、今回はPythonで試してみます。
言語をPythonで設定すると、以下のようなコードが自動生成されます。
import json def lambda_handler(event, context): # TODO implement return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
handlerメソッドは lambda_handler
になっており、これをテスト実行してみると、以下のようなLogが出力されます。
Function Logs START RequestId: xxxx Version: $LATEST END RequestId: xxxx REPORT RequestId: xxxx Duration: 1.28 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 39 MB Init Duration: 160.50 ms
handlerメソッドが lambda_handler
であるため、このLambdaは実行のたびに lambda_handler
が実行されることになります。
そのため、実装したいコードはその lambda_handler
の中に書いていけば良いのですが、実は場合によっては lambda_handler
の外に処理を書いたほうがいい場合もあります。
handlerメソッドの外に処理を書いたほうがいい場合
DBとの接続のコードのサンプルでは、以下のように書かれています。
ハンドラの外部で pymysql.connect() を実行すると、関数がデータベース接続を再利用できるようになるため、パフォーマンスが向上します。
実はhandlerメソッドの外で定義したコードは、handlerメソッド内に定義したコードと異なり、Lambda実行の度に毎回実行されることはありません。
代わりに、Lambdaのコンテナ(Lambdaの実態はコンテナです)が立ち上がった時の最初の一回しか実行されず、以降はそのコンテナが終了するまで結果を保持し続けるため、例えばDBとの接続のような一回実行すれば問題ないような処理を書くにはもってこいの場所になっています。
実際にサンプルコードで試してみます。
import json print('external printing') def lambda_handler(event, context): print('internal printing') return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
このコードを実行してみると、初回実行時は以下のようなログが出力されます。
START RequestId: xxxx Version: $LATEST external printing internal printing END RequestId: xxxx REPORT RequestId: xxxx Duration: 1.67 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 39 MB Init Duration: 139.88 ms
二回目以降は以下のようになります。
Function Logs START RequestId: xxxx Version: $LATEST internal printing END RequestId: xxxx REPORT RequestId: xxxx Duration: 1.21 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 39 MB
初回にあった、handlerメソッドの外で定義している external printing
の出力が、二回目以降は消えているのがわかります。
handlerメソッド内外でのexitの挙動
さて、突然ですが、コードを書く上で exit
をしたい状況はたまに存在します。
例えば、本来なってほしくない結果が得られてしまったときなど、それ以降の処理を続行せずにそこで終わらせたいときなどです。
Lambdaでももちろん exit
をすることは可能なのですが、上記の通りLambdaでのコードは、handlerメソッド内外で異なる挙動をします。
では、 exit
実行時にはどのような違いがあるのでしょうか?
handlerメソッド内部での exit
実行
handlerメソッド内部で、以下のコードで exit
を実行してみます。
import json import sys print('external printing') def lambda_handler(event, context): print('internal printing') sys.exit(0) return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
初回・二回目以降のログは以下のようになります。
初回
START RequestId: xxxx Version: $LATEST external printing internal printing END RequestId: xxxx REPORT RequestId: xxxx Duration: 144.18 ms Billed Duration: 145 ms Memory Size: 128 MB Max Memory Used: 39 MB Init Duration: 215.69 ms RequestId: xxxx Error: Runtime exited without providing a reason Runtime.ExitError
二回目以降
START RequestId: xxxx Version: $LATEST internal printing END RequestId: xxxx REPORT RequestId:xxxx Duration: 104.91 ms Billed Duration: 105 ms Memory Size: 128 MB Max Memory Used: 11 MB RequestId: xxxx Error: Runtime exited without providing a reason Runtime.ExitError
exit
しない時と同様、二回目以降はhandlerメソッド外部の処理は呼ばれていないことがわかります。
handlerメソッド外部での exit
実行
handlerメソッド外部で、以下のコードで exit
を実行してみます。
import json import sys print('external printing') sys.exit(0) def lambda_handler(event, context): print('internal printing') return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
初回・二回目以降のログは以下のようになります。
初回
START RequestId: xxxx Version: $LATEST external printing external printing END RequestId: xxxx REPORT RequestId: xxxx Duration: 1369.88 ms Billed Duration: 1370 ms Memory Size: 128 MB Max Memory Used: 11 MB RequestId: 19a1b5ac-519a-4d6e-bbaa-afee39439c57 Error: Runtime exited without providing a reason Runtime.ExitError
二回目以降
START RequestId: xxxx Version: $LATEST external printing END RequestId: xxxx REPORT RequestId: xxxx Duration: 1328.81 ms Billed Duration: 1329 ms Memory Size: 128 MB Max Memory Used: 11 MB RequestId: xxxx Error: Runtime exited without providing a reason Runtime.ExitError
初回に external printing
が二回出てしまっているのは謎ですが、少なくとも二回目以降でも external printing
が出ている、すなわちhandlerメソッド外部の処理が実行されているのがわかります。
上記の結果から、 exit
をhandlerメソッド内部で実行するときと異なり、外部で実行する時は全体的に終了されていることがわかります。
DBとの接続設定など、handler外部での処理ごと終了させたい場合はhandler外部で exit
を実行し、そうではなく毎回実行したい処理だけ終了したい場合はhandler内部で exit
を実行すると良いでしょう。
最後に
Lambdaは便利ですが、上記のように細かい設定も存在します。 活用していけばよりパフォーマンスを改善したりすることもできるので、ぜひ気にかけていきましょう。