Help us understand the problem. What is going on with this article?

新しいLambda functionのcontextを調べる

More than 5 years have passed since last update.

先週、AWS Lambdaのアップデートがされましたね!

【AWS発表】AWS Lambda – モバイル開発のための新機能とともに全てがプロダクションに

同期呼び出しがサポートされたことで、これでまともに2Tierとして使えるようになりました。
でも、context.succeedcontext.failって、どういう挙動させるんだろう?と気になったのでcontextの中身を調べてみました。
前バージョンのcontextの中身は下記を参照してください。

検証コード

exports.handler = function(event, context) {
    console.log(context);
    console.log(context.__proto__);
    console.log('awsRequestId', context.awsRequestId);
    console.log('invokeid', context.invokeid);
    console.log('logStreamName', context.logStreamName);
    console.log('succeed()', context.succeed.toString());
    console.log('fail()', context.fail.toString());
    console.log('done()', context.done.toString());

    context.done(null);
};

一度、contextconsole.logで出力して、表示された値をそれぞれ出力してあげる感じです。

出力結果

START RequestId: 09ecd985-e0ca-11e4-84c0-8b4f359a82ec
2015-04-12T04:11:49.738Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    { awsRequestId: '09ecd985-e0ca-11e4-84c0-8b4f359a82ec',
  invokeid: '09ecd985-e0ca-11e4-84c0-8b4f359a82ec',
  logStreamName: '2015/04/12/d76c581c256a434985cfe529e6274b64',
  succeed: [Function],
  fail: [Function],
  done: [Function] }
2015-04-12T04:11:49.797Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    {}
2015-04-12T04:11:49.797Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    awsRequestId 09ecd985-e0ca-11e4-84c0-8b4f359a82ec
2015-04-12T04:11:49.797Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    invokeid 09ecd985-e0ca-11e4-84c0-8b4f359a82ec
2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    logStreamName 2015/04/12/d76c581c256a434985cfe529e6274b64
2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    succeed() function (result) {
                checkExpectedArgRange('succeed', arguments, 0, 1);
                if(isUndefined(result)) {
                    result = null;
                }
                try {
                    result = JSON.stringify(result);
                    finish_event_invoke(null, result);
                } catch(err) {
                    var errorObject = failParamToErrorObject(err)[1];
                    errorObject['errorMessage'] = "Unable to stringify body as json: " + errorObject['errorMessage'];
                    fail_event_invoke('unhandled', errorObject);
                }
            }
2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    fail() function (error) {
                checkExpectedArgRange('fail', arguments, 0, 1);
                var multi_result = failParamToErrorObject(error); 
                fail_event_invoke(multi_result[0], multi_result[1]);
            }
2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    done() function () {
                checkExpectedArgRange(arguments, 0, 2);
                if(isUndefinedOrNull(arguments[0]) && isUndefinedOrNull(arguments[1])) {
                    _this.succeed();
                } else if (isUndefinedOrNull(arguments[0])) {
                    _this.succeed(arguments[1]);
                } else if (isUndefinedOrNull(arguments[1])) {
                    _this.fail(arguments[0]);
                } else {
                    //support backwards compatibility by logging message here
                    console.log("Error Message: " + arguments[1]);
                    _this.fail(arguments[0]);
                }
            }
2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    result: null
END RequestId: 09ecd985-e0ca-11e4-84c0-8b4f359a82ec
REPORT RequestId: 09ecd985-e0ca-11e4-84c0-8b4f359a82ec  Duration: 176.25 ms Billed Duration: 200 ms     Memory Size: 128 MB Max Memory Used: 9 MB   

出力結果を見てみるといくつかのプロパティ、メソッドが追加されて、ログにも結果出力のログが増えてますね。
それぞれを細かくみていきます。

context.logStreamName

2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    logStreamName 2015/04/12/d76c581c256a434985cfe529e6274b64

ログストリームの名前が引き渡させるようになりました。
全く使い道がわかりませんが、このタイミングでログストリームの名前が確定されるということは、実行後にログを取得するときに待つ必要がなくなったのでは!?
まだ検証してませんがコマンドラインで実行→ログ取得を高速に回せるようになっていると良いなぁと思います。

context.succeed

2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    succeed() function (result) {
                checkExpectedArgRange('succeed', arguments, 0, 1);
                if(isUndefined(result)) {
                    result = null;
                }
                try {
                    result = JSON.stringify(result);
                    finish_event_invoke(null, result);
                } catch(err) {
                    var errorObject = failParamToErrorObject(err)[1];
                    errorObject['errorMessage'] = "Unable to stringify body as json: " + errorObject['errorMessage'];
                    fail_event_invoke('unhandled', errorObject);
                }
            }

