概述
我试图了解在使用 Node.js 时使用模型实例时如何确保异步安全。在这里,我在代码示例中使用了 Mongoose ODM,但这个问题适用于将数据库与 Node.js 采用的异步事件驱动 I/O 方法一起使用的任何情况。
考虑以下代码(使用 Mongoose 进行 MongoDB 查询):
片段 A
MyModel.findOne( { _id : <id #1> }, function( err, doc ) {
MyOtherModel.findOne( { _id : someOtherId }, ( function(err, otherDoc ) {
if (doc.field1 === otherDoc.otherField) {
doc.field2 = 0; // assign some new value to a field on the model
}
doc.save( function() { console.log( 'success' ); }
});
});
在应用程序的单独部分中,可以更新 MyModel 描述的文档。考虑以下代码:
片段 B
MyModel.update( { _id : <id #1> }, { $set : { field1 : someValue }, callback );
在 Snippet A 中,发出一个 MongoDB 查询,并在文档准备好后触发一个已注册的回调。MyModel 描述的文档实例保留在内存中(在“doc”对象中)。可能会出现以下顺序:
- 片段 A执行
- 为 MyModel 发起一个查询,注册一个回调(回调 A)供以后使用
- << Node 事件循环运行 >>
- 从数据库中检索MyModel,执行注册的回调(回调A)
- 为 MyOtherModel 发起一个查询,注册一个回调供以后使用(回调 B)
- << Node 事件循环运行 >>
- 片段 B执行
- 文档 (id #1) 已更新
- << Node 事件循环运行 >>
- MyOtherModel 从数据库中获取,执行注册的回调(callback B)
- 在比较中错误地使用了文档的旧版本(id #1)。
问题
- 是否有任何保证不会在 Node.js/MongoDB 中发生这种类型的竞争条件?
- 我能做些什么来确定性地防止这种情况发生?
虽然 Node 以单线程方式运行代码,但在我看来,任何允许运行事件循环的行为都会为潜在的陈旧数据打开大门。如果这种遵守是错误的,请纠正我。