Node.js / Mongoose上的“VersionError:找不到匹配的文档”错误

时间:2013-07-06 02:29:39

标签: node.js mongodb mongoose

我对Node.js和Mongo / Mongoose比较陌生,而且我在解决特定的Mongoose错误时遇到了非常的困难:

VersionError:找不到匹配的文档。

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

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

(TL; DR - “Mongoose v3现在为每个文档添加了一个模式可配置的版本密钥。只要对数组的修改可能会更改任何数组的元素位置,该值就会以原子方式递增。”如果您尝试保存一个文档,但版本密钥不再与您检索到的对象匹配,您可以获得上述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)

6 个答案:

答案 0 :(得分:34)

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

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

不正确的操作顺序:

  1. 收到客户请求
  2. 文件已锁定
  3. 检索到的文件
  4. 文档已编辑
  5. 文件已解锁
  6. 客户请求已解决
  7. 保存的文件
  8. 一旦你看到它写出来,问题就很明显了:我们将文件保存在文件锁之外。

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

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

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

    正确/固定的运营顺序

    1. 收到客户请求
    2. 文件已锁定
    3. 检索到的文件
    4. 文档已编辑
    5. 保存的文件
    6. 文件已解锁
    7. 客户请求已解决
    8. (您可以提出一个论点,即应该交换#6和#7,但这超出了Mongo / Mongoose /这个问题的范围。)

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

答案 1 :(得分:10)

它可以指出与robertklep指出的同时保存同一份文件。

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

答案 2 :(得分:7)

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

答案 3 :(得分:0)

我在NextJS / Express应用程序中遇到此问题,但在阅读this Mongoose discussion之后,我发现我可以通过从文档中删除__v属性来解决问题我我试图保存:

const fixedDocument = _.pickBy(originalDocument, (val, key) => key !== '__v');

答案 4 :(得分:0)

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

我固定将mongoose中的splice函数替换为pull函数。

答案 5 :(得分:0)

当我尝试将用户的参考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));