简体   繁体   English

如何防止Mongoose允许跨并行请求对文档进行多次更新?

[英]How can I prevent Mongoose from allowing multiple updates of a document across parallel requests?

Given the schema: 给定架构:

{
    widgets: [{
        widget: {type: mongoose.Schema.Types.ObjectId, ref: 'widget'},
        count: {type: Number}
    }]
}

and an application route where: 以及申请路线,其中:

  1. User attempts to add a new widget into the widgets array of an existing document. 用户尝试将新的窗口小部件添加到现有文档的窗口小部件数组中。
  2. We check that the widget being added meets some requirements. 我们检查添加的小部件是否满足某些要求。
  3. We add the widget to the document and save the document. 我们将小部件添加到文档中并保存文档。

In this setup, if I have 2 parallel requests to perform the same action, both checks in number 2 pass and the document is saved with 2 copies of the widget, even though that's "illegal" in my business logic. 在此设置中,如果我有2个并行请求来执行相同的操作,则两次都将检查2次通过,并且文档将与小部件的2个副本一起保存,即使这在我的业务逻辑中是“非法的”。

The async flow looks like: 异步流如下所示:

  1. Req1 hits route, loads existing doc Req1命中路线,加载现有文档
  2. Req2 hits route, loads existing doc Req2点击路线,加载现有文档
  3. Req1 checks doc conditions Req1检查文档条件
  4. Req2 checks doc conditions Req2检查文档条件
  5. Req1 embeds the new widget, saves doc Req1嵌入新的小部件,保存文档
  6. Req2 embeds the new widget, saves doc Req2嵌入新的小部件,保存文档

I thought that document versioning ( __v ) would solve this for me as it has in the past, but apparently I never understood this to begin with because both requests are running sequentially and my debugger shows that the version of the document is X in both pre-save states, and X+1 in the post-save state. 我以为文档版本控制( __v )可以像过去一样为我解决此问题,但是显然我从不理解这一点,因为两个请求都按顺序运行,并且调试器显示两个版本中的文档版本均为X -save状态,X + 1处于后保存状态。 I don't understand why that doesn't throw a version error. 我不明白为什么这不会引发版本错误。

I think that this is an asynchronous problem to solve, not necessarily strictly Mongoose, and have tagged as such. 我认为这是要解决的异步问题,不一定严格地是Mongoose,因此已被标记。

Edit: This works but seems remarkably verbose: 编辑:这有效,但似乎非常冗长:

model
    .findOneAndUpdate({
        _id: doc._id,
        __v: doc.__v
    },
    {
        $push: {
            widgets: {
                widget: widget_id,
                qty: 1
            }
        },
        $inc: {
            __v: 1
        }
    },
    function(err, doc) {
        // ...
    });

Also, it is unfortunate that I can't alter my existing doc and then run the save() method on it. 另外,很遗憾,我无法更改现有文档,然后在其上运行save()方法。

My searching found this bug where the versionKey didn't increment automatically when using this method. 我的搜索发现了此错误 ,其中使用此方法时versionKey不会自动增加。 I suppose I really don't understand versionKey properly! 我想我真的不太了解versionKey

Have a look at this explanations of versionKey property (if you haven't already) 看一下对versionKey属性的解释(如果还没有的话)

http://aaronheckmann.tumblr.com/post/48943525537/mongoose-v3-part-1-versioning http://aaronheckmann.tumblr.com/post/48943525537/mongoose-v3-part-1-versioning

The example from the article looks similar to yours except that they are modifying array items (comments) and you are pushing new item to widgets array. 本文中的示例与您的示例相似,不同之处在于它们正在修改数组项(注释),并且您将新项推送到小部件数组。 But as I understood if you use mongoose v3+ and perform save() operation, this will do all necessary operations with versionKey for you. 但是据我了解,如果您使用猫鼬v3 +并执行save()操作,那么它将为您执行所有必要的versionKey操作。

So if you will do something like: 因此,如果您将执行以下操作:

model.findById(doc._id, function(err, doc) {
  // handle error
  doc.widgets.push({ widget: widget_id, count: widget_count });
  doc.save(callback);
});

then save() operation should internally looks like this: 然后save()操作在内部应如下所示:

doc.update(
  { _id: doc._id, __v: doc.__v },
  { 
    $push: { 
      widgets: { widget: widget_id, count: widget_count } 
    },
    $inc: {
      __v: 1
    }
  }
);

So maybe you should make sure you use mongoose v3+ and do push() and then save() or is it how you did this stuff initially? 因此,也许您应该确保使用猫鼬v3 +并先执行push()然后再执行save(),或者这是您最初如何执行此操作的?

I haven't tested this, just wanted to share my search results and thoughts with you in case this can help somehow. 我尚未对此进行测试,只是想与您分享我的搜索结果和想法,以防万一。

Then maybe you can try to add widgets check in update query: 然后,也许您可​​以尝试添加小部件以检查更新查询:

model.findById(doc._id, function(err, doc) {
  // handle error

  // on update query check if there is no such widget in widgets array
  doc.update(
    { _id: doc._id, { 'widgets.widget': { $nin: [widget_id] } }},
    { 
      $push: { 
        widgets: { widget: widget_id, count: widget_count } 
      }
    }
  );
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM