编辑6/15 我尝试运行相同的代码,在收到ConcurrencyDBError之前再次调用$doSafePush()
之前添加延迟(即执行return when.resolve("wait").delay(35).then(function() {$doSafePush(err.object, "set", value, retries-1);});
而不是return $doSafePush(err.object, "set", value, retries-1);
})。只要我等待至少35毫秒,它就可以正常工作;低于该阈值它有时。感觉像魔术数字,但它正在工作......
仍然有兴趣了解为什么如果有人有任何线索:)
我在遇到并发读取时遇到了问题在node.js
下的MongoDB中对单个文档编写操作请参阅下面我正在运行的代码。
基本上我将一个Collection定义为Mongo DB,结构为{_id: ObjectID, __occ: Number, set: Array}
。由于我有多个事务要在“并行”中执行,所以我想在我只更新文档时使用经典的乐观一致性控制框架来编写安全推送,如果我正在使用的版本具有正确的_id和右__occ;否则我强行重装并再试一次。
本质上,它会导致多次写入和读取(如果写入失败)操作同时在同一个文档上运行。
不幸的是,我无法让它发挥作用。在某些时候,Mongo开始返回有趣的文档,它会搞砸一切。
从下面运行代码,我有以下输出(对应于下面最后一个函数最底部的debug(...)命令)
第1部分:工作得很好
29033b successfully added >>> occ=1 set={#1 elts, ["29033b"]} +87ms
29033c successfully added >>> occ=2 set={#2 elts, ["29033b","29033c"]} +33ms
29033d successfully added >>> occ=3 set={#3 elts, ["29033b","29033c","29033d"]} +37ms
第2部分:第一个misshap:前一个项目#3被另一个项目替换=>第一个数据丢失
290342 successfully added >>> occ=3 set={#3 elts, ["29033b","29033c",**"290342"**]} +37ms
第3部分:第2次发生错误;回到原来的项目#3 => '290342'丢失
29033e successfully added >>> occ=4 set={#4 elts, ["29033b","29033c",**"29033d"**,"29033e"]} +32ms
290344 successfully added >>> occ=5 set={#5 elts, ["29033b","29033c","29033d","29033e","290344"]} +10ms
290343 successfully added >>> occ=6 set={#6 elts, ["29033b","29033c","29033d","29033e","290344","290343"]} +35ms
第4部分:第3次发生错误;我们回到第290位的'290342'项目;它变得丑陋
29033f successfully added >>> occ=4 set={#4 elts, ["29033b","29033c",**"290342"**,"29033f"]} +31ms
第5部分:第4次事故;而且第3项已经改变,第3部分的第4项丢失了
290340 successfully added >>> occ=5 set={#5 elts, ["29033b","29033c","29033d",**"29033e"**,**"290340"**]} +35ms
第6部分:第5次发生事故;第4项已更改
290341 successfully added >>> occ=5 set={#5 elts, ["29033b","29033c","29033d","29033e",**"290341"**]} +45ms
这就是我在MongoDB中所拥有的(通过mongo shell查询)
{ "_id" : ObjectId("575f8981762120ce1a290324"), "__occ" : 5, "set" : [ "29033b", "29033c", "29033d", "29033e", "290340" ] }
我期待findOneAndUpdate是原子的,但它对我来说很奇怪。任何人都可以帮我弄清楚我做错了什么
提前致谢
PS:缩短所有数字以便于阅读,但实际上是正确的MongoDB ID,例如: 575f8981762120ce1a29033b => 29033b
// ------ MAIN CODE ------
var document = 'a previously loaded document from MongoDB'; // has _id and __occ properties
var valueList = [
"29033b",
"29033c",
"29033d",
"29033e",
"29033f",
"290340",
"290341",
"290342",
"290343",
"290344"
];
var promiseArray = [];
for (let i = 0; i < valueList.length; i++) {
promiseArray.push(safePush(document, valueList[i]));
}
return when.all(promiseArray)
.then(function(array) {
// do something with results
})
// ------ FUNCTIONS USED ------
function safePush(obj, value) {
return $doSafePush(obj, "set", value)
.then(function(updatedObject) {
return updatedObject;
}).catch(function(err) {
if (err instanceof ConcurrencyDBError) {
if (retries > 0) {
return $doSafePush(err.object, "set", value, retries-1);
} else {
err.message += " => max number of retries reached, failing";
}
}
// else
var message = err.message || "ERROR Unexpected error";
return when.reject(GenericError.create(message, err));
});
}
function $doSafePush(obj, property, value) {
var $query = {
'_id': obj.id,
'__occ': obj.__occ
}
var $update = {
"$push": {}
"$inc": {
"__occ": 1
}
};
$update["$push"][property] = value; // Update == $push value into 'property' array and $inc __occ counter
return $getMongoCollection("Users").findOneAndUpdate($query, $update, {returnOriginal: false})
.then(function(r) {
var result = r.value;
if (result == null) {
// No object matching $query found => OCC error
// Load new object and reject Promise
return $getMongoCollection().findOne({_id: new ObjectID(obj._id)})
.then(function(newObj) {
return when.reject(new ConcurrencyDBError(util.format("ERROR Concurrency error trying to add %s to %s", value, property), newObj));
});
}
// Else Object was found and updated => return it
debug("BGCHECK 3 >>> OK >>> %s successfully added >>> occ=%d set={#%d elts, %j}", value, result.__occ, u.getProperty(result, property).length, u.getProperty(result, property));
return when.resolve(result);
});
}