概要
Node.js を使用するときにモデルのインスタンスを使用するときに非同期の安全性を確保する方法を理解しようとしています。ここでは、コード サンプルで Mongoose ODM を使用していますが、この問題は、Node.js が採用している非同期イベント ドリブン I/O アプローチでデータベースが使用されている場合に当てはまります。
次のコードを検討してください (MongoDB クエリに Mongoose を使用しています)。
スニペット 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 );
スニペット A では、登録されたコールバックを使用して MongoDB クエリが発行され、ドキュメントの準備が整うと起動されます。MyModel によって記述されたドキュメントのインスタンスは、メモリ ("doc" オブジェクト内) に保持されます。次のシーケンスが発生する可能性があります。
- スニペット Aが実行されます
- MyModel に対してクエリが開始され、後で使用するためにコールバック (コールバック A) が登録されます。
- << ノード イベント ループが実行されます >>
- MyModel がデータベースから取得され、登録されたコールバック (コールバック A) が実行されます。
- MyOtherModel のクエリが開始され、後で使用するためにコールバックが登録されます (コールバック B)
- << ノード イベント ループが実行されます >>
- スニペット Bが実行される
- ドキュメント (id #1) が更新されました
- << ノード イベント ループが実行されます >>
- MyOtherModel がデータベースから取得され、登録されたコールバック (コールバック B) が実行されます。
- 古いバージョンのドキュメント (id #1) が比較で誤って使用されています。
質問
- この種の競合状態が Node.js/MongoDB で発生しないという保証はありますか?
- このシナリオの発生を確定的に防ぐにはどうすればよいですか?
Node はシングルスレッド方式でコードを実行しますが、イベント ループの実行を許可すると、古いデータの可能性があるように思えます。この遵守が間違っている場合は、私を修正してください。