DynamoDB 记录未通过映射循环添加



我正在使用 Axios 从 API 获取价格(通常为数千(,然后想将其存储在 DynamoDB 中。如果我在本地调用 lambda 函数,一切按预期工作,但如果我部署函数并使用 AWS CLI 调用它,它不再在 DynamoDB 中存储任何值。我在请求中收到的数据和来自 Axios 调用的响应是相同的。

我不知何故认为这是调用 DynamoDB 的异步函数的范围问题,但我无法解决它。期待您的建议。如果您需要更多代码,请告诉我。

更新价格.js

import { updatePrice } from "./libs/pricing-lib";
import {
success,
failure
} from "./libs/response-lib";
export async function main(event, context) {
try {
let result = await updatePrice(event.pathParameters.id, event.pathParameters.date);
return success(result);
} catch(e) {
return failure(e);
}
}

dynamodb-lib-js

import AWS from "aws-sdk";
export function call(action, params) {
const dynamoDb = new AWS.DynamoDB.DocumentClient();
return dynamoDb[action](params).promise();
}

定价库.js

export async function updatePrice(stockid, from) {
try {
let url = getUrl(stockid, from);
const resultEodApi = (await axios.get(url)).data;
resultEodApi.map((price) => {
try {
let priceParams = {
TableName: process.env.pricesTableName,
Item: {
stockid: stockid,
date: price.date,
close: price.close
}
};
dynamoDbLib.call("put", priceParams);
} catch (e) {
return e;
}
});
return true;
} catch (e) {
return e;
}
}

稍微扩展一下 Ashish 的答案。

问题:

正如 Ashish 所说,put操作在 Lambda 部署中不起作用的原因是调用是异步执行的。

dynamoDb[action](params).promise()的调用将启动异步put操作,并返回 Promise 对象。当put操作返回时,承诺将被解决。

但是,在代码中,既不会await要解析承诺,也不会将承诺作为处理程序的输出返回。对updatePrice的调用终止并返回undefined,此时AWS Lambda 将暂停函数的执行。结果,put呼叫永远不会通过。

为什么本地执行和远程执行之间存在差异?

您看到远程执行和本地执行之间存在差异的原因是本地节点.js进程和 Lambda 函数具有不同的语义。

在本地运行节点.js进程时,节点.js进程仅在解决所有承诺后终止1

Lambda 执行的行为有所不同。Lambda 不会等待承诺得到解决,然后再终止执行2。相反,Lambda 会在解析处理程序返回的承诺后立即终止执行。在您的情况下,处理程序函数返回true3,因此会立即解析,并且执行终止。此时,您的put呼叫尚未解决。

为了说明差异,请考虑以下代码:

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(); }

在本地运行此函数时,函数执行完成,但节点进程仍在运行,直到解析承诺。在本地运行,您将获得以下输出(请注意顺序(:

> Function execution done!
> Finished doing my asynchronous thing, can exit now!

在 lambda 上运行,当函数执行结束时,其余的运行也会结束。承诺永远不会解决。我们将得到以下输出:

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    

我们只得到Function execution done!打印。另请注意,执行持续时间仅为 3.37 毫秒。在 AWS 停止进程之前,我们异步运行的 300 毫秒睡眠没有时间解决。

修复问题

来自 AWS Lambda 开发人员指南:

如果代码执行异步任务,请返回一个 promise 以确保它完成运行。当您解析或拒绝承诺时,Lambda 会将响应或错误发送给调用方。

您可以使用Ashish的解决方案 - 返回您创建的承诺。或者,您可以显式awaitPromise.all。无论哪种情况,重要的是要确保在从函数返回之前不会丢失您创建的任何承诺。


1更具体地说,它等待事件循环为空。

2它实际上暂停了执行。下次调用处理程序时,执行将从同一位置继续。如果您连续多次调用处理程序,则某些put可能会通过,具体取决于时间以及 aws-sdk 库在执行流中处理此类中断的能力,但很难预测。

3实际上,async函数的返回值始终是包装返回值的承诺。因此,在您的情况下,您拥有的是一个return true;-true被包装在一个Promise对象中,该对象立即解析并终止执行。

问题在于定价库.js因为您在 map 中进行异步操作。您需要从地图内部返回承诺,并将地图包装在承诺全部中。看这里

export async function updatePrice(stockid, from) {
try {
let url = getUrl(stockid, from);
const resultEodApi = (await axios.get(url)).data;
return Promise.all(resultEodApi.map((price) => {
try {
let priceParams = {
TableName: process.env.pricesTableName,
Item: {
stockid: stockid,
date: price.date,
close: price.close
}
};
return dynamoDbLib.call("put", priceParams);
} catch (e) {
return e;
}
}));
return true;
} catch (e) {
return e;
}
}

对于这个问题,我认为最好使用批处理请求。

https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/dynamodb-example-table-read-write-batch.html

我也有同样的问题,我重写了我的 util 函数以使用批处理请求,现在更容易理解。

相关内容

  • 没有找到相关文章

最新更新