我正在处理一个问题,当我在node.js应用程序中使用mongo数据库(使用mongoose)创建文档时,我会遇到重复的键错误。
更新特定用户的HTTP请求。
POST /process { "user": 123 }
服务器检查用户是否存在。如果它不存在,则会创建一个新用户,否则会更新用户并保存。
User.findOne({ "user": 123 }, function (err, doc) {
if (!doc) {
doc = new User({ ... });
}
doc.updated = Date.now();
doc.save(...);
}
有两个异步调用findOne
和save
。我遇到的问题是当一个异步调用(即doc.save
在第一个请求完成之前第二个HTTP请求(针对同一个用户)进来时会发生什么。即使node.js是单个的如果在异步I / O期间有一些延迟,这仍然会发生。
R1: POST /process
R1: findOne => async
R2: POST /process
R2: findOne => async
R1: !doc = true
R2: !doc = true
因此,对于这两个请求,应用程序认为用户不存在,因此尝试使用相同的密钥创建文档两次。
首先,我将findOne
和save
之间的时间缩短了一些。但是,问题仍然在某些情况下发生(只有1/1000)。
我不想使用upsert,因为在创建新用户时我还设置了一些默认值的其他字段。我认为这对于upsert来说会很棘手。
理想情况下,我想确保只有一个请求可以立即进入处理逻辑(有点像函数调用的互斥锁)。但是,我不想阻止请求调用 - 所以也许我可以使用一些很好的异步锁定实用程序?
示例:
/* don't block, call the function when a lock can be acquired */
lock(function (done) {
/* can only enter here one at a time */
done(); /* <-- unlocks */
}
或者,我是以错误的方式接近这个。有什么想法吗?
答案 0 :(得分:0)
简单。 不要直接使用.findOne()
和.save()
.update()
数据库中的对象。
MongoDB和&#34;锁定&#34; (以这种方式)只是不会发生,这基本上是设计。你并不打算:
这是可扩展模式中的一个非常大的 NO 。
所以而不是这样做。
&#34;在书架上书写新段落&#34;
最好是
&#34;在书架上书写新段落,其中当前段落是我期望的那样&#34; 。
那应该是一个明确的&#34;类推。
所以改为:
User.update(
{ "user": 123 },
{ "$set": { "updated": new Date() } },
function(err,numAffected) {
// do something in callback
}
);
简单地说&#34;&#34;只是更新当前&#34;更新&#34;在那里提出请求时的日期。
或更好:
var updatedDate = new Date();
User.update(
{ "user": 123, "updated": { "$lt": updatedDate } },
{ "$set": { "updated": updatedDate } },
function(err,numAffected) {
// do something in callback
}
);
未触及对象的是当前值&#34;更新&#34;是&#34;大于&#34;您要将其设置为的值。
纯粹的逻辑。