2017-04-19 79 views
0

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

首先我使用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; 
        } 
       } 
      ) 
     } 
    ); 
}); 

回答

0

我无法找到一种方法来使用纯粹的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

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功能

称为

saveUndo(noteId) { 
    this.addToActionQueue(noteId, {payload: noteId, type: "UNDO"}); 
    this.getNoteByIdAndProcessQueue(noteId); 
} 

getNoteByIdAndProcessQueue功能

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

由于updateOne()是一个异步调用,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); 
      result.UndoableNoteList.past = past; 
      result.UndoableNoteList.present = present; 
      result.UndoableNoteList.future = future; 

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

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

+0

当我删除updateOne时,不会保存文档。我现在有一个解决方案,我排队的数据库调用,并一次做一个,但它会很好,如果数据库管理这个队列 – sissonb

+0

当然,它不会被保存,除非你调用结果。保存()'明确。但是,如果请求是并行产生的,'result.save()'可以同时发生在同一个文档中。 –

+0

结果对象只是一个包含我的数据的普通对象。调用'result.save()'给了我下面的错误'TypeError:result.save不是函数' – sissonb