概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: 3 Things I Wish I Knew About AWS Lambda Functions Early On
- 原文公開日: 2020/05/24
- 著者: Andrew Titenko
AWS Lambda Functionsについて知っておきたかった3つの注意(翻訳)
ここ1年半の間、かなり多くのサーバーレスアプリケーションを構築してきましたが、そろそろAWS Lambdaエンジンについて学んだ重要なことをいくつか皆さんにご紹介するときがやってまいりました。
⚓1. 実行コンテキストが共有されることがある
私やチームメンバーが驚かされたのは、同一のLambda関数のまったく無関係な2つの実行(execution)がグローバル実行コンテキストを共有してしまう場合がありうるということでした。以下のスニペット例をご覧ください。
/* my-lambda-function.js */
const Logger = require('...');
const logger = new Logger('...');
exports.handler = () => {
// ...
}
皆さんも、オブジェクトのグローバルインスタンスを1つ作成して、それを引き回したり他のリスティングで使うためにエクスポートしたりする場面(ロガーインスタンスの再利用など)で同じようなことをしているのではないでしょうか。
このスニペットには改変可能なステートはないので、これをグローバルインスタンスとして扱うことはアンチパターンとはみなされません。しかし問題はここからです。これはLambdaエンジンのコンテキストではアンチパターンなのです。
知らないうちに他の誰かがLogger
クラスを更新して、改変可能なステートを持てるようにすると、人生で最悪級のバグ「暗黙のデータ改変」がコードで発生します。データがどこで改変されたのかも見えず、修正に何時間も費やすはめになります。
しかもこのバグはすぐには顕在化しません。各Lambda関数は直感的には独立したアプリケーションとして扱うものですし、各コードの実行もまったく新しいグローバルコンテキストでまったくの白紙状態から開始されると直感的に期待するものだからです。直感では、データの改変など思いもよりません。
ここで知っておいていただきたいのは、この「リクエストバンドル」現象は、特定の時間スレッショルド以内でたて続けに2つのリクエストが行われたときにしか発生しないことです。私の経験では、このスレッショルドは2〜3秒以内というところです。
⚓2. ネットワーク接続のタイムアウト
もうひとつのわかりにくい現象は、Lambda関数で発生する謎のタイムアウトです。こんな振る舞いが発生するなど考えもしないので、それだけでこのデバッグもかなりつらい作業になります。
Lambda関数を書く場合、自分の関数が現在の作業を完了したというシグナルをLambdaエンジンに送るのにコールバックやPromiseを用い、その呼び出しリクエストに対するレスポンスを受け取れるようになります。
直感的には「自分のハンドラーに返されたPromiseはresolveまたはrejectされるかのどちらか」あるいは「ハンドラーcallback()
関数を呼ぶと自分の関数は実行を停止し、実行結果を受け取る」ことを期待するものです。しかし、事態はそれほど単純ではありません。
自分のNode.jsイベントループ内のcallback()
呼び出しやPromise解決のタイミングで何かが起きると、Lambdaエンジンはそのイベントループが終了するまでシグナル上で動かなくなります。イベントループ内でスクリプトのフローを完了した後にデータベース接続を開く場合が典型的です。これらを終了しないで放置すると、関数はタイムアウトします。
この振る舞いを無効にするには、自分のLambda関数ハンドラに渡すcontext
オブジェクトでcallbackWaitsForEmptyEventLoop
フラグにfalse
を設定します。
/* my-lambda-function.js */
exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
}
⚓3. Lambdaレイヤーの制限
もうひとつ知っておきたかったことは、Lambdaレイヤーの制限です。Lambda関数1つにつき最大で5つのレイヤーをアタッチできますが、Lamdba関数1つあたりのバンドルサイズの最大値は250MBです。そしてこのサイズには、関数のコードや依存関係のほかに、アタッチされた全レイヤーも含まれます。
私はうっかり、レイヤー数や関数のサイズに上限はないものと思い込んでいました。その結果この問題をもろに踏んでしまい、サーバーレスアプリケーションの設計をいくつもしくじってやり直すはめになりました。
これらの制限を頭に叩き込み、今後アプリケーションのアーキテクチャを設計するときはこれらの制限を必ず考慮に含めましょう。どうか私の轍を踏みませんように!
本記事がお役に立ちましたら、ぜひシェアしてください。お読みいただいた皆さんに感謝いたします!
おたより発掘
1つめの注意のやつはまじではまった https://t.co/IpoEntVJOu
— rso (@rsooo) July 4, 2020