88 votes

Quand fermer la connexion à la base de données MongoDB dans Nodejs

Travailler avec Nodejs et MongoDB grâce au pilote natif de Node MongoDB. J'ai besoin de récupérer des documents, de les modifier, puis de les sauvegarder. Voici un exemple :

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

Avec la nature asynchrone, si le processus de mise à jour du document prend plus de temps, alors lorsque le curseur atteint la fin des documents, la connexion à la base de données est fermée. Toutes les mises à jour ne sont pas enregistrées dans la base de données.

Si le db.close() est omis, tous les documents sont correctement mis à jour, mais l'application se bloque, sans jamais sortir.

J'ai vu un post suggérant d'utiliser un compteur pour suivre le nombre de mises à jour, quand il retombe à zéro, alors fermez le db. Mais est-ce que je fais quelque chose de mal ici ? Quelle est la meilleure façon de gérer ce genre de situation ? Est-ce que db.close() doivent être utilisées pour libérer des ressources ? Ou bien une nouvelle connexion à la base de données doit-elle être ouverte ?

1voto

Andrew Kirk Points 488

En me basant sur la suggestion de @mpobrien ci-dessus, j'ai trouvé la solution suivante asynchrone module pour être incroyablement utile à cet égard. Voici un exemple de modèle que j'ai fini par adopter :

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

1voto

JJ Stiff Points 36

J'ai trouvé une solution qui implique un compteur comme celui-ci. Il ne dépend pas d'un appel count() et n'attend pas de délai d'attente. Il fermera la base de données après que tous les documents dans each() soient épuisés.

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

Ainsi, à chaque fois que vous allez faire un appel comme db.each() ou db.save(), vous utiliserez ces méthodes pour vous assurer que la base de données est prête pendant le travail et fermée lorsque vous avez terminé.

Exemple de l'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. **
    }
  });
});

Maintenant, cela suppose que l'avant-dernier callback de chacun d'entre eux passe par mydb.open() avant que le dernier callback de chacun d'entre eux ne passe par mydb.close().... donc, bien sûr, faites-moi savoir si c'est un problème.

Donc : mettez un mydb.open(db) avant un appel de db et mettez un mydb.close(db) au point de retour de la callback ou après l'appel de db (selon le type d'appel).

Il me semble que ce type de compteur devrait être maintenu dans l'objet db mais c'est ma solution actuelle. Peut-être que nous pourrions créer un nouvel objet qui prendrait un db dans le constructeur et envelopper les fonctions mongodb pour mieux gérer la fermeture.

0voto

Une façon moderne de faire cela sans compteurs, bibliothèques ou tout code personnalisé :

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

Testé sur la version 3.2.7 du pilote mongodb mais selon le lien, il pourrait être valide puisque version 2.0

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X