Node MongoDBネイティブドライバーを使用してNodejsとMongoDBを操作します。いくつかのドキュメントを取得し、変更を加えて、すぐに保存します。これは例です。
_db.open(function (err, db) {
db.collection('foo', function (err, collection) {
var cursor = collection.find({});
cursor.each(function (err, doc) {
if (doc != null) {
doc.newkey = 'foo'; // Make some changes
db.save(doc); // Update the document
} else {
db.close(); // Closing the connection
}
});
});
});
_
非同期の性質により、ドキュメントの更新プロセスに時間がかかる場合、カーソルがドキュメントの最後に達すると、データベース接続が閉じられます。すべての更新がデータベースに保存されるわけではありません。
db.close()
を省略すると、すべてのドキュメントは正しく更新されますが、アプリケーションはハングし、終了することはありません。
カウンターを使用して更新の数を追跡し、ゼロに戻ってからデータベースを閉じることを提案する投稿を見ました。しかし、私はここで何か間違っていますか?このような状況に対処する最善の方法は何ですか?リソースを解放するにはdb.close()
を使用する必要がありますか?または、新しいデータベース接続を開く必要がありますか?
カウントアプローチに基づいた潜在的なソリューションを次に示します(テストしていませんが、エラートラップはありませんが、アイデアを伝える必要があります)。
基本的な戦略は次のとおりです:更新が必要なレコード数のカウントを取得し、各レコードを非同期に保存し、成功時にコールバックを実行します。カウントが0に達すると(最後の更新が終了すると)、カウントをデクリメントしてDBを閉じます。 {safe:true}
を使用することにより、各更新が成功することを保証できます。
Mongoサーバーは接続ごとに1つのスレッドを使用するため、a)未使用の接続を閉じるか、b)それらをプール/再利用することをお勧めします。
db.open(function (err, db) {
db.collection('foo', function (err, collection) {
var cursor = collection.find({});
cursor.count(function(err,count)){
var savesPending = count;
if(count == 0){
db.close();
return;
}
var saveFinished = function(){
savesPending--;
if(savesPending == 0){
db.close();
}
}
cursor.each(function (err, doc) {
if (doc != null) {
doc.newkey = 'foo'; // Make some changes
db.save(doc, {safe:true}, saveFinished);
}
});
})
});
});
プールされた接続を使用し、アプリケーションの終了時にクリーンアップ関数でdb.close()を呼び出すことをお勧めします。
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html を参照してください
少し古いスレッドですが、とにかく。
カウンターの使用は単純なシナリオに適用される場合がありますが、複雑な状況では難しい場合があります。データベース接続がアイドル状態のときにデータベース接続を閉じることで私が思いついた解決策を次に示します。
var dbQueryCounter = 0;
var maxDbIdleTime = 5000; //maximum db idle time
var closeIdleDb = function(connection){
var previousCounter = 0;
var checker = setInterval(function(){
if (previousCounter == dbQueryCounter && dbQueryCounter != 0) {
connection.close();
clearInterval(closeIdleDb);
} else {
previousCounter = dbQueryCounter;
}
}, maxDbIdleTime);
};
MongoClient.connect("mongodb://127.0.0.1:27017/testdb", function(err, connection)(
if (err) throw err;
connection.collection("mycollection").find({'a':{'$gt':1}}).toArray(function(err, docs) {
dbQueryCounter ++;
});
//do any db query, and increase the dbQueryCounter
closeIdleDb(connection);
));
これは、すべてのデータベース接続の一般的なソリューションです。 maxDbIdleTimeは、dbクエリタイムアウトと同じ値以上に設定できます。
これはあまりエレガントではありませんが、これを行うより良い方法は考えられません。 NodeJsを使用してMongoDbとMysqlを照会するスクリプトを実行します。データベース接続が適切に閉じられないと、スクリプトはそこで永久にハングします。
上記の@mpobrienからの提案に基づいて、 async モジュールがこの点で非常に役立つことがわかりました。私が採用するようになったパターンの例を次に示します。
const assert = require('assert');
const async = require('async');
const MongoClient = require('mongodb').MongoClient;
var mongodb;
async.series(
[
// Establish Covalent Analytics MongoDB connection
(callback) => {
MongoClient.connect('mongodb://localhost:27017/test', (err, db) => {
assert.equal(err, null);
mongodb = db;
callback(null);
});
},
// Insert some documents
(callback) => {
mongodb.collection('sandbox').insertMany(
[{a : 1}, {a : 2}, {a : 3}],
(err) => {
assert.equal(err, null);
callback(null);
}
)
},
// Find some documents
(callback) => {
mongodb.collection('sandbox').find({}).toArray(function(err, docs) {
assert.equal(err, null);
console.dir(docs);
callback(null);
});
}
],
() => {
mongodb.close();
}
);
ここに私が思いついた解決策があります。 toArrayの使用を避け、非常に短くて便利です。
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:27017/mydb", function(err, db) {
let myCollection = db.collection('myCollection');
let query = {}; // fill in your query here
let i = 0;
myCollection.count(query, (err, count) => {
myCollection.find(query).forEach((doc) => {
// do stuff here
if (++i == count) db.close();
});
});
});
私はこのようなカウンターを含むソリューションを思いつきました。 count()呼び出しに依存せず、タイムアウトを待ちません。 each()内のすべてのドキュメントが使い果たされた後、dbを閉じます。
var mydb = {}; // initialize the helper object.
mydb.cnt = {}; // init counter to permit multiple db objects.
mydb.open = function(db) // call open to inc the counter.
{
if( !mydb.cnt[db.tag] ) mydb.cnt[db.tag] = 1;
else mydb.cnt[db.tag]++;
};
mydb.close = function(db) // close the db when the cnt reaches 0.
{
mydb.cnt[db.tag]--;
if ( mydb.cnt[db.tag] <= 0 ) {
delete mydb.cnt[db.tag];
return db.close();
}
return null;
};
したがって、db.each()やdb.save()などの呼び出しを行うたびに、これらのメソッドを使用して、作業中にdbの準備が完了し、完了時に閉じるようにします。
OPの例:
foo = db.collection('foo');
mydb.open(db); // *** Add here to init the counter.**
foo.find({},function(err,cursor)
{
if( err ) throw err;
cursor.each(function (err, doc)
{
if( err ) throw err;
if (doc != null) {
doc.newkey = 'foo';
mydb.open(db); // *** Add here to prevent from closing prematurely **
foo.save(doc, function(err,count) {
if( err ) throw err;
mydb.close(db); // *** Add here to close when done. **
});
} else {
mydb.close(db); // *** Close like this instead. **
}
});
});
ここで、これは、それぞれからの最後から2番目のコールバックがmydb.open()を経由してから、それぞれからの最後のコールバックがmydb.close()....に行くことを前提としているので、もちろん、これが問題。
そのため、db呼び出しの前にmydb.open(db)を置き、コールバックの戻り点または呼び出しの種類に応じてdb呼び出しの後にmydb.close(db)を置きます。
この種のカウンタはdbオブジェクト内で維持する必要があるように思えますが、これは私の現在の回避策です。たぶん、コンストラクタでdbを受け取る新しいオブジェクトを作成し、mongodb関数をラップして、より適切に処理できます。