46
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

新しいLambda functionのcontextを調べる

Last updated at Posted at 2015-04-12

先週、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したりすると幸せになるかもしらんです。

46
39
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?