Expanding a little on Ashish's answer.
The problem:
As Ashish said, the reason the put
operation does not work in the Lambda deployment is that the call is performed asynchronously.
The call to dynamoDb[action](params).promise()
starts the asynchronous put
operation, and returns a Promise object. When the put
operation returns, the promise will be resolved.
However, in your code you neither await
for the promises to be resolved, nor do you return the promises as an output from the handler. The call to updatePrice
terminates and returns undefined
, at which point AWS Lambda suspends the execution of the function. As a result, the put
calls never go through.
Why is there a difference between the local and remote executions?
The reason you see a difference between the remote and local executions is that a local node.js process and a Lambda function have different semantics.
When you run a node.js process locally, the node.js process only terminates once all promises have been resolved1.
The Lambda execution behaves differently. Lambda does not wait for promises to be resolved before it terminates the execution2. Instead, Lambda terminates the execution as soon as it resolves the promise that is returned by the handler. In your case, the handler function returns true
3, so it is resolved immediately, and the execution terminates. At that point in time, your put
calls haven't resolved yet.
To illustrate the difference, consider the following code:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
module.exports.hello = async (event) => {
sleep(300).then(() => console.log("Finished doing my asynchronous thing, can exit now!"))
console.log("Function execution done!")
return true;
};
// call the handler when running locally:
if (require.main === module) { module.exports.hello(); }
When you're running this locally, the function execution finishes, but the node process is still running until the promise is resolved. Running locally, you will get the following output (notice the order):
> Function execution done!
> Finished doing my asynchronous thing, can exit now!
Running on lambda, when the function execution ends, so does the rest of the run. The promise is never resolved. We will get the following output:
START RequestId: <id> Version: $LATEST
2019-12-26T09:04:28.843Z <id> INFO Function execution done!
END RequestId: <id>
REPORT RequestId: <id> Duration: 3.37 ms Billed Duration: 100 ms Memory Size: 1024 MB Max Memory Used: 71 MB Init Duration: 114.44 ms
We only get the Function execution done!
print. Notice also that the execution Duration is only 3.37ms. The 300ms sleep we ran asynchronously did not have time to resolve before AWS stopped the process.
Fixing the problem
From the AWS Lambda developer guide:
If your code performs an asynchronous task, return a promise to make sure that it finishes running. When you resolve or reject the promise, Lambda sends the response or error to the invoker.
You can use Ashish's solution -- returning the promises you created. Alternatively, you can explicitly await
the Promise.all
. In either case it is important to make sure that you do not lose any of the promises you create before returning from function.
1 More specifically, it waits for the event loop to be empty.
2 It actually suspends the execution. The execution will continue from the same place the next time you invoke the handler. If you made multiple consecutive calls to the handler, some of your put
s would probably get through, depending on timing, and on how well the aws-sdk library handles these kinds of interruptions in the execution flow, but it is very hard to predict.
3 Actually, the return value of an async
function is always a promise that wraps the return value. So in your case what you have is a return true;
-- the true
is wrapped in a Promise
object, which is resolved immediately and the execution terminates.