Node.js findOne和updateOne的Mongo DB并发问题
我对更新同一文档的并发请求有问题。我没有使用Node.js findOne和updateOne的Mongo DB并发问题,node.js,mongodb,concurrency,Node.js,Mongodb,Concurrency,我对更新同一文档的并发请求有问题。我没有使用findAndModify(),因为我需要访问文档的当前状态来进行更新,findAndModify()不支持更新。我还希望避免使用db.fsyncLock(),因为这会锁定整个数据库,我只需要锁定一个集合中的一个文档 首先我使用findOne()获取文档,然后在findOne()的回调中使用updateOne()更新同一文档。当我将一组操作排队并同时运行它们时,我相信它们在调用findOne()时都在访问相同的状态,而不是等待updateOne()从上
findAndModify()
,因为我需要访问文档的当前状态来进行更新,findAndModify()
不支持更新。我还希望避免使用db.fsyncLock()
,因为这会锁定整个数据库,我只需要锁定一个集合中的一个文档
首先我使用findOne()
获取文档,然后在findOne()
的回调中使用updateOne()
更新同一文档。当我将一组操作排队并同时运行它们时,我相信它们在调用findOne()
时都在访问相同的状态,而不是等待updateOne()
从上一个操作完成
我该怎么处理
mongoDBPromise.then((db)=> {
db.collection("notes").findOne(
{path: noteId},
(err, result)=> {
if (err) {
console.log(err);
return;
}
if (!result.UndoableNoteList.future.length) {
console.log("Nothing to redo");
return;
}
let past = result.UndoableNoteList.past.concat(Object.assign({},result.UndoableNoteList.present));
let present = Object.assign({},result.UndoableNoteList.future[0]);
let future = result.UndoableNoteList.future.slice(1, result.UndoableNoteList.future.length);
db.collection("notes").updateOne(
{path: noteId},
{
$set: {
UndoableNoteList: {
past: past,
present: present,
future:future
}
}
},
(err, result)=> {
if (err) {
console.log(err);
return;
}
}
)
}
);
});
由于updateOne()
是一个异步调用,findOne()
不会等待它完成,因此可能存在同时更新同一文档的情况,这在mongo中是不允许的
我认为在这种情况下,updateOne()
是不必要的请注意,您已经在findOne()
query中找到了需要更新的文档的正确实例。现在,您可以更新该实例并保存该文档,而无需执行updateOne()
。我认为这样可以避免这个问题:
mongoDBPromise.then((db)=> {
db.collection("notes").findOne(
{path: noteId},
(err, result)=> {
if (err) {
console.log(err);
return;
}
if (!result.UndoableNoteList.future.length) {
console.log("Nothing to redo");
return;
}
let past = result.UndoableNoteList.past.concat(Object.assign({},result.UndoableNoteList.present));
let present = Object.assign({},result.UndoableNoteList.future[0]);
let future = result.UndoableNoteList.future.slice(1, result.UndoableNoteList.future.length);
result.UndoableNoteList.past = past;
result.UndoableNoteList.present = present;
result.UndoableNoteList.future = future;
//save the document here and return
}
);
});
希望这个答案对你有帮助 我无法找到使用纯mongodb函数顺序运行查询的方法。我编写了一些node.js逻辑,阻止mongodb查询在同一文档上运行,并将这些查询添加到队列中。下面是代码当前的样子 Websocket撤消侦听器
module.exports = (noteId, wsHelper, noteWebSocket) => {
wsHelper.addMessageListener((msg, ws)=> {
if (msg.type === "UNDO") {
noteWebSocket.broadcast(msg, noteWebSocket.getOtherClientsInPath(noteId, wsHelper));
noteWebSocket.saveUndo(noteId);
}
});
};
从侦听器调用的saveUndo
函数
saveUndo(noteId) {
this.addToActionQueue(noteId, {payload: noteId, type: "UNDO"});
this.getNoteByIdAndProcessQueue(noteId);
}
从saveUndo调用的getNoteByIdAndProcessQueue
函数
getNoteByIdAndProcessQueue(noteId) {
if (this.isProcessing[noteId])return;
this.isProcessing[noteId] = true;
mongoDBPromise.then((db)=> {
db.collection("notes").findOne(
{path: noteId},
(err, result)=> {
if (err) {
this.isProcessing[noteId] = false;
this.getNoteByIdAndProcessQueue(noteId);
return;
}
this.processQueueForNoteId(noteId, result.UndoableNoteList);
});
});
}
processQueueForNoteId
函数
processQueueForNoteId(noteId, UndoableNoteList) {
this.actionQueue[noteId].forEach((action)=> {
if (action.type === "UNDO") {
UndoableNoteList = this.undoNoteAction(UndoableNoteList);
} else if (action.type === "REDO") {
UndoableNoteList = this.redoNoteAction(UndoableNoteList);
} else if (action.type === "ADD_NOTE") {
UndoableNoteList = this.addNoteAction(UndoableNoteList, action.payload);
} else if (action.type === "REMOVE_NOTE") {
UndoableNoteList = this.removeNoteAction(UndoableNoteList, action.payload);
}
});
let actionsBeingSaved = this.actionQueue[noteId].concat();
this.actionQueue[noteId] = [];
mongoDBPromise.then((db)=> {
db.collection("notes").updateOne(
{path: noteId},
{
$set: {
UndoableNoteList: UndoableNoteList
}
},
(err, result)=> {
this.isProcessing[noteId] = false;
// If the update failed then try again
if (err) {
console.log("update error")
this.actionQueue[noteId] = actionsBeingSaved.concat(this.actionQueue[noteId]);
}
// if action were queued during save then save again
if (this.actionQueue[noteId].length) {
this.getNoteByIdAndProcessQueue(noteId);
}
}
)
});
}
当我删除updateOne时,文档不会保存。我目前有一个解决方案,我将db调用排队,然后一次执行一个,但是如果db管理这个队列就好了。当然,它不会被保存,除非您显式地调用
result.save()
。但同样,如果请求是并行生成的,则相同文档的result.save()
可以同时发生。result对象只是一个包含我的数据的普通对象。调用result.save()