Lambda関数の次のコードがあります。
console.log('Loading function');
var aws = require('aws-sdk');
var ddb = new aws.DynamoDB();
function getUser(userid) {
var q = ddb.getItem({
TableName: "Users",
Key: {
userID: { S: userid } }
}, function(err, data) {
if (err) {
console.log(err);
return err;
}
else {
console.log(data);
}
});
console.log(q);
}
exports.handler = function(event, context) {
console.log('Received event');
getUser('user1');
console.log("called DynamoDB");
context.succeed();
};
そのように定義されている[Users]テーブルがあります。
{
"cognitoID": { "S": "token" },
"email": { "S": "[email protected]" },
"password": { "S": "somepassword" },
"tos_aggreement": { "BOOL": true },
"userID": { "S": "user1" }
}
(AWSコンソールまたはCLIから)関数を呼び出すと、ログにメッセージが表示されますが、getItem()のコールバックは呼び出されません。
コールバックなしでgetItem(params)を実行してから、完了、成功、失敗のコールバックを定義しましたが、send()を実行すると、完全なコールバックも呼び出されません。
私は呼び出しが非同期であることを知っており、おそらくラムダ関数はクエリが完了する前に終了していたためコールバックは呼び出されないだろうと思っていましたが、関数の最後に単純な愚かなループを追加し、呼び出しの時間を計りましたコールバックがまったく呼び出されずに、3秒後にアウト。
さまざまな関数batchGetItem、getItem、listTablesとスキャンを試してみました。結果は同じで、エラーはありませんが、コールバック関数が呼び出されることはありません。
Lambdaを使用せずにdynamoDBにクエリを実行すると結果が得られるので、ここで何も起こらないのはなぜだと思いますか。
関数のロールを作成し、dynamoDBの機能へのアクセスを許可するポリシーを作成しました。
ポリシーは次のようになります。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": "arn:aws:lambda:*:*:*" }, { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:BatchGetItem", "dynamodb:Scan", "dynamodb:PutItem", "dynamodb:Query", "dynamodb:GetRecords", "dynamodb:ListTables" ], "Resource": "arn:aws:dynamodb:*:*:*" }, { "Action": [ "logs:*" ], "Effect": "Allow", "Resource": "*" } ] }
シミュレーターでポリシーを実行したところ、思ったとおりに機能しました。提案?
したがって、コードが正しいことがわかります。問題は、dynamodb APIがこれらすべてのコールバックを使用し、基本的にはデータが取得される前に関数が終了することです。
最も速い修正は、context.succeed()
呼び出しを削除することで、データが取得されます。もちろんasyncモジュールを使用すると効果があり、それを使用したくない場合は、コールバックにカウンターまたはブール値を追加し、値が変更されるまで待機して、コールバックが呼び出されたことを示します(これは一種の吸引です)考えれば)
私はいくつかの同様の問題があり、多くの役立つリソースを見つけることができませんでした。これが私がやったことです。おそらく、賢い誰かがこれが最善かどうかを教えてくれるでしょう。
function getHighScores(callback) {
var params = {
TableName : 'sessions',
ScanFilter: {"score":{"AttributeValueList":[{"N":"0"}], "ComparisonOperator":"GT"}},
};
var dynamo = new AWS.DynamoDB();
dynamo.scan(params, function(err, data) {
if (err) {
console.log (err)
callback(err);
} else {
callback(data.Items);
console.log(data.Items);
}
});
}
getHighScores(function (data) {
console.log(data);
context.succeed(data);
});
要約すると、メイン関数から小さい関数へのコールバックのパスバックがあると、アプリケーションはDynamoを完了するまで続行できます。 context.succeedを2次関数に保持するか、他の関数をそこで続行します。
私の問題は、ElastiCacheに接続するために、ラムダがVPCで実行されていたことです。これにより、DynamoDBやAPI Gatewayなどのパブリックインターネットリソースへのクエリが無期限にハングします。 DynamoDBにアクセスするために、VPC内にNAT Gatewayをセットアップする必要がありました。
コールバック地獄を回避するには、Promiseを使用します。 funfunfunctionと呼ばれる人によるYouTubeにはかなり良いチュートリアルがいくつかあります。
Node.jsにasync/awaitが導入されたため、メイン関数が終了する前に、クエリ呼び出しが戻るまで待機させることができます。
let result = await ddb.getItem(params).promise();
return result;
私の問題は、私が呼び出そうとしていた関数が受け入れられたということでしたコールバック。
したがって、Node.jsはLambda関数の実行を継続し、それが最後に到達するとLambdaに完了したためシャットダウンを通知します。
約束を使用してそれを解決する方法の例を次に示します。
'use strict';
exports.handler = async (event, context) => {
// WRAP THE FUNCTION WHICH USES A CALLBACK IN A PROMISE AND RESOLVE IT, WHEN
// THE CALLBACK FINISHES
return await new Promise( (resolve, reject) => {
// FUNCTION THAT USES A CALLBACK
functionThatUsesACallback(params, (error, data) => {
if(error) reject(error)
if(data) resolve(data)
});
});
};