Когда закрыть соединение с базой данных MongoDB в Nodejs
работа с Nodejs и MongoDB через собственный драйвер узла 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() должны быть использованы для освобождения ресурсов? Или нужно открыть новое соединение с БД?
6 ответов:
вот потенциальное решение, основанное на подходе подсчета (я его не тестировал, и нет захвата ошибок, но он должен передать идею).
основная стратегия: получить количество записей, которые должны быть обновлены, сохранить каждую запись асинхронно и обратный вызов на успех, который уменьшит количество и закрыть БД, если количество достигает 0 (когда последнее обновление заканчивается). С помощью
{safe:true}мы можем гарантировать, что каждое обновление проходит успешно.в сервер mongo будет использовать один поток на соединение, поэтому хорошо либо 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.закрыть() в функции очистки в конце срока службы вашего приложения:
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 может быть установлено как то же значение, что и тайм-аут запроса БД или больше.
Это не очень элегантно, но я не могу придумать лучшего способа сделать это. Я использую NodeJs для запуска скрипта, который запросы MongoDb и Mysql, и скрипт зависает там навсегда, если соединения с базой данных не закрыты должным образом.
основываясь на предложении от @mpobrien выше, я нашел асинхронные модуль, чтобы быть невероятно полезны в этом отношении. Вот пример шаблона, который я пришел принять:
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 () и не ждет тайм-аута. Он закроет БД после того, как все документы в каждом() будут исчерпаны.
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.каждый() или DB.save () вы бы использовали эти методы, чтобы убедиться, что БД готова во время работы и закрыта, когда это будет сделано.
пример из 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. ** } }); });теперь это предполагает, что второй для последнего обратного вызова от каждого делает это через mydb.open () перед последним обратным вызовом от каждого идет в mydb.закрывать.)(... поэтому, конечно, дайте мне знать, если это проблема.
Итак: поставьте mydb.откройте (db) перед вызовом db и поместите mydb.закрыть (db) в точке возврата обратного вызова или после вызова db (в зависимости от типа вызова).
мне кажется, что этот вид счетчика должен поддерживаться в объекте db, но это мой текущий обходной путь. Может быть, мы могли бы создать новую объект, который принимает БД в конструкторе и обернуть функции mongodb для обработки закрыть лучше.
Comments