假设我们有一个简单的应用程序,用户可以在其中创建产品并对其进行评论。产品和评论的架构可以是:
var productSchema = new mongoose.Schema({
author_id: ObjectId,
description: String
});
var commentSchema = new mongoose.Schema({
product_id: ObjectId,
author_id: ObjectId,
message: String
});
我们希望确保每条评论都涉及现有产品。这可以通过mongoose pre save hook轻松完成:
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
最终结果是我们有一条评论指的是不存在的产品。
这只是造成这种情况发生的众多案例之一。如何防止这种角落的情况?
答案 0 :(得分:2)
似乎使用mongoose post hook
而不是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
执行之前,该怎么办?这将取消评论)灯灭!我们最终得到的评论指的是不存在的产品!发生这种情况的可能性非常低,但仍然是......
如果有人能想出更好的处理并发的方法,请改进我的答案或自己编写!
答案 1 :(得分:0)
旗帜怎么样。
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(...)
}