何时关闭Nodejs中的MongoDB数据库连接

时间:2022-05-28 12:23:49

Working with Nodejs and MongoDB through Node MongoDB native driver. Need to retrieve some documents, and make modification, then save them right back. This is an example:

通过节点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
      }
    });
  });
});

With asynchronous nature, if the process of updating the document takes longer, then when cursor reaches the end of documents, database connection is closed. Not all updates are saved to the database.

使用异步特性,如果更新文档的过程需要更长的时间,那么当游标到达文档的末尾时,就关闭数据库连接。并不是所有的更新都保存到数据库中。

If the db.close() is omitted, all the documents are correctly updated, but the application hangs, never exits.

如果省略db.close(),所有文档都将被正确更新,但是应用程序挂起,永远不会退出。

I saw a post suggesting using a counter to track number of updates, when fall back to zero, then close the db. But am I doing anything wrong here? What is the best way to handle this kind of situation? Does db.close() have to be used to free up resource? Or does a new db connection needs to open?

我看到一篇文章建议使用计数器跟踪更新的数量,当返回到0时,然后关闭db。但是我做错什么了吗?处理这种情况最好的办法是什么?关闭()是否必须用于释放资源?还是需要打开一个新的db连接?

6 个解决方案

#1


24  

Here's a potential solution based on the counting approach (I haven't tested it and there's no error trapping, but it should convey the idea).

这里有一个基于计数方法的潜在解决方案(我还没有对它进行测试,也没有错误捕获,但是它应该传达了这个想法)。

The basic strategy is: Acquire the count of how many records need to be updated, save each record asynchronously and a callback on success, which will decrement the count and close the DB if the count reaches 0 (when the last update finishes). By using {safe:true} we can ensure that each update is successful.

基本策略是:获取需要更新多少条记录的计数,异步保存每条记录,并在成功时保存一个回调,如果计数达到0(最后一次更新结束时),这将减少计数并关闭DB。通过使用{safe:true},我们可以确保每个更新都是成功的。

The mongo server will use one thread per connection, so it's good to either a) close unused connections, or b) pool/reuse them.

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);
        }
      });
    })
  });
});

#2


13  

It's best to use a pooled connection and then call db.close() in cleanup function at the end of your application's life:

最好使用一个合并的连接,然后在应用程序生命结束时调用db.close()中的清理函数:

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

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

参见http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html

A bit old thread, but anyway.

有点旧的线,但无论如何。

#3


4  

I found that using counter may apply to simple scenario, but may be hard in complicated situations. Here is a solution that I come up by closing the database connection when database connection is idle:

我发现使用计数器可能适用于简单的场景,但在复杂的情况下可能很难。这里有一个解决方案,当数据库连接处于空闲状态时关闭数据库连接:

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);
));

This can be a general solution for any database Connections. maxDbIdleTime can be set as the same value as db query timeout or longer.

这可以是任何数据库连接的通用解决方案。maxDbIdleTime可以设置为与db查询超时相同的值或更长的值。

This is not very elegant, but I can't think of a better way to do this. I use NodeJs to run a script that queries MongoDb and Mysql, and the script hangs there forever if the database connections are not closed properly.

这不是很优雅,但我想不出更好的方法来做这件事。我使用node . js来运行查询MongoDb和Mysql的脚本,如果数据库连接没有正确关闭,脚本将永远挂在那里。

#4


1  

Based on the suggestion from @mpobrien above, I've found the async module to be incredibly helpful in this regard. Here's an example pattern that I've come to adopt:

根据上面@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();
    }
);

#5


1  

Here's a solution I came up with. It avoids using toArray and it's pretty short and sweet:

我想到了一个解决办法。它避免使用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();
    });
  });
});

#6


0  

I came up with a solution that involves a counter like this. It does not depend on a count() call nor does it wait for a time out. It will close the db after all the documents in each() are exhausted.

我想出了一个解决方案,包括这样一个计数器。它不依赖count()调用,也不等待超时。在耗尽每个()中的所有文档之后,它将关闭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;
};

So that each time you are going to make a call like db.each() or db.save() you would use these methods to ensure the db is ready while working and closed when done.

因此,每当您要发出像db.each()或db.save()这样的调用时,您将使用这些方法来确保数据库在工作时已经就绪,并在完成时关闭。

Example from OP:

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. **
    }
  });
});

Now, this assumes that the second to last callback from each makes it through the mydb.open() before the last callback from each goes to mydb.close().... so, of course, let me know if this is an issue.

现在,我们假设每个使它的倒数第二回调通过mydb.open()从每个→mydb.close之前最后一个回调()....当然,如果这是一个问题,请告诉我。

