使用 mongoose 中间件删除依赖文档时的并发问题

2024-04-11

假设我们有一个简单的应用程序,用户可以在其中创建产品并对其进行评论。产品和评论的架构可以是:

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

最终结果是我们的评论提到了不存在的产品。

这只是导致这种情况发生的众多情况之一。如何防止这种极端情况?


似乎使用猫鼬post hooks 而不是pre hooks 解决了这个问题:

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执行(这将删除注释)灯熄灭!我们最终得到的评论涉及的是不存在的产品!这种情况发生的几率非常低,但仍然......

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 mongoose 中间件删除依赖文档时的并发问题 的相关文章

  • 从 DOM 中删除后,动态添加的 JavaScript 脚本会继续执行

    因此 我正在创建一个 SPA 并使用 AJAX 将 HTML 页面加载到我网站的索引页面中 问题是 当包含我的一个页面时 它似乎会徘 徊并执行其中的 JavaScript 代码 即使它随后从 DOM 中删除 索引 html 正文 div d
  • 如何在 Chrome 中实现抓取光标图标?

    我知道可以在 Chrome 中使用抓取光标图标 当然是在 Gmail 中 但我不知道如何在我的代码中实现它 我已经尝试过 在CSS中 body cursor grab body cursor webkit grab body cursor
  • 代码点火器 JSON

    你好 我使用 codeigniter 然后我从控制器中的数据库中回显输出 然后在我的视图文件中执行以下操作 但它没有显示任何内容 S 我的模型文件 function forumList this gt db gt select oversk
  • React:React 如何确保在浏览器有机会绘制之后调用 useEffect?

    useLayoutEffect 的文档说 useLayoutEffect 内计划的更新将被刷新 在浏览器有机会绘制之前同步进行 useEffect 的文档说 与 componentDidMount 和 componentDidUpdate
  • 使用 easyXDM 调整 IFrame 大小

    我将 iFrame 代码提供给客户 以便他们可以显示我网站上的动态内容 我希望其页面上的 iFrame 能够调整大小以适合我的内容 我按照 easyXDM 网站的说明进行操作 但也许我遗漏了一些东西 我没有收到任何错误 但 iFrame 保
  • 如何获取数组中最后 5 个元素(不包括第一个元素)?

    在 JavaScript 数组中 如何获取最后 5 个元素 排除第一个元素 1 55 77 88 would return 55 77 88 添加其他示例 1 55 77 88 99 22 33 44 would return 88 99
  • Angular-Datatables + Angular-xeditable:取消可编辑行

    当组合 Angular DataTables 和 Angular XEditable 时 添加新行时会取消可编辑行 这是jsfiddle https jsfiddle net faj61h5d 10 示例操作如下 1 这是初始状态 2 将第
  • 使用 ES6 模块导出/导入单个类方法?

    假设我有一个像这样的简单课程fileA js class foo constructor x this name x fooMethod x return x hello 我想导入并使用fooMethod in fileB js像这样 im
  • MongoDB 嵌套数组查询

    我问这个作为评论another https stackoverflow com questions 5250652 query a nested array in mongodb问题 还发了一个question https groups g
  • Rxjs 可观察等待直到满足某些条件

    我有以下重试逻辑来重试操作 对于单个请求来说它工作得很好 对于多个正在进行的请求 我想在重试之前等待现有的重试逻辑完成 handleError errors Observable
  • 如何使用 selenium 获取 javascript 结果?

    我有以下代码 from selenium import selenium selenium selenium localhost 4444 chrome http some site com selenium start sel selen
  • 对数字和字母元素的数组进行排序(自然排序)

    假设我有一个数组 var arr 1 5 ahsldk 10 55 3 2 7 8 1 2 75 abc huds 我尝试对其进行排序 我得到了类似的东西 1 1 10 2 2 3 5 55 7 75 8 abc ahsldk huds 注
  • 全局定义的 AngularJS 控制器和封装

    根据 AngularJS 的教程 控制器函数仅位于全局范围内 http docs angularjs org tutorial step 04 http docs angularjs org tutorial step 04 控制器函数本身
  • 在外部单击时关闭弹出 div

    我有一个弹出 div 仅在单击特定按钮时显示 单击同一按钮时它甚至会隐藏 我的问题是 我还想在单击外部任何地方时隐藏 div 我无法这样做 因为弹出 div 位于主包装类内部 并且无法通过在包装类上使用 click 事件并使其隐藏来做到这一
  • IE6 丢失查询字符串

    我有一个使用 javascript 从查询字符串中获取值的页面window location 从网络服务器运行时效果很好 但如果我通过将其放在地址栏中使用 IE6 在本地运行它 c mysite index htm 网站创建的任何查询字符串
  • Tween JS 基础知识之三个 JS 立方体

    我是 Tween JS 的新手 尝试使用 Tween 制作一个向右移动的简单动画 下面是我在 init 函数中的代码 我使用的是三个 JS var geometry new THREE CylinderGeometry 200 200 20
  • 获取 2 个日期之间的月份名称

    我有两个约会from and to 我想获取这两个日期之间的所有月份名称 以下是我的代码 var monthNames January February March April May June July August September
  • 使用 JavaScript 从 URL 变量读取来加载不同的 CSS 样式表

    我试图在我的 WordPress 博客上使用两个不同的样式表 以便在通过 Web 访问页面时使用一个样式表 而在通过我们的 iOS 应用程序访问博客内容时使用另一个样式表 现在 我们将 app true 附加到来自 iOS 应用程序的 UR
  • 如何在 jQuery 中检查复选框是否被选中?

    我需要检查checked复选框的属性 并使用 jQuery 根据选中的属性执行操作 例如 如果age复选框被选中 然后我需要显示一个文本框来输入age 否则隐藏文本框 但下面的代码返回false默认情况下 if isAgeSelected
  • Javascript / jQuery - 转换特殊 html 字符

    我有一个pre元素中包含一些 html 代码 该代码中有特殊字符 例如 lt 所以它不会破坏页面 然后我有一个 javascript 函数 它获取此 pre 元素的内容 突出显示它 使用 codemirror 并用突出显示的文本替换元素内容

随机推荐