Node.js/Mongoose 上的“版本错误:未找到匹配的文档”错误

Posted

技术标签:

【中文标题】Node.js/Mongoose 上的“版本错误:未找到匹配的文档”错误【英文标题】:"VersionError: No matching document found" error on Node.js/Mongoose 【发布时间】:2013-07-04 03:49:22 【问题描述】:

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

版本错误:找不到匹配的文档。

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

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

http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html

(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)

【问题讨论】:

也许打开调试 (docs) 可能会帮助您找到这个问题。我最近遇到了同样的问题,结果我同时保存了同一个文档实例两次。 【参考方案1】:

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

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

【讨论】:

确实;我认为这基本上是我们环境特有的问题,我只是通过大量的console.loging 才发现的。我认为关键问题仍然存在(我将更好地编辑 + 突出显示):有没有办法直接从 Mongoose 中记录失败的保存?这将有助于未来的搜索者。 :) @toblerpwn - 你能详细说明你认为造成这种情况的原因吗? @UpTheCreek - 绝对。我将为此创建另一个答案。待命。 我还站着...比以往任何时候都好...看起来像一个真正的幸存者...感觉像个小孩【参考方案2】:

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

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

操作顺序不正确:

    收到客户端请求 文档已锁定 检索到的文档 文档已编辑 文档已解锁 客户端请求已解决 文档已保存

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

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

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

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

正确/固定的操作顺序

    收到客户端请求 文档已锁定 检索到的文档 文档已编辑 文档已保存 文档已解锁 客户端请求已解决

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

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

【讨论】:

我自己一直在处理类似的问题。解决很多问题的一件事是确保所有 .save() 都有回调并确保保存是连续的。 item.save(function() item.save( [...] ); ) 我也在努力捕捉错误并在错误后重新获取/重新保存文档 (somewhat along these lines)。仍在尝试找出不仅要捕获错误的细节,还要找出旧文档的细节。 即使我的 save() 调用不是并发的,我也会遇到此错误。相关:github.com/Automattic/mongoose/issues/1844 和 ***.com/questions/20723474/… @toblerpwn 我们在我们的应用程序中遇到了同样的问题。你认为使用 redis-lock 是一个强大的解决方案吗?这是我们正在考虑的选项之一。 redis-lock 对我们来说已经好几年了,是的,尤其是当必须连续和一致地编辑多个文档时。但是,我会验证您的用例是否确实需要锁;我相信使用 WiredTiger(可能之前),MongoDB 在许多情况下都内置了良好的文档级锁定。但不是所有的。我们将redis-lock 用于边缘情况。【参考方案3】:

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

【讨论】:

【参考方案4】:

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

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

【讨论】:

【参考方案5】:

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

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));

【讨论】:

以上是关于Node.js/Mongoose 上的“版本错误:未找到匹配的文档”错误的主要内容,如果未能解决你的问题,请参考以下文章

??node.js????Mongoose??????

??? MongoDB ??? Node.js ????????? Mongoose ??

Node.JS + Mongoose 回调地狱

Node.js + mongoose [RangeError: 超出最大调用堆栈大小]

Node.js / Mongoose / 按顺序排序和显示

Node.js/mongoose - 数组中的子文档不会删除/移除