So: put a mydb.open(db) before a db call and put a mydb.close(db) at the return point of the callback or after the db call (depending on the call type).

因此:在db调用之前放置一个mydb.open(db),并在回调的返回点或db调用之后(取决于调用类型)放置一个mydb.close(db)。

Seems to me that this kind of counter should be maintained within the db object but this is my current workaround. Maybe we could create a new object that takes a db in the constructor and wrap the mongodb functions to handle the close better.

在我看来,这种计数器应该在db对象中维护,但这是我目前的解决方案。也许我们可以创建一个新对象,它在构造函数中接受一个db并封装mongodb函数,以便更好地处理close。

#1


24  

Here's a potential solution based on the counting approach (I haven't tested it and there's no error trapping, but it should convey the idea).

这里有一个基于计数方法的潜在解决方案(我还没有对它进行测试,也没有错误捕获,但是它应该传达了这个想法)。

The basic strategy is: Acquire the count of how many records need to be updated, save each record asynchronously and a callback on success, which will decrement the count and close the DB if the count reaches 0 (when the last update finishes). By using {safe:true} we can ensure that each update is successful.

基本策略是:获取需要更新多少条记录的计数,异步保存每条记录,并在成功时保存一个回调,如果计数达到0(最后一次更新结束时),这将减少计数并关闭DB。通过使用{safe:true},我们可以确保每个更新都是成功的。

The mongo server will use one thread per connection, so it's good to either a) close unused connections, or b) pool/reuse them.

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);
        }
      });
    })
  });
});

#2


13  

It's best to use a pooled connection and then call db.close() in cleanup function at the end of your application's life:

最好使用一个合并的连接,然后在应用程序生命结束时调用db.close()中的清理函数:

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

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

参见http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html

A bit old thread, but anyway.

有点旧的线,但无论如何。

#3


4  

I found that using counter may apply to simple scenario, but may be hard in complicated situations. Here is a solution that I come up by closing the database connection when database connection is idle:

我发现使用计数器可能适用于简单的场景,但在复杂的情况下可能很难。这里有一个解决方案,当数据库连接处于空闲状态时关闭数据库连接:

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);
));

This can be a general solution for any database Connections. maxDbIdleTime can be set as the same value as db query timeout or longer.

这可以是任何数据库连接的通用解决方案。maxDbIdleTime可以设置为与db查询超时相同的值或更长的值。

This is not very elegant, but I can't think of a better way to do this. I use NodeJs to run a script that queries MongoDb and Mysql, and the script hangs there forever if the database connections are not closed properly.

这不是很优雅,但我想不出更好的方法来做这件事。我使用node . js来运行查询MongoDb和Mysql的脚本,如果数据库连接没有正确关闭,脚本将永远挂在那里。

#4


1  

Based on the suggestion from @mpobrien above, I've found the async module to be incredibly helpful in this regard. Here's an example pattern that I've come to adopt:

根据上面@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();
    }
);

#5


1  

Here's a solution I came up with. It avoids using toArray and it's pretty short and sweet:

我想到了一个解决办法。它避免使用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();
    });
  });
});

#6


0  

I came up with a solution that involves a counter like this. It does not depend on a count() call nor does it wait for a time out. It will close the db after all the documents in each() are exhausted.

我想出了一个解决方案,包括这样一个计数器。它不依赖count()调用,也不等待超时。在耗尽每个()中的所有文档之后,它将关闭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;
};

So that each time you are going to make a call like db.each() or db.save() you would use these methods to ensure the db is ready while working and closed when done.

因此,每当您要发出像db.each()或db.save()这样的调用时,您将使用这些方法来确保数据库在工作时已经就绪,并在完成时关闭。

Example from OP:

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. **
    }
  });
});

Now, this assumes that the second to last callback from each makes it through the mydb.open() before the last callback from each goes to mydb.close().... so, of course, let me know if this is an issue.

现在,我们假设每个使它的倒数第二回调通过mydb.open()从每个→mydb.close之前最后一个回调()....当然,如果这是一个问题,请告诉我。

So: put a mydb.open(db) before a db call and put a mydb.close(db) at the return point of the callback or after the db call (depending on the call type).

因此:在db调用之前放置一个mydb.open(db),并在回调的返回点或db调用之后(取决于调用类型)放置一个mydb.close(db)。

Seems to me that this kind of counter should be maintained within the db object but this is my current workaround. Maybe we could create a new object that takes a db in the constructor and wrap the mongodb functions to handle the close better.

在我看来,这种计数器应该在db对象中维护,但这是我目前的解决方案。也许我们可以创建一个新对象,它在构造函数中接受一个db并封装mongodb函数,以便更好地处理close。