Lambda マネージドインスタンスの Node.js ランタイム - AWS Lambda

Lambda マネージドインスタンスの Node.js ランタイム

Node.js ランタイムの場合、Lambda マネージドインスタンスは async / await ベースの実行でワーカースレッドを使用して同時リクエストを処理します。関数の初期化はワーカースレッドごとに 1 回行われます。同時呼び出しは 2 つのディメンションにわたって処理され、ワーカースレッドは vCPU 間で並列処理を行い、非同期実行は各スレッド内で同時実行を行います。同じワーカースレッドで処理される各同時リクエストは、同じハンドラーオブジェクトとグローバル状態を共有するため、複数同時リクエストで安全に処理する必要があります。

最大同時実行数

Lambda が各実行環境に送信する同時リクエストの最大数は、関数設定の PerExecutionEnvironmentMaxConcurrency 設定で制御できます。これはオプションの設定となっており、デフォルト値はランタイムによって異なります。Node.js ランタイムの場合の同時リクエスト数は、vCPU あたり 64 件がデフォルト値になっていますが、独自の値を設定することもできます。Lambda は、各実行環境の容量に応じて、設定された最大数までの同時リクエストの数を自動的に調整し、それらのリクエストを取得します。

Node.js の場合、各実行環境が処理できる同時リクエストの数は、同時リクエストを非同期で処理するワーカースレッドの数と各ワーカースレッドの容量によって決まります。ワーカースレッドのデフォルトの数は、使用可能な vCPU の数によって決まります。または、AWS_LAMBDA_NODEJS_WORKER_COUNT 環境変数を設定してワーカースレッドの数を設定できます。各ワーカースレッドで数のリクエストを処理できるため、非同期関数ハンドラーを使用することをお勧めします。関数ハンドラーが同期の場合、各ワーカースレッドは一度に 1 つのリクエストのみを処理できます。

同時実行のための関数の構築

非同期関数ハンドラーを使用すると、各ランタイムワーカーは複数のリクエストを同時に処理します。グローバルオブジェクトは、複数の同時リクエスト間で共有されます。ミュータブルなオブジェクトの場合は、グローバル状態を使用しないか、AsyncLocalStorage を使用します。

AWS SDK クライアントは非同期に対応しているため、特別な処理は必要ありません。

例: グローバル状態

次のコードでは、関数ハンドラー内で変更されるグローバルオブジェクトが使用されています。そのため、非同期に対応しません。

