Mongoose异步多保存冲突

Mongoose asynchronous multiple save conflicts

本文关键字:冲突 保存 异步 Mongoose      更新时间:2023-09-26

我在使用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) { });
        }
    }

有时,两个人会同时调用这个,而第一个人会保存(),另一个人可能已经找到了"服务器"findOne,并得到了不是最新的"旧对象"并保存()。

这里的大问题是,当第二个人保存()"服务器"("旧对象")时,它会覆盖第一个的更改。。。你可以想象它会在我的应用程序上产生多大的冲突。

我曾想过将所有的save()方法改为update(),这样就可以解决这个问题,但在项目的某个阶段,直接使用update()非常棘手,而且不太实用。

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

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

希望你能理解我的问题;)非常感谢。

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

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

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

如果你想实现某种类型的"锁定",那么类似的方法是最好的方法。因此,如果你想在文档上设置一个"状态"来显示有人正在编辑它,那么就维护一个字段来进行编辑,并将其构建到你的查询中。

为了"阅读"一份文件并获得你想呈现给"编辑"的信息,你可以做这样的事情:

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

这意味着当有人"抓取"编辑时,随后的操作将无法做到这一点,因为他们希望检索锁定状态为false的文档,而现在已经不是了。当将编辑提交为"保存"时,同样的原则也适用,只是相反:

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

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

我刚刚意识到我发布了一篇关于堆栈溢出的类似帖子。你可以在这里找到帖子:如何使用mongoDB/mongoose 在并行执行中读取/写入文档

在这篇帖子中,有人告诉我要记住一些日期,以避免这种行为。这就是我所做的,而且效果很好。但是,如果您使用多进程,则需要找到一种在进程之间共享内存的方法。