我正在尝试实现API网关调用的函数。它会传递一个电子邮件地址+密码,然后检查电子邮件地址是否已被使用。如果不是这种情况,则应将其放入我的发电机数据库表中。
当使用已经在使用的电子邮件地址对其进行测试时,仍然会执行put操作,尽管boolean应该设置为true。
'use strict';
var AWS = require('aws-sdk'),
uuid = require('uuid'),
documentClient = new AWS.DynamoDB.DocumentClient();
exports.handler = function(event, context, callback) {
if (event.body !== null && event.body !== undefined) {
let body = JSON.parse(event.body);
let eMailAddress = body.mail;
let password = body.password;
var EmailInUse = Boolean(false);
var paramsScan = {
TableName: "accounts"
};
documentClient.scan(paramsScan, function(err, data) {
for (var i in data.Items) {
i = data.Items;
if (i.EmailAddress == eMailAddress) {
console.log("already used");
callback(err, "Email Address already in Use!");
EmailInUse = true;
}
}
});
console.log(EmailInUse);
if (EmailInUse == false) {
console.log("should not enter if email used");
var params = {
Item: {
"AccountID": uuid.v1(),
"Password": password,
"EmailAddress": eMailAddress
},
TableName: "accounts"
};
documentClient.put(params, function(err, data) {
if (err) {
callback(err, null);
} else {
const response = {
statusCode: "200",
"headers": {},
body: JSON.stringify(params),
"isBase64Encoded": "false"
};
callback(null, response);
}
});
}
}
};
这是我的Cloudwatch日志,使用相同的参数调用它2次:
12:54:01
START RequestId: 281b0eda-950b-40fc-a2e2-d326cd04f8a4 Version: $LATEST
12:54:01
2019-02-26T12:54:01.434Z 281b0eda-950b-40fc-a2e2-d326cd04f8a4 false
12:54:01
2019-02-26T12:54:01.471Z 281b0eda-950b-40fc-a2e2-d326cd04f8a4 should not enter if email used
12:54:01
END RequestId: 281b0eda-950b-40fc-a2e2-d326cd04f8a4
12:54:01
REPORT RequestId: 281b0eda-950b-40fc-a2e2-d326cd04f8a4 Duration: 320.98 ms Billed Duration: 400 ms Memory Size: 128 MB Max Memory Used: 31 MB
12:54:47
START RequestId: b9df94ce-0d59-4dfb-8b61-8098db566431 Version: $LATEST
12:54:47
2019-02-26T12:54:47.591Z b9df94ce-0d59-4dfb-8b61-8098db566431 false
12:54:47
2019-02-26T12:54:47.591Z b9df94ce-0d59-4dfb-8b61-8098db566431 should not enter if email used
12:54:47
2019-02-26T12:54:47.812Z b9df94ce-0d59-4dfb-8b61-8098db566431 already used
12:54:47
END RequestId: b9df94ce-0d59-4dfb-8b61-8098db566431
12:54:47
REPORT RequestId: b9df94ce-0d59-4dfb-8b61-8098db566431 Duration: 311.87 ms Billed Duration: 400 ms Memory Size: 128 MB Max Memory Used: 31 MB
看着这个我注意到在检查电子邮件地址是否已被使用之后,调用最后一个日志输出“已经使用”。有人能告诉我如何解决这个问题吗?提前谢谢了。
问题只是同步。
函数documentClient.scan
在你的情况下使用回调。这意味着,在执行回调之前调用以下代码(console.log(EmailInUse);
等)。
您可以将所有内容放入回调中,或者使用async/await
,因为AWS Lambda支持Node.js 8.10:
var AWS = require('aws-sdk'),
uuid = require('uuid'),
documentClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async event => {
if (!event.body) return httpResponse(400, 'body is missing!');
try {
let body = JSON.parse(event.body);
let eMailAddress = body.mail;
let password = body.password;
var EmailInUse = Boolean(false);
var paramsScan = {
TableName: "accounts"
};
const data = await documentClient.scan(paramsScan).promise();
for (var i in data.Items) {
i = data.Items;
if (i.EmailAddress == eMailAddress) {
console.log("already used");
// you can just return here:
//return httpResponse(200, "Email Address already in Use!");
EmailInUse = true;
}
}
console.log(EmailInUse);
if (EmailInUse == false) {
console.log("should not enter if email used");
var params = {
Item: {
"AccountID": uuid.v1(),
"Password": password,
"EmailAddress": eMailAddress
},
TableName: "accounts"
};
await documentClient.put(params).promise();
return httpResponse(200, JSON.stringify(params));
}
} catch (err) {
return httpResponse(500, JSON.stringify(err));
}
};
function httpResponse(statusCode, body) {
return {
statusCode,
body,
"isBase64Encoded": "false"
};
}
您可以在找到电子邮件地址时完成该过程,然后您可以摆脱EmailInUse
变量 - 它使您的代码更短,更简单,更容易推理。
@ttulka的回答非常准确。
不过,我想在他的回答之上添加一些内容:
即使在回调 - 或异步/等待 - 整理后,您的代码仍然可能失败。那为什么呢?
DynamoDB是一个分布式系统。分布式系统本质上倾向于在其核心使用最终的一致性,而这正是DynamoDB默认的功能。
这意味着在使用@ttulka的代码片段修复代码后,您仍然可能会遇到eventual consistency问题。如果您想完全确定从表中读取最新值,则必须在查询中使用ConsistentRead属性。
请记住,DynamoDB运行的这些复制通常是闪电般快速的(大多数情况下它们只需要几百毫秒),但是你可能最终陷入某些灰色区域,然后你会想知道为什么你的代码没有'工作。
对于您的用例(检查现有的电子邮件),这应该没关系,因为两个人几乎不可能同时在同一封电子邮件中注册。但是,确保在处理关键数据(如银行账户)时,您应该始终支持ConsistentReads。与EventualConsistentReads相比,它们的成本是其两倍。
另外,请注意托马斯·爱德华兹的答案:扫描操作非常昂贵(性能和成本都很高)。你应该不惜一切代价avoid他们,而是使用Global Secondary Indexes。
希望这可以帮助!
编辑:修正ttulka的昵称后他指出:)
扫描是非常昂贵的,随着您的网站的增长,这将是非常有效的。
另外请记住,DynamoDB可能需要一些时间来保存记录,这就是您可能能够通过的原因。
如果要经常快速地搜索,请在DynamoDB中使用EmailAddress
上的索引,或者找到另一种检查重复的方法。我有一个单独缓存的已注册电子邮件索引来检查速度。