銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

非同期Lambdaのコンテキストはどこ行った?

f:id:takamints:20180630111519j:plain
Flickrdetective ( Creatorolarte.ollie, License: CC BY-SA 2.0 )


結局どこへも行ってなかったって結論ですがっ!

AWSによるサーバーレスアーキテクチャ
Peter Sbarski
翔泳社
売り上げランキング: 4,634


この4月からAWS Lambda で Node.js 8.10 が使えるようになっており、AWSのコンソールから新規作成するとひな型がasync関数になっています。 しかし引数リストが event ひとつだけになっていて、従来第2引数で渡されていた context はどこに行ったの?と調べてみました。

目次

はじめての Lambda with Node.js 8.10 は非同期関数

現在、Node.js 8.10 のLambda を新規作成すると以下のひな型が作られます。async関数になってますね。

そして、引数が event だけ。えらいスッキリしましたね。context は何処へ?

exports.handler = async (event) => {
    // TODO implement
    return 'Hello from Lambda!'
};

ところで Lambda のコンテキストって何でしょう

AWS Lambdaに渡される context は以下のキーを持つオブジェクト。 Lambda関数自体の情報を保持しています。 結構重要な情報があるはずなので、これが渡されないとはいぶかしい。

  • callbackWaitsForEmptyEventLoop
  • functionName
  • functionVersion
  • invokedFunctionArn
  • memoryLimitInMB
  • awsRequestId
  • logGroupName
  • logStreamName
  • identity
  • clientContext

参考: Context オブジェクト (Node.js) - AWS Lambda

this === context?」って仮説は否定されました

ジャバスクリプターが「コンテキスト」と聞いて最初に連想するのは this ですよね(本当か?)

そこで「Node.js 8.10 のLambda関数内では、this === context なのではないか?」と仮説を立てて、以下のLambdaを書いてみた。

exports.handler = async (event) => {
    return JSON.stringify({
        callbackWaitsForEmptyEventLoop:
            this.callbackWaitsForEmptyEventLoop,
        functionName: this.functionName,
        functionVersion: this.functionVersion,
        invokedFunctionArn: this.invokedFunctionArn,
        memoryLimitInMB: this.memoryLimitInMB,
        awsRequestId: this.awsRequestId,
        logGroupName: this.logGroupName,
        logStreamName: this.logStreamName,
        identity: this.identity,
        clientContext: this.clientContext,
    });
};

で、コンソールから動かしてみたのですが、全てがnullで、仮説は完全否定されました。 まあ、そんなヤヤコシイことしないか。

じゃあコンテキストはどこ行った?

てことでAWSコンソールでしばらく探ってみたのですが、「もしやどこにも行っていないのでは?」と思い、

exports.handler = async (event, context) => {
    return JSON.stringify({
        callbackWaitsForEmptyEventLoop:
            context.callbackWaitsForEmptyEventLoop,
        functionName: context.functionName,
        functionVersion: context.functionVersion,
        invokedFunctionArn: context.invokedFunctionArn,
        memoryLimitInMB: context.memoryLimitInMB,
        awsRequestId: context.awsRequestId,
        logGroupName: context.logGroupName,
        logStreamName: context.logStreamName,
        identity: context.identity,
        clientContext: context.clientContext,
    });
};

上のLambdaで確認すると、、、お見事大当たり。なんだちゃんと渡って来てるじゃないかと。

コンソールの実行結果: f:id:takamints:20180630102131p:plain

非同期Lambdaのcallbackの扱いは?

じゃ、「コールバックはどこ行った?」と確認してみると、しっかり第3引数でもらってました。何も変わっちゃいなかったのね。

そして、Async なLambdaで、従来通り callback を呼び出した場合は、優先的に戻り値として採用されるようになっています。

以下のLambdaを呼び出すと "OK!" が返されます。

exports.handler = async (event, context, callback) => {
    callback(null, "OK!");
    return JSON.stringify({
        callbackWaitsForEmptyEventLoop:
            context.callbackWaitsForEmptyEventLoop,
        functionName: context.functionName,
        functionVersion: context.functionVersion,
        invokedFunctionArn: context.invokedFunctionArn,
        memoryLimitInMB: context.memoryLimitInMB,
        awsRequestId: context.awsRequestId,
        logGroupName: context.logGroupName,
        logStreamName: context.logStreamName,
        identity: context.identity,
        clientContext: context.clientContext,
    });
};

結論と所感など

この調査で分かったこと:

  • Node.js 8.10 の Async な Lambda にも、contextやcallbackコールバックは渡されている(単にひな型の引数リストからなくなってるだけ)。
  • コールバックで解決した値が、return よりも優先的にLambdaの戻り値として採用される。

contextについて、あえて渡さないことにメリットがないので当たり前と言えば当たり前か。 自分の妙な思い込みが邪魔をしました(笑)。

コールバックの件については、古いLambdaを改造するときに、どうしても await を使いたくなったら、Node.js のランタイムバージョンを8.10に変更して、async を付け加えれるだけで済むのですね。レアケースかも知れんけど。

しかし、なんで初期状態の引数リストからcontextを削除しちゃったのだろうかね?

callbackを、あえて示さないのは理解できるが・・・。