let state = { currentUser: null, requestData: null }; export const handler = async (event, context) => { state.currentUser = event.userId; state.requestData = event.data; await processData(state.requestData); // state.currentUser might now belong to a different request return { user: state.currentUser }; };

以下のように関数ハンドラー内で state オブジェクトを初期化すると、共有グローバル状態を避けられます。

export const handler = async (event, context) => { let state = { currentUser: event.userId, requestData: event.data }; await processData(state.requestData); return { user: state.currentUser }; };

例: データベース接続

次のコードは、複数の呼び出し間で共有される共有クライアントオブジェクトが使用されています。使用する接続ライブラリによっては、同時実行に対応しない場合があります。

const { Client } = require('pg'); // Single connection created at init time const client = new Client({ host: process.env.DB_HOST, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD }); // Connect once during cold start client.connect(); exports.handler = async (event) => { // Multiple parallel invocations share this single connection = BAD // With multi-concurrent Lambda, queries will collide const result = await client.query('SELECT * FROM users WHERE id = $1', [event.userId]); return { statusCode: 200, body: JSON.stringify(result.rows[0]) }; };

同時実行に対応させるには、以下のように接続プールを使用します。プールは、同時データベースクエリごとに個別の接続を使用します。

const { Pool } = require('pg'); // Connection pool created at init time const pool = new Pool({ host: process.env.DB_HOST, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, max: 20, // Max connections in pool idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000 }); exports.handler = async (event) => { // Pool gives each parallel invocation its own connection const result = await pool.query('SELECT * FROM users WHERE id = $1', [event.userId]); return { statusCode: 200, body: JSON.stringify(result.rows[0]) }; };

Node.js 22 コールバックベースのハンドラー

Node.js 22 を使用する場合、Lambda マネージドインスタンスでコールバックベースの関数ハンドラーを使用することはできません。コールバックベースのハンドラーは、Lambda (デフォルト) 関数でのみサポートされています。Node.js 24 以降のランタイムの場合、コールバックベースの関数ハンドラーは Lambda (デフォルト) と Lambda マネージドインスタンスの両方で廃止されています。

代わりに、Lambda マネージドインスタンスを使用する際には async 関数ハンドラーを使用します。詳細については、「Node.js の Lambda 関数ハンドラーの定義」をご覧ください。

共有の /tmp ディレクトリ

/tmp ディレクトリは、実行環境内のすべての同時リクエスト間で共有されます。同じファイルに同時に書き込むと、別のプロセスによってファイルが上書きされるなど、データの破損が発生する可能性があります。これに対処するには、共有ファイルのファイルロックを実装するか、リクエストごとに一意のファイル名を使用して競合を回避します。空き容量を占有しないよう、不要なファイルはクリーンアップしてください。

ログ記録

ログインターリービング (ログでインターリーブされるさまざまなリクエストからのログエントリ) は、複数同時実行システムでは正常です。Lambda マネージドインスタンスを使用する関数は常に、高度なログ記録コントロールで導入した構造化された JSON ログ形式を使用します。この形式には requestId が含まれ、ログエントリを 1 つのリクエストに関連付けることができます。console ロガーを使用すると、requestId が各ログエントリに自動的に含まれます。詳細については、「Node.js での Lambda の高度なログ記録コントロールの使用」を参照してください。

Winston などの一般的なサードパーティーのログ記録ライブラリには、通常、ログ出力にコンソールを使用できるサポートが含まれています。

リクエストコンテキスト

context.awsRequestId を使用すると、現在のリクエストのリクエスト ID に非同期対応でアクセスできます。

context.xRayTraceId を使用して X-Ray トレース ID にアクセスします。これにより、現在のリクエストのトレース ID への同時実行に対応したアクセスが可能になります。Lambda は、Lambda マネージドインスタンスの _X_AMZN_TRACE_ID 環境変数をサポートしていません。AWS SDK を使用すると、X-Ray トレース ID が自動的に伝播されます。

初期化とシャットダウン

関数の初期化はワーカースレッドごとに 1 回行われます。関数が初期化中にログを発行すると、繰り返しログエントリが表示されることがあります。

拡張機能を持つ Lambda 関数の場合、実行環境はシャットダウン中に SIGTERM シグナルを出力します。このシグナルは拡張機能によって使用され、バッファのフラッシュなどのクリーンアップタスクをトリガーします。拡張機能を持つ Lambda (デフォルト) 関数は、process.on() を使用して SIGTERM シグナルにサブスクライブすることもできます。これは、ワーカースレッドでは process.on() を使用できないため、Lambda マネージドインスタンスを使用する関数ではサポートされていません。実行環境のライフサイクルの詳細については、「Lambda 実行環境のライフサイクルの概要」を参照してください。

依存関係バージョン

Lambda マネージドインスタンスには、以下のパッケージバージョンの最小要件が必要です。

  • AWS SDK for JavaScript v3: バージョン 3.933.0 以降

  • AWS X-Ray SDK for Node.js: バージョン 3.12.0 以降

  • AWS Distro for OpenTelemetry – Instrumentation for JavaScript: バージョン 0.8.0 以降

  • Powertools for AWS Lambda (TypeScript): バージョン 2.29.0 以降

Powertools for AWS Lambda (TypeScript)

Powertools for AWS Lambda (TypeScript) は Lambda マネージドインスタンスと互換性があり、ログ記録、トレース、メトリクスなどのユーティリティを利用できます。詳細については、「Powertools for AWS Lambda (TypeScript)」を参照してください。

次のステップ