Когда закрывать соединение с базой данных MongoDB в Nodejs - PullRequest
68 голосов
/ 04 декабря 2011

Работа с Nodejs и MongoDB через собственный драйвер Node 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 ]

24 голосов
/ 07 декабря 2011

Вот потенциальное решение, основанное на подходе подсчета (я не проверял его, и нет отслеживания ошибок, но он должен передать идею).

Основная стратегия такова: Получить количествозаписи должны быть обновлены, сохранять каждую запись асинхронно и при успешном завершении обратного вызова, который уменьшит счетчик и закроет БД, если счет достигнет 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);
        }
      });
    })
  });
});
14 голосов
/ 27 декабря 2014

Лучше всего использовать соединение из пула, а затем вызвать db.close () в функции очистки в конце срока службы вашего приложения:

process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);

См. http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html

Немногостарая нить, но все равно.

4 голосов
/ 25 августа 2013

Я обнаружил, что использование счетчика может применяться к простому сценарию, но может быть сложным в сложных ситуациях. Вот решение, которое я приду, закрыв соединение с базой данных, когда соединение с базой данных простаивает:

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 может быть установлено то же значение, что и для тайм-аута запроса базы данных, или больше.

Это не очень элегантно, но я не могу придумать лучшего способа сделать это. Я использую NodeJ для запуска сценария, который запрашивает MongoDb и Mysql, и сценарий там навсегда зависает, если соединения с базой данных не закрыты должным образом.

1 голос
/ 02 июля 2017

Вот решение, которое я придумал.Он избегает использования 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();
    });
  });
});
1 голос
/ 27 января 2017

Основываясь на предложении @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();
    }
);
0 голосов
/ 03 февраля 2014

Я придумал решение, которое включает в себя счетчик, подобный этому.Он не зависит от вызова 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.each () или 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.close () .... так что, конечно, дайте мне знать, если это проблема.

Итак: поместите mydb.open (db) перед вызовом db и поставьте mydb.close (db)в точке возврата обратного вызова или после вызова db (в зависимости от типа вызова).

Мне кажется, что этот тип счетчика должен поддерживаться в объекте db, но это мой текущий обходной путь.Возможно, мы могли бы создать новый объект, который принимает в конструктор дб и оборачивать функции mongodb для лучшей обработки закрытия.

...