未通过映射循环添加 DynamoDB 记录
DynamoDB record not added through map loop
我正在使用 Axios 从 API 获取价格(通常为数千),然后想将其存储在 DynamoDB 中。如果我在本地调用 lambda 函数,一切都会按预期工作,但如果我部署该函数并使用 AWS CLI 调用它,它就不会再在 DynamoDB 中存储任何值。我在请求中收到的数据和 Axios 调用的响应是相同的。
不知何故,我认为这是调用 DynamoDB 的异步函数的范围问题,但我无法解决它。期待您的建议。如果您需要更多代码,请告诉我。
updatePrice.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();
}
定价-lib.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;
}
}
问题在于定价 - lib.js 因为您在地图内部进行异步操作。您将需要 return 从地图内部承诺并将地图包装在承诺全部中。看这里
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;
}
}
稍微扩展一下 Ashish 的回答。
问题:
正如 Ashish 所说,put
操作在 Lambda 部署中不起作用的原因是调用是异步执行的。
对 dynamoDb[action](params).promise()
的调用启动异步 put
操作,并且 return 是一个 Promise 对象。当 put
操作 returns 时,promise 将被解析。
但是,在您的代码中,您既没有 await
承诺要解决,也没有 return 承诺作为处理程序的输出。对 updatePrice
的调用终止并且 returns undefined
,此时 AWS Lambda 暂停函数 的执行。结果,put
次呼叫永远不会通过。
为什么本地执行和远程执行有区别?
您看到远程和本地执行之间存在差异的原因是本地 node.js 进程和 Lambda 函数具有不同的语义。
当您 运行 在本地 node.js 进程时,node.js 进程只会在所有承诺都已解决后终止1.
Lambda 的执行行为不同。 Lambda 在终止执行之前不会等待承诺得到解决2。相反,Lambda 在解决了由处理程序return编辑的承诺后立即终止执行。在你的例子中,处理函数 returns true
3,所以它立即被解析,并且执行终止。那时,您的 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(); }
当您在本地 运行ning 时,函数执行完成,但节点进程仍在 运行ning,直到承诺得到解决。 运行 在本地,你会得到如下输出(注意顺序):
> 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 毫秒睡眠没有时间解决。
解决问题
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.
您可以使用 Ashish 的解决方案 -- return使用您创建的承诺。或者,您可以显式 await
Promise.all
。在任何一种情况下,重要的是要确保您不会丢失在从函数 returning 之前创建的任何承诺。
1更具体地说,它等待事件循环为空。
2 其实就是暂停执行。下次调用处理程序时,将从同一位置继续执行。如果您对处理程序进行多次连续调用,您的某些 put
可能会通过,具体取决于时间安排以及 aws-sdk 库在执行流程中处理此类中断的程度,但它是很难预测。
3 实际上,async
函数的 return 值始终是包装 return 值的承诺。因此,在您的情况下,您拥有的是 return true;
—— true
包装在 Promise
对象中,该对象立即解析并终止执行。
对于这个问题,我觉得还是使用批量请求比较好
我也有同样的问题,我重写了我的 util 函数以使用批处理请求,现在更容易理解了。
我正在使用 Axios 从 API 获取价格(通常为数千),然后想将其存储在 DynamoDB 中。如果我在本地调用 lambda 函数,一切都会按预期工作,但如果我部署该函数并使用 AWS CLI 调用它,它就不会再在 DynamoDB 中存储任何值。我在请求中收到的数据和 Axios 调用的响应是相同的。
不知何故,我认为这是调用 DynamoDB 的异步函数的范围问题,但我无法解决它。期待您的建议。如果您需要更多代码,请告诉我。
updatePrice.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();
}
定价-lib.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;
}
}
问题在于定价 - lib.js 因为您在地图内部进行异步操作。您将需要 return 从地图内部承诺并将地图包装在承诺全部中。看这里
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;
}
}
稍微扩展一下 Ashish 的回答。
问题:
正如 Ashish 所说,put
操作在 Lambda 部署中不起作用的原因是调用是异步执行的。
对 dynamoDb[action](params).promise()
的调用启动异步 put
操作,并且 return 是一个 Promise 对象。当 put
操作 returns 时,promise 将被解析。
但是,在您的代码中,您既没有 await
承诺要解决,也没有 return 承诺作为处理程序的输出。对 updatePrice
的调用终止并且 returns undefined
,此时 AWS Lambda 暂停函数 的执行。结果,put
次呼叫永远不会通过。
为什么本地执行和远程执行有区别?
您看到远程和本地执行之间存在差异的原因是本地 node.js 进程和 Lambda 函数具有不同的语义。
当您 运行 在本地 node.js 进程时,node.js 进程只会在所有承诺都已解决后终止1.
Lambda 的执行行为不同。 Lambda 在终止执行之前不会等待承诺得到解决2。相反,Lambda 在解决了由处理程序return编辑的承诺后立即终止执行。在你的例子中,处理函数 returns true
3,所以它立即被解析,并且执行终止。那时,您的 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(); }
当您在本地 运行ning 时,函数执行完成,但节点进程仍在 运行ning,直到承诺得到解决。 运行 在本地,你会得到如下输出(注意顺序):
> 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 毫秒睡眠没有时间解决。
解决问题
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.
您可以使用 Ashish 的解决方案 -- return使用您创建的承诺。或者,您可以显式 await
Promise.all
。在任何一种情况下,重要的是要确保您不会丢失在从函数 returning 之前创建的任何承诺。
1更具体地说,它等待事件循环为空。
2 其实就是暂停执行。下次调用处理程序时,将从同一位置继续执行。如果您对处理程序进行多次连续调用,您的某些 put
可能会通过,具体取决于时间安排以及 aws-sdk 库在执行流程中处理此类中断的程度,但它是很难预测。
3 实际上,async
函数的 return 值始终是包装 return 值的承诺。因此,在您的情况下,您拥有的是 return true;
—— true
包装在 Promise
对象中,该对象立即解析并终止执行。
对于这个问题,我觉得还是使用批量请求比较好
我也有同样的问题,我重写了我的 util 函数以使用批处理请求,现在更容易理解了。