41

我对 Node.js 和 Mongo/Mongoose 比较陌生,而且我很难解决特定的 Mongoose 错误:

VersionError:找不到匹配的文档。

(这个问题底部的整个错误跟踪/堆栈。)

这篇博文非常清楚地概述了 VersionError 是如何发生的:

(TL;DR - “Mongoose v3 现在为每个文档添加了一个模式可配置的版本键。只要对数组的修改可能会更改任何数组的元素位置,这个值就会自动递增。”如果您尝试保存文档,但版本key 不再匹配你检索到的对象,你得到上面的VersionError。)

核心问题:有没有办法显示违规save()操作?或者哪个文件保存失败?还是什么都没有?!;)

挑战:这是一个包含许多数组的相对较大的代码库,我不确定如何开始解决问题。特别是,错误跟踪/堆栈似乎没有显示问题存在的位置。见下文:

VersionError: No matching document found.
at handleSave (<project_path>/node_modules/mongoose/lib/model.js:121:23)
at exports.tick (<project_path>/node_modules/mongoose/lib/utils.js:408:16)
at null.<anonymous> (<project_path>/node_modules/mongoose/node_modules/mongodb/lib/mongodb/collection.js:484:9)
at g (events.js:192:14)
at EventEmitter.emit (events.js:126:20)
at Server.Base._callHandler (<project_path>/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js:391:25)
at Server.connect.connectionPool.on.server._serverState (<project_path>/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:558:20)
at MongoReply.parseBody (<project_path>/node_modules/mongoose/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js:131:5)
at Server.connect.connectionPool.on.server._serverState (<project_path>/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:517:22)
at EventEmitter.emit (events.js:96:17)
4

5 回答 5

36

根据要求,以下是我们问题的概述,以及我们如何解决它:

在我们的系统中,我们创建了一个自定义文档锁定例程(使用 redis-lock),其中以这个精确(不正确)的顺序发生了以下情况:

不正确的操作顺序:

  1. 收到客户请求
  2. 文档锁定
  3. 检索到的文档
  4. 文件已编辑
  5. 文件解锁
  6. 客户请求已解决
  7. 已保存文档

一旦你看到它被写出来,问题就很明显了:我们将文档保存在文档锁之外。

假设#6 在我们的系统中需要 100 毫秒。那是一个 100 毫秒的窗口,如果任何其他请求获取相同的文档,我们将发生保存冲突(这个问题中的标题错误基本上是保存冲突恕我直言)。

换句话说/示例:在我们的系统中,请求 A 获取了文档 X 的版本 1,对其进行了编辑,然后将其解锁,但在请求 A 保存文档之前,请求 B 获取了文档 X 并将其增加到版本 2(在 Mongo 上阅读有关此的更多信息的版本)。然后请求 A 解析其客户端请求并保存文档 X,但它正在尝试保存版本 1,现在它看到它具有版本 2,因此出现上述错误。

所以修复很容易。将您的文件保存在您的锁内。(在上面的例子中,将#7 移动到#5 之前。见下文。)

正确/固定的操作顺序

  1. 收到客户请求
  2. 文档锁定
  3. 检索到的文档
  4. 文件已编辑
  5. 已保存文档
  6. 文件解锁
  7. 客户请求已解决

(您可以提出应该交换#6和#7的论点,但这超出了Mongo/Mongoose/this question的范围。)

我将暂时不回答这个问题,看看是否有人可以阐明一种更好的方法来隔离相关代码并解决此问题。在我们的案例中,这是一个非常系统性的问题,并且对于我们当时的技能水平进行故障排除非常具有挑战性。

于 2013-09-16T22:41:33.990 回答
11

正如 robertklep 指出的那样,它可能指向同时保存相同的文档。

我们在使用 async.parallel 在同一个文档上运行并发保存时遇到了类似的问题。

于 2013-09-05T11:32:42.790 回答
8

当您的进程在内存中维护文档的过时版本,然后在另一个进程更新后的某个时间点尝试保存它时,也会发生此错误。

于 2016-06-23T06:06:39.093 回答
1

当我尝试将用户的参考 ID 更新为电子邮件时,我遇到了同样的错误。使用 async / await 修复非常简单!这是代码片段,希望对您有所帮助。

email
    .save()
    .then(() =>
      User.findById(email.from).then(async sender => { // declare function as async
        sender.emails.sent.push(email._id);
        await sender.save(); // wait for save() to complete before proceeding
      }).catch((err) => console.log(err))
    )
    .then(() =>
      User.findById(email.to).then(async receiver => { // same as above
        receiver.emails.received.push(email._id);
        await receiver.save(); // same as above
      }).catch((err) => console.log(err))
    )
    .then(() => res.status(200).json({ message: successMessage }))
    .catch(err => console.log(err));
于 2019-04-14T14:21:56.057 回答
0

splice我遇到了这个问题,因为我正在使用函数从 Document 数组中删除一个项目。

我用mongoose的函数修复了替换splice函数。pull

于 2019-02-23T19:22:51.317 回答