使用mongoose中间件去除相关文档时的并发性问题

假设我们有一个简单的应用程序,用户可以创build产品并对其进行评论。 产品和评论的模式可能是:

var productSchema = new mongoose.Schema({ author_id: ObjectId, description: String }); var commentSchema = new mongoose.Schema({ product_id: ObjectId, author_id: ObjectId, message: String }); 

我们要确保每个评论都是指现有的产品。 这可以很容易地完成与mongoose预存钩:

 commentSchema.pre("save", function(next) { Product.count({ _id: this.product_id }, function(err, count) { if (err || !count) { next(new Error("Could not find product")); } else { next(); } }); }); 

另外,如果用户移除产品,我们希望移除该产品上的所有评论。 这可以很容易地完成使用预先删除挂钩:

 productSchema.pre("remove", function(next) { Comment.remove({ product_id: this._id }, next); }); 

但是,如果用户A删除一个产品,同时用户B对该产品进行评论呢?

可能会出现以下情况:

 Call pre save hook for new comment, and check if product exists Call pre remove hook for product, and remove all comments In pre save hook, done checking: product actually exists, call next Comment saved In pre remove hook, done removing comments: call next Product removed 

最终的结果是,我们有一个评论是指一个不存在的产品。

这只是导致这种情况发生的许多情况之一。 如何防止这个angular落案件?

       

网上收集的解决方案 "使用mongoose中间件去除相关文档时的并发性问题"

似乎使用mongoosepost hook s而不是pre hook解决了这个问题:

 commentSchema.post("save", function(comment) { Product.count({ _id: comment.product_id }, function(err, count) { if (err || !count) comment.remove(); }); }); productSchema.post("remove", function(product) { Comment.remove({ product_id: product._id }).exec(); }); 

让我们来看看为什么通过考虑四种可能的情况(我能想到)来解决问题:

 1) Comment gets saved before product is removed 2) Comment gets saved after product is removed but before post remove hook 3) Comment gets saved after product is removed and while post remove hook is executing 4) Comment gets saved after product is removed and post remove hook executed ------------------------------------------------------------------------ In case 1, after the product is removed, the comment will be removed in the post remove hook. In case 2, same, post remove hook will remove the comment. In case 3, the comment post save hook will successfully remove the comment. In case 4, same as case 3, post save hook removes the comment. 

但是,仍然有一个小问题:如果产品被删除后,但执行post remove hook之前发生了什么事情呢? 说电力或类似的东西。 在这种情况下,我们将最终以涉及不存在的产品的评论为结束。 为了解决这个问题,我们可以保留产品上的pre remove hook 。 这保证了只有在删除所有依赖注释的情况下才能删除产品。 但是,正如OP所指出的那样,这并不能解决并发问题,这就是我们post remove hook来救援的地方! 所以我们需要两个:

 productSchema.pre("remove", function(next) { var product = this; Comment.remove({ product_id: product._id }, next); }); productSchema.post("remove", function(product) { Comment.remove({ product_id: product._id }).exec(); }); 

我希望这是它,但我仍然可以想到一个非常遥远的情况:如果一个评论得到保存后产品被删除和post remove hook执行,但在评论post save hook执行(这将删除评论)灯熄灭! 我们最后得到的是一个不存在的产品的评论! 这种情况发生的可能性非常低,但仍然..

如果有人能想到一个更好的方法来处理并发,请改进我的答案或写自己的!

如何标志。

 var deletingPostOfId = null; function deletePost(id) { deletingPostOfId = id; Post.remove({_id: id}, function() { deletingPostOfId = null; }) } function createComment(comment) { if(comment.post_id !== deletingPostOfId) Comment.save(...) }