与findOne和updateOne的Mongo DB并发问题

时间:2017-04-19 03:20:47

标签: node.js mongodb concurrency

我遇到了更新同一文档的并发请求的问题。我没有使用findAndModify(),因为我需要访问文档的当前状态以进行findAndModify()支持的更新。我也想避免使用db.fsyncLock(),因为它会锁定整个数据库,我只需要将一个文档锁定在一个集合中。

首先我使用findOne()来获取文档,然后在updateOne()的回调中使用findOne()来更新同一文档。当我排队一系列操作并立即运行它们时,我相信他们在调用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;
                    }
                }
            )
        }
    );
});

2 个答案:

答案 0 :(得分:0)

由于updateOne()是异步调用,findOne()不会等待它完成,因此可能会出现同时更新同一文档的情况,这在mongo中是不允许的。

我认为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);
            result.UndoableNoteList.past = past;
            result.UndoableNoteList.present = present;
            result.UndoableNoteList.future = future;

            //save the document here and return
        }
    );
});

希望这个答案可以帮到你!

答案 1 :(得分:0)

我无法找到使用纯mongodb函数顺序运行查询的方法。我编写了一些node.js逻辑,阻止mongodb查询在同一文档上运行,并将这些查询添加到队列中。这是代码目前的样子。

Websocket Undo Listener

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);
                }
            }
        )
    });
}