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

时间:2015-10-28 17:39:55

标签: node.js mongoose

鉴于架构:

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

和申请路线:

  1. 用户尝试将新窗口小部件添加到现有文档的窗口小部件数组中。
  2. 我们检查添加的小部件是否符合某些要求。
  3. 我们将小部件添加到文档中并保存文档。
  4. 在此设置中,如果我有2个并行请求执行相同操作,则2号检查都会通过,文档将保存2个小部件副本,即使这在我的业务逻辑中是“非法的”

    异步流程如下:

    1. Req1点击路线,加载现有文档
    2. Req2点击路线,加载现有文档
    3. Req1检查doc条件
    4. Req2检查doc条件
    5. Req1嵌入新窗口小部件,保存文档
    6. Req2嵌入新窗口小部件,保存文档
    7. 我认为文档版本控制(__v)会像过去那样为我解决这个问题,但显然我从来没有理解这一点,因为两个请求都是按顺序运行的,而我的调试器显示的是文档在两个预保存状态下都是X,在保存后状态下是X + 1。我不明白为什么不会抛出版本错误。

      我认为这是一个需要解决的异步问题,不一定是严格的Mongoose,并且已经标记为这样。

      编辑:这有效,但看起来非常冗长:

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

      另外,遗憾的是我无法更改现有文档,然后对其运行save()方法。

      我的搜索发现this bugversionKey在使用此方法时未自动增加versionKey。我想我真的不理解ParseUser(json)

2 个答案:

答案 0 :(得分:1)

看一下versionKey属性的解释(如果你还没有)

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

文章中的示例与您的类似,只是它们正在修改数组项(注释),并且您正在将新项目推送到窗口小部件数组。但据我所知,如果您使用mongoose v3 +并执行save()操作,这将为您执行所有必要的操作与versionKey。

所以,如果你做的事情如下:

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

然后save()操作应该在内部看起来像这样:

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

所以也许你应该确保你使用mongoose v3 +并执行push()然后保存()或者你最初是这样做的?

我没有对此进行过测试,只是想与您分享我的搜索结果和想法,以防万一这会有所帮助。

答案 1 :(得分:0)

然后也许你可以尝试添加小部件检查更新查询:

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