同期呼び出しのサポートで追加された、成功時にレスポンスを返すメソッドです。
succeedにJSON変換可能な値を渡すとJSONに変換してレスポンスを返してくれるようになるようです。
そのため、それ以外のフォーマットでレスポンスを返すというのはできなさそうですね。

JSON変換成功時に呼び出しているfinish_event_invokeはグローバルな関数ではなく、succeedのスコープに隠蔽されているのでこのあたりを拡張するのは無理そうです。

context.fail

2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    fail() function (error) {
                checkExpectedArgRange('fail', arguments, 0, 1);
                var multi_result = failParamToErrorObject(error); 
                fail_event_invoke(multi_result[0], multi_result[1]);
            }

こちらも同期呼び出しのサポートで追加された、失敗時にレスポンスを返すメソッドです。
failParamToErrorObjectで前のcontextのようにエラーの有無と、エラーメッセージ(or オブジェクト)に変換してfail_event_invoke渡しているのかなと妄想しますが、実際のところよくわかっていないです。
AWSの中の人にこのあたりの実装を聞きたいですね。

context.done

2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    done() function () {
                checkExpectedArgRange(arguments, 0, 2);
                if(isUndefinedOrNull(arguments[0]) && isUndefinedOrNull(arguments[1])) {
                    _this.succeed();
                } else if (isUndefinedOrNull(arguments[0])) {
                    _this.succeed(arguments[1]);
                } else if (isUndefinedOrNull(arguments[1])) {
                    _this.fail(arguments[0]);
                } else {
                    //support backwards compatibility by logging message here
                    console.log("Error Message: " + arguments[1]);
                    _this.fail(arguments[0]);
                }
            }

前のバージョンからあったcontext.doneですが実装が内部でsucceedfailを呼び出す形に変更されています。
そのため、既存の実装でも同期呼び出しは扱えそうですね。

context.done(); //-> succeed
context.done(null, 'success'); //-> succeed
context.done(undefined, 'success'); //-> succeed
context.done('fail'); //-> fail
context.done('fail', null); //-> fail
context.done('fail', undefined); //-> fail
context.done(true, 'fail'); //-> fail

だいたいdoneの呼び出しはこんな感じですね。
まぁ、実装的にはこれまで通り、第一引数にエラーの有無を渡して、第二引数にエラーメッセージ(or オブジェクト)を渡すので良さそうです。

result log

2015-04-12T04:11:49.798Z    09ecd985-e0ca-11e4-84c0-8b4f359a82ec    result: null

ついでにログの種類も増えていて、レスポンスとして返される値もログに出力されるようになったようです。
正直、エンジニアが見てはいけない値(個人情報系とか)をレスポンスで返す可能性を考えると、これはちょっとオフにしたい・・・。

error log

START RequestId: 1ccb67ee-e0cc-11e4-bc7a-139cb7123bb7
Failure while running task: ReferenceError: finish_event_invoke is not defined
    at exports.handler (/var/task/index.js:2:17)
Process exited before completing request
ReferenceError: finish_event_invoke is not defined
END RequestId: 1ccb67ee-e0cc-11e4-bc7a-139cb7123bb7
REPORT RequestId: 1ccb67ee-e0cc-11e4-bc7a-139cb7123bb7  Duration: 221.85 ms Billed Duration: 300 ms     Memory Size: 128 MB Max Memory Used: 9 MB   

ちなみに構文エラーや、エラーをキャッチしなかった場合は上記のようなログになります。

まとめ

調査も完了したので、これから自作のlambda-handlerというモジュールのアップデート作業に入ります。正直、面倒臭いです。
Lambda functionの第一引数に渡すjsonを指定したり、requireしているモジュールをモック化してLambda functionを呼び出す便利なモジュールが欲しい・・・。

追記

/var/runtime/node_modules/awslambda/bin/awslambda
/var/runtime/node_modules/awslambda/lib/awslambda.js

に実行系とコードがあるの見つけました。
AWS Lambdaで実行前と実行後にどうしているの気にしている人はここをcatしたりすると幸せになるかもしらんです。

kinzal
PHPやったり、JSやったり、AWSやったり、CI環境構築したり、絵描したり、幽体離脱したり。そんな人。
http://about.me/kinzal
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした