2013-07-06 47 views
37

我对Node.js和Mongo/Mongoose比较陌生,而且我有非常难排除特定Mongoose错误的难度:“VersionError:No matching document found”Node.js/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) 
+5

也许打开调试([docs](http://mongoosejs.com/docs/api.html#index_Mongoose-set))可能会帮助您追踪此问题。我最近有同样的问题,事实证明,我同时保存了同一个文档实例两次。 – robertklep

回答

33

每请求,这里是我们的问题的轮廓,以及我们如何解决它:

在我们的系统中,我们创建了一个自定义文档锁定程序(使用Redis的锁),其中,按照这种精确的事(不正确的)顺序:业务

不正确的顺序:

  1. 客户端请求中接收
  2. 文献锁定
  3. 文献检索
  4. 文档编辑
  5. 文件解锁
  6. 客户端请求解决
  7. 文档保存

一旦你看到它写出来,问题是显而易见的:我们被拯救我们文档锁外的文档。

假设#6在我们的系统中需要100ms。这是一个100ms窗口,其中如果有其他请求抓取同一个文档,我们将会有一个保存冲突(这个问题中的标题错误基本上是一个保存冲突恕我直言)。例如:在我们的系统中,请求A获取了文档X的版本1,对其进行了编辑,然后将其解锁,但在请求A保存文档之前,请求B抓取文档X并将其增加到版本2(其中,请阅读Mongo版本以获得更多关于此的信息)。然后,请求A解析其客户端请求并保存文档X,但它试图保存版本1,现在它认为它具有版本2,并因此出现上述错误。

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

CORRECT/FIXED OPERATIONS的高阶

  1. 客户端请求中接收
  2. 文献锁上
  3. 文献检索
  4. 已编辑文档
  5. 已保存文档
  6. 文件解锁
  7. 客户端请求解决

(你能有这样的#6,#7,应换一种说法,那不过是蒙戈/猫鼬/这个问题的范围之外。)

我将暂时搁置这个问题一段时间,并且看看是否有人能够更好地了解隔离相关代码并解决此问题的方法。在我们的情况下,这是一个非常系统的问题,对于我们当时的技能水平进行故障排除非常具有挑战性。

+2

我一直在处理类似的问题在这里和那里我自己。解决很多问题的一件事就是确保一切。save()有回调并确保保存是连续的。 'item.save(function(){item.save([...]);})'我也在努力捕捉错误并重新设置/重新保存文档post-error([有点沿着这些行] ://gist.github.com/droppedonjapan/5483a81917ea37ef381b))。仍然试图找出不仅捕捉错误的具体细节,而且还要弄清旧的文档。 –

+1

即使当我的save()调用不是并发时,我也有这个错误。相关:https://github.com/Automattic/mongoose/issues/1844和http://stackoverflow.com/questions/20723474/version-error-on-saving-mongoose-docs – Aaron

+0

@toblerpwn我们有同样的问题在我们的应用中。你认为使用redis-lock是一个强有力的解决方案吗?这是我们正在考虑的选项之一。 – PunDefeated

10

它有意地指向同时保存相同的文件,正如robertklep指出的那样。

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

+1

确实;我认为这基本上是一个特定于我们环境的问题,我只有通过丰富的'console.log'才能找到它。我认为关键的问题仍然存在(我将编辑+突出显示更好):有没有办法直接记录失败的保存从Mongoose?这将有助于未来的搜索者。 :) – toblerpwn

+1

@toblerpwn - 你能否详细说明你的想法是什么造成的? – UpTheCreek

+1

@UpTheCreek - 绝对。我会为此创建另一个答案。支持。 – toblerpwn

5

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

0

我在NextJS /快速应用程序有这个问题,但阅读this Mongoose discussion后,我发现我可以只解决问题从文档我试图挽救去除__v财产

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