I have the following code for a Lambda function:
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();
};
I have a [Users] table that is defined as such:
{
"cognitoID": { "S": "token" },
"email": { "S": "user1@domain.com" },
"password": { "S": "somepassword" },
"tos_aggreement": { "BOOL": true },
"userID": { "S": "user1" }
}
When I call the function (from the AWS Console or the CLI) I can see the messages in the logs but the callback for the getItem() is never called.
I tried with doing getItem(params) with no callback, then defined the callbacks for complete, success and failure but when I do the send(), even the complete callback isn't called either.
I know that the calls are asynchronous and I thought that maybe, the lambda function was finishing before the query was done and therefore the callback would not be called, but, I added a simple stupid loop at the end of the function and the call timed out after 3 seconds, without the callbacks being called at all.
I tried with different functions batchGetItem, getItem, listTables and scan. Result is the same, no error but the callback function is never called.
I'm betting that if I query dynamoDB without using Lambda it will get me the results so I'm really wondering why nothing is happening here.
I create a role for the function and I created a policy that would allow access to the functionalities in dynamoDB that I need but to no avail.
The policy looks like this:
{ "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": "*" } ] }
I ran the policy in the simulator and it worked as I thought it would. Suggestions?
So, it turns out that the code is correct. The problem is that the dynamodb API uses all those callbacks and basically the function ends BEFORE the data has been retrieved.
The quickest fix is to remove the
context.succeed()
call and the data will be retrieved. Of course using the async module would help and if you don't want to use that, just add a counter or a boolean to your callback and then wait until the value has changed, indicating that the callback has been called (which kind of sucks if you think of it)I had some similar issues and did not find many helpful resources. Here's what I ended up doing. Probably someone smarter can tell us if this is bestest.
In summary, having the passback of callback through the main function to the smaller function, allows the application to continue until completing the Dynamo. Keep the context.succeed in the secondary function or continue other function there.
My problem is that my lambda was running in a VPC in order to connect to ElastiCache. This causes any queries to public Internet resources such as DynamoDB and API Gateway to hang indefinitely. I had to set up a NAT Gateway within my VPC in order to access DynamoDB.
To avoid callback hell, use Promises. There are some pretty good tutorials on youtube by guy called funfunfunction.