Mongoose异步多重保存冲突

Lau*_*ent 6 javascript mongoose mongodb node.js socket.io

我使用Node/Mongoose/Socket.io有一个很大的逻辑问题...假设我有一个服务器模型,它通常在我的应用程序中同时调用,有些调用涉及更新模型中的数据.

    db.Server.findOne({_id: reference.server}).exec(function(error, server) {

        catches.error(error);

        if (server !== null) {

              server.anything = "ahah";

              server.save(function(error) { });

        }

    }
Run Code Online (Sandbox Code Playgroud)

有时,2个人会同时打电话给这个,而第一个人会保存()另一个人已经找到了一个"服务器"并得到了"旧对象",它不是最新的并保存( )它.

这里的一个大问题是,当第二个人将保存()"服务器"("旧对象")时,它将逐字地覆盖第一个的变化......你可以想象它将在我的应用程序上创建的大冲突.

我考虑将所有save()方法更改为update()来解决问题但是在项目中的某些时候直接使用update()非常棘手,而不是那么实用.

当有人更新时,有没有办法"锁定"findOne()调用?就像你发现一个()时你也会说"嘿,我会尽快更新,所以不要让人们现在就找到它"(使用Mongoose,甚至是MongoDb)

已经有一段时间我正在搜索我没有找到任何答案:(

希望你理解我的问题;)谢谢!

Nei*_*unn 4

正如您在这里可以看出的,这不是处理数据更新的最佳方式。如果您考虑您要求做的事情,那么基本上可以归结为:

  1. 从数据库中获取对象。
  2. 更新代码中的属性。
  3. 将该数据保存回来,并不能保证其他东西会修改它。

因此,在可能的情况下,您需要避免这种模式并遵循常识,即您只需要更改当前未设置为该值的现有值。因此,这意味着只需使用运算符处理“更新”类型的语句,例如$set

db.Server.findOneAndUpdate(
    { "_id": refernce.server, "anything": { "$ne": "ahah" } },
    { "$set": { "anything": "ahah" } },
    function(err,server) {
       if ( server != null ) {

          // then something was actually found and modified
          // so server now is the updated document

       }
    }
);
Run Code Online (Sandbox Code Playgroud)

这意味着您将丢弃猫鼬的任何字段验证或其他保存挂钩,但它是一种“原子”形式的更新,因为读取和写入不是单独的操作,这就是您当前的实现方式。

如果您希望实现某种类型的“锁定”,那么类似的方法是您实现此目的的最佳方法。因此,如果您想在文档上设置“状态”以显示某人当前正在编辑该文档,请维护一个字段来执行此操作并将其构建到您的查询中。

为了“阅读”文档并获取您想要呈现给“编辑”的信息,您可以执行以下操作:

db.Server.findOneAndUpdate(
    { "$_id": docId, "locked": false },
    { "$set": { "locked": true } },
    function(err,document) {

    }
);
Run Code Online (Sandbox Code Playgroud)

这意味着当有人“抓取”编辑内容时,后续操作将无法执行此操作,因为他们正在寻求检索锁定状态为 的文档false,但现在已不再是。当将编辑提交为“保存”时,同样的原则也适用,只是相反:

db.Server.findOneAndUpdate(
    { "$_id": docId, "locked": true },
    { "$set": { "locked": false } },
    function(err,document) {

    }
);
Run Code Online (Sandbox Code Playgroud)

您始终可以执行更高级的操作,例如保存修订或通过操作或任何其他形式的处理来获取版本号。但一般来说,您应该根据自己的需要自行管理