16
votes

Comment réparer MongoError: impossible d'utiliser une session qui s'est terminée

J'essaie de lire les données d'une collection MongoDB Atlas en utilisant Node.js. Lorsque j'essaye de lire le contenu de ma collection, j'obtiens l'erreur MongoError: Cannot use a session that has ended . Voici mon code

client.connect(err => {
    const collection = client
        .db("sample_airbnb")
        .collection("listingsAndReviews");

    const test = collection.find({}).toArray((err, result) => {
        if (err) throw err;
    });
    client.close();
});

Je suis en mesure de rechercher un document spécifique, mais je ne sais pas comment renvoyer tous les documents d'une collection. J'ai recherché cette erreur, je ne trouve pas grand-chose dessus. Merci


2 commentaires

pouvez-vous partager votre fichier indes.js?


J'ai partagé tout le code pertinent @MaheshBhatnagar


4 Réponses :


18
votes

Dans votre code, il n'attend pas que find() termine son exécution et passe à l' client.close() . Ainsi, au moment où il essaie de lire les données de la base de données, la connexion est déjà terminée. J'ai fait face à ce même problème et je l'ai résolu comme ceci:

// connect to your cluster
const client = await MongoClient.connect('yourMongoURL', { 
    useNewUrlParser: true, 
    useUnifiedTopology: true,
});
// specify the DB's name
const db = client.db('nameOfYourDB');
// execute find query
const items = await db.collection('items').find({}).toArray();
console.log(items);
// close connection
client.close();

EDIT: tout cela devrait être dans une fonction async .


3 commentaires

Merci, c'est similaire à ce que j'ai fini par faire.


Comment faire de tout cela une fonction async


Ce que je voulais dire, c'est que vous devriez écrire ceci dans une fonction asynchrone car vous utilisez await. Comme suit: const findItems = async () => { // write above code here };



2
votes

J'ai rencontré le même problème lorsque j'ai mis à jour MongoClient de la version 3.3.2 vers la dernière version (3.5.2 au moment de la rédaction de cet article.) Soit installer uniquement la version 3.3.2 en modifiant le package.json "mongodb": "3.3.2", ou utilisez simplement async et attendez le wrapper.

Si le problème persiste, supprimez les node_modules et réinstallez.


1 commentaires

votre solution a fonctionné pour moi aussi, j'ai triple vérifié toutes mes fonctions d'attente asynchrone et recevais toujours la même erreur jusqu'à ce que je rétrograde ma version à 3.3.2. Merci!



0
votes

Une option consiste à utiliser une chaîne Promise . collection.find({}).toArray() peut recevoir une fonction de rappel ou renvoyer une promesse, vous pouvez donc enchaîner les appels avec .then()

let counter = 0;

function doSomethingAsync(id, start) {
  return new Promise(resolve => {
    setTimeout(() => {
      counter++;
      const stop = new Date();    
      const runningTime = getSeconds(start, stop);
      resolve(`result${id} completed in ${runningTime} seconds`);
    }, 2000);
  });
}

function getSeconds(start, stop) {
  return (stop - start) / 1000;
}

async function test() {
  console.log('Awaiting 3 Async calls');
  console.log(`Counter before execution: ${counter}`);
  const start = new Date();
  
  const [result1, result2, result3] = await Promise.all([
        doSomethingAsync(1, new Date()),
        doSomethingAsync(2, new Date()),
        doSomethingAsync(3, new Date())
  ]);
  
  const stop = new Date();

  console.log(result1, result2, result3);
  console.log(`Counter after all ran: ${counter}`);
  console.log(`Total time to run: ${getSeconds(start, stop)}`);
 }

test();

Bien entendu, cette connexion en guirlande rend le code plus synchrone. Ceci est utile lorsque le prochain appel de la chaîne est lié au précédent, comme obtenir un identifiant d'utilisateur dans le premier, puis rechercher les détails de l'utilisateur dans le suivant.

Plusieurs requêtes non liées doivent être exécutées en parallèle (asynchrone) et lorsque tous les résultats sont de retour, supprimez la connexion à la base de données. Vous pouvez le faire en suivant chaque appel dans un tableau ou un compteur, par exemple.

async function test(){
    let [allItems, namesBeginningWithS, fiftyFiveYearOlds] = await Promise.all([
        collection.find({}).toArray(),
        collection.find({ name: /^S/ }).toArray(),
        collection.find({ age: 55 }).toArray()
    ]);

    client.close();
}

Vous avez 3 requêtes. Au fur et à mesure que chacun appelle dispose() le compteur s'incrémente. Quand ils ont tous appelé dispose() , le dernier fermera également la connexion.

Async / Await devrait le rendre encore plus facile, car ils déballent le résultat Promise de la fonction then .

let counter = 0;

function doSomethingAsync(id, start) {
  return new Promise(resolve => {
    setTimeout(() => {
      counter++;
      const stop = new Date();    
      const runningTime = getSeconds(start, stop);
      resolve(`result${id} completed in ${runningTime} seconds`);
    }, 2000);
  });
}

function getSeconds(start, stop) {
  return (stop - start) / 1000;
}

async function test() {
  console.log('Awaiting 3 Async calls');
  console.log(`Counter before execution: ${counter}`);
  
  const start = new Date();
  
  let callStart = new Date();
  const result1 = await doSomethingAsync(1, callStart);
  
  callStart = new Date();
  const result2 = await doSomethingAsync(2, callStart);
  
  callStart = new Date();
  const result3 = await doSomethingAsync(3, callStart);
  
  const stop = new Date();

  console.log(result1, result2, result3);
  console.log(`Counter after all ran: ${counter}`);
  console.log(`Total time to run: ${getSeconds(start, stop)}`);
 }

test();

Vous trouverez ci-dessous un exemple de la façon dont Async / Await peut finir par amener le code async à se comporter de manière séquentielle et à s'exécuter de manière inefficace en attendant qu'une fonction async se termine avant d'appeler la suivante, lorsque le scénario idéal est de les invoquer tous immédiatement et d'attendre seulement qu'ils sont complets.

async function test(){
    const allItems = await collection.find({}).toArray();
    const namesBeginningWithS = await collection.find({ name: /^S/ }).toArray();
    const fiftyFiveYearOlds = await collection.find({ age: 55 }).toArray();
    client.close();
}

test();

Remarque: l'attente comme dans l'exemple ci-dessus rend les appels séquentiels à nouveau. Si chacun prend 2 secondes pour s'exécuter, la fonction prendra 6 secondes pour se terminer.

En combinant le meilleur de tous les mondes, vous voudrez utiliser Async / Await tout en exécutant tous les appels immédiatement. Heureusement, Promise a une méthode pour faire cela, donc test() peut être écrit comme ceci: -

const totalQueries = 3;
let completedQueries = 0;

collection.find({}).toArray()
.then( items => {
    console.log('All items', items);
    dispose(); // Increments the counter and closes the connection if total reached
})

collection.find({ name: /^S/ }).toArray()
.then( items => {
    console.log("All items with field 'name' beginning with 'S'", items);
    dispose(); // Increments the counter and closes the connection if total reached
);

collection.find({ age: 55 }).toArray()
.then( items => {
    console.log("All items with field 'age' with value '55'", items);
    dispose(); // Increments the counter and closes the connection if total reached
);

function dispose(){
    if (++completedQueries >= totalQueries){
        client.close();
    }
}

Voici un exemple de travail pour démontrer la différence de performance: -

collection.find({}).toArray() // returns the 1st promise
.then( items => {
    console.log('All items', items);
    return collection.find({ name: /^S/ }).toArray(); //return another promise
})
.then( items => {
    console.log("All items with field 'name' beginning with 'S'", items);
    client.close(); // Last promise in the chain closes the database
);


0 commentaires

0
votes

d'autres personnes ont abordé ce sujet mais je veux juste souligner que .toArray () est exécuté de manière asynchrone, vous devez donc vous assurer qu'il est terminé avant de fermer la session

cela ne fonctionnera pas

const randomUser = await db.collection('user').aggregate([ { $sample: { size: 1 } } ]).toArray();
console.log(randomUser); 
await client.close();

cette volonté

const randomUser = await db.collection('user').aggregate([ { $sample: { size: 1 } } ]);
console.log(randomUser.toArray()); 
await client.close();


0 commentaires