web-dev-qa-db-ja.com

NodejsでMongoDBデータベース接続を閉じるタイミング

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()を使用する必要がありますか?または、新しいデータベース接続を開く必要がありますか?

68
realguess

カウントアプローチに基づいた潜在的なソリューションを次に示します(テストしていませんが、エラートラップはありませんが、アイデアを伝える必要があります)。

基本的な戦略は次のとおりです:更新が必要なレコード数のカウントを取得し、各レコードを非同期に保存し、成功時にコールバックを実行します。カウントが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);
        }
      });
    })
  });
});
24
mpobrien

プールされた接続を使用し、アプリケーションの終了時にクリーンアップ関数でdb.close()を呼び出すことをお勧めします。

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

http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html を参照してください

少し古いスレッドですが、とにかく。

14
pkopac

カウンターの使用は単純なシナリオに適用される場合がありますが、複雑な状況では難しい場合があります。データベース接続がアイドル状態のときにデータベース接続を閉じることで私が思いついた解決策を次に示します。

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を照会するスクリプトを実行します。データベース接続が適切に閉じられないと、スクリプトはそこで永久にハングします。

5
cl yu

上記の@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();
    }
);
1
Andrew Kirk

ここに私が思いついた解決策があります。 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
Alan

私はこのようなカウンターを含むソリューションを思いつきました。 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関数をラップして、より適切に処理できます。

0
JJ Stiff