When to close MongoDB database connection in Nodejs

前端 未结 8 602
一整个雨季
一整个雨季 2020-11-28 21:56

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:

相关标签:
8条回答
  • 2020-11-28 22:12

    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.

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

    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);
            }
          });
        })
      });
    });
    
    0 讨论(0)
  • 2020-11-28 22:17

    Here an extended example to the answer given by pkopac, since I had to figure out the rest of the details:

    const client = new MongoClient(uri);
    (async () => await client.connect())();
    
    // use client to work with db
    const find = async (dbName, collectionName) => {
      try {
        const collection = client.db(dbName).collection(collectionName);
        const result = await collection.find().toArray()
        return result;
      } catch (err) {
        console.error(err);
      }
    }
    
    const cleanup = (event) => { // SIGINT is sent for example when you Ctrl+C a running process from the command line.
      client.close(); // Close MongodDB Connection when Process ends
      process.exit(); // Exit with default success-code '0'.
    }
    
    process.on('SIGINT', cleanup);
    process.on('SIGTERM', cleanup);
    

    Here is a link to the difference between SIGINT and SIGTERM. I had to add the process.exit(), otherwise my node web-server didn't exit cleanly when doing Ctrl + C on the running process in command line.

    0 讨论(0)
  • 2020-11-28 22:18

    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.

    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.

    0 讨论(0)
  • 2020-11-28 22:19

    Modern way of doing this without counters, libraries or any custom code:

    let MongoClient = require('mongodb').MongoClient;
    let url = 'mongodb://yourMongoDBUrl';
    let database = 'dbName';
    let collection = 'collectionName';
    
    MongoClient.connect(url, { useNewUrlParser: true }, (mongoError, mongoClient) => {
       if (mongoError) throw mongoError;
    
       // query as an async stream
       let stream = mongoClient.db(database).collection(collection)
            .find({}) // your query goes here
            .stream({
              transform: (readElement) => {
                // here you can transform each element before processing it
                return readElement;
              }
            });
    
       // process each element of stream (async)
       stream.on('data', (streamElement) => {
            // here you process the data
            console.log('single element processed', streamElement);
       });
    
       // called only when stream has no pending elements to process
       stream.once('end', () => {
         mongoClient.close().then(r => console.log('db successfully closed'));
       });
    });
    

    Tested it on version 3.2.7 of mongodb driver but according to link might be valid since version 2.0

    0 讨论(0)
  • 2020-11-28 22:23

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

    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();
        });
      });
    });
    
    0 讨论(0)
  • 2020-11-28 22:29

    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:

    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 讨论(0)
提交回复
热议问题