Lambdaのコールドスタートや初回DB接続時間の解消について調べてみた

AWS
AWS
この記事は約5分で読めます。

この記事はコールドスタートや初回接続に要する期間を検証するものではなく、インターネット上にある情報をまとめた記事となります。

主にAWS公式の情報をメインとし、補足としてその他のブログ記事の情報を参考にしました。

コールドスタートとは

Lambdaを呼び出す際、Lambdaを実行するための環境とコードをセットアップするためのステップから開始すること。

Lambdaが初めて呼び出される際、おおまかに以下のステップがある。

  1. コードダウンロード
  2. 環境作成
  3. イベントハンドラー外部の初期化コード実行
  4. ハンドラーのコード実行

コールドスタートは、1の処理から開始することをいう。

Lambdaの実行が完了すると、Lambdaは一定期間実行環境を保持する(期間は公開されておらず、定まった期間でもない)。
環境を保持している間にLambdaが呼び出されると、Lambdaは環境を再利用できる。
このときのLambdaの実行をウォームスタートという。
ウォームスタートでは、上記の4から処理を開始するため、Lambdaの実行時間がコールドスタートと比較して速くなる。

なお、コールドスタートでLambdaが実行した後に同じLambdaを呼び出すと必ずウォームスタートになる考えてしまうが、再度コールドスタートで始まることもある。
以下のパターンがそれにあたる。

  • Lambdaサービスは複数のAZにわたる高可用性サービスであり、負荷分散のため異なるAZの関数を呼び出した場合
  • 関数を同時に呼び出す場合
    • 並行して実行する必要があるため、同時に呼び出すたびに新しい実行環境が必要となり、コールドスタートから始まる
  • Lambdaのコードや構成を変更した場合

コールドスタートを回避する方法

EventBridgeルールを使用して定期的に関数を呼び出す

関数を1分ごとに呼び出すように設定し、実行環境をアクティブに保つ。
しかし、以下の場合はコールドスタートを回避できない。

  • 機能がトラフィックに合わせてスケールアップする場合
  • Lambdaサービスが通常の負荷分散操作の一部として別のAZで関数を実行する場合
  • Lambdaサービスは実行環境を定期的に取得して最新の状態に保つため、その場合

プロビジョニングされた同時実行性(Provisioned Concurrency)

この機能により、ウォームスタートが保たれる。
Lambdaが呼び出されるより前に、ハンドラーの実行以外の処理を終えている状態にしている(コードダウンロード、環境構築、初期化コード実行まで完了)。

例えば、Provisioned Concurrencyが6の場合、6つの実行環境が用意されていることになる。

Provisioned Concurrencyが、オンデマンド関数と異なる点は以下。

  • 初期化コードの最適化は必要ない
    • 呼び出しの前に初期化コードは実行されるため、呼び出しの待ち時間に影響しない
    • Javaなどの初期化に時間のかかるランタイムの場合、これらのパフォーマンスはProvisioned Concurrencyを使用することで恩恵を受ける
  • 初期化コードは呼び出しの総数よりも頻繁に実行される
    • Provisioned Concurrencyの1つにあたり、別々のAZで1つずつ実行環境がある
    • 初期化コードでログを実装している場合、このコードが実行されるたびに追加のログファイルが表示される
  • LATESTバージョンでは使用できない
    • 公開されているバージョンと関数のエイリアスでのみ使用可能

注意点として、Provisioned Concurrencyを使用しても、全てのLambda関数が必ずウォームスタートで開始するわけではない。
Provisioned Concurrencyが6であり、6を超える7のリクエストが同時にあった場合、超過した 1 はコールドスタートで開始される。

初期化コードの最適化

初期化コードとは、イベントハンドラー内で呼び出されない、イベントハンドラー外の処理のこと。
初期化コードの実行はコールドスタート時に行われる。
初期化コード内で事前準備可能な時間のかかる処理を行うことで、Lambdaのウォームスタート時に実行時間短縮が見込まれる。

初期化コードは、ハンドラーコードが関数で実行を開始する前に発生する。
この初期化コードで使用されるのは、主に以下。

  • ライブラリと依存関係のインポート
    • 本当に必要なものをインポート
      • require(aws-sdk)ではなく、require(aws-sdk/clients/dynamodb)にする
  • 構成のセットアップ
  • 他のサービスへの接続(データベースなど)

グローバルスコープ

前項と同じく、コールドスタートの際に役立つもの。

初期化コードで行うこと。

  • 同じ実行環境での呼び出しで、その値でよいものはグローバル変数に
  • ライブラリのインポート
  • DBの接続ロジック

イベントハンドラー内で行うこと。

  • DBの接続ロジックは遅延読み込みを使用し、最初の呼び出しでのみ確立する

DB接続の初回だけ時間がかかる問題

LambdaからRDSなどのDBと接続する際、初回は当然時間がかかる。
この時間を削減することはできないと思われるので、前述した通り初期化コード内で接続処理を行い、ウォームスタートではすでに接続した状態にしておく。

ただし、多くのLambdaからDBに接続する場合、DBの同時接続数に引っかかる可能性があるため、その辺りは注意が必要。

DB接続処理ではないが、初回だけ処理時間がかかる問題

この問題も初期化コードでその処理を実行することが可能であれば、ウォームスタート時には処理時間が削減できる。

参考

Lambda execution environments - AWS Lambda
When the Lambda service receives a request to run a function via the Lambda API, the service first prepares an execution environment. During this step, the serv...
Optimizing static initialization - AWS Lambda
Static initialization happens before the handler code starts running in a function. This is the “INIT” code that happens outside of the handler. This code is of...
AWS LambdaのJavaは遅い? - Qiita
AWS Lambdaで動作するJavaは初回が遅いですが、速くする方法がないか調べました。 末尾にある参考サイトの内容にたどり着いて、実際に試してみたのでその記録です。 レイテンシ情報はX-Rayにて取得しました。 テスト対象 ...
Connecting your Java AWS Lambda to an RDS database and RDS Proxy
Troubleshooting RDS IAM Authentication, and other tips
タイトルとURLをコピーしました