Mongoose: Les rappels cessent de se produire

Créé le 13 sept. 2016  ·  53Commentaires  ·  Source: Automattic/mongoose

J'utilise mongoose depuis quelques années, mais dernièrement, j'ai beaucoup de problèmes où la connexion semble disparaître.

Paramètres de connexion

// Connecting to MongoDB 3.2.1
var connString = "mongodb://xxxxx:xxxxx<strong i="8">@xxxxx</strong>:xxxx,xxxx:xxxx/xxx?ssl=true";
var connOptions = {
  "mongos": { "ssl": true, "sslValidate": false }
};
mongoose.connect(connString, connOptions, function(err, db) {
  if(err) { console.log("Error connecting to db: "+err); }
});

Le problème

Mongoose arrête juste de rappeler. Il n'émet aucune erreur, la mémoire/cpu est bien sur mon serveur et ma base de données, et je peux me connecter à partir de n'importe quel autre serveur. Le redémarrage résout toujours le problème :

db.Something.count({}, callback);

Versions

Sur mongoose 4.4.11 , mongoose.connection.readyState === UNAUTHORIZED lorsque les rappels s'arrêtent.

J'ai donc mis à mongoose 4.6.0 mais dans les 4 heures, les rappels se sont à nouveau arrêtés, maintenant avec mongoose.connection.readyState === CONNECTED .

Des idées

Avez-vous des idées sur la façon dont je peux faire en sorte que cela arrête de se produire?

Je suis heureux de rassembler plus d'informations de débogage, faites-moi simplement savoir quelles informations obtenir.

underlying library issue

Commentaire le plus utile

Malheureusement, je vais devoir repousser celui-ci car le pilote mongodb est bloqué pour être libéré. Désolé et merci pour votre patience :+1: 🌴

Tous les 53 commentaires

Vous connectez-vous à un cluster autonome, à un ensemble de réplicas ou à un cluster partitionné ? Pouvez-vous également confirmer que vous utilisez SSL ?

Cela a été un énorme problème pour moi ces derniers jours. Chaque application en cours d'exécution s'est retrouvée avec une boucle d'événements de plus en plus lente, entraînant une latence croissante dans l'ensemble du système. Le redémarrage des applications Node a résolu le problème à chaque fois, le problème réapparaissant après des minutes ou des heures de fonctionnement.

Le retour à la mangouste 4.5.10 a résolu le problème.

  • Connexion au jeu de répliques
  • Utiliser SSL

Problème similaire ici avec 4.6.0, je ne sais pas si je me connecte à un jeu de réplicas ou non, mais j'utilise certainement SSL.

FWIW, 4.6.0 interrompait constamment les connexions. 4.5.10 c'est bien.

@djanowski pouvez-vous fournir plus d'informations, comme à quoi ressemble votre code de connexion et quelle version de mongodb vous avez ?

@ vkarpov15 J'utilisais MongoDB 3.2.6.

Possibilités de connexion :

auth: {
  authMechanism: 'SCRAM-SHA-1',
},
mongos: {
  ssl: true,
  sslValidate: true,
  sslCA: [cert],
},

Mongos simples ou mongos multiples ?

Plusieurs mongos. Utilisation d'un déploiement Compose Mongo.

Y a-t-il autre chose que je puisse faire pour aider à résoudre ce problème ? Cela nous empêche de passer aux versions plus récentes...

Difficile pour moi de faire une hypothèse réelle sans voir votre code. Si vous vous connectez avec mongoose.connect je vérifierais la valeur de mongoose.connection.readyState lorsque ce problème commence à se produire. De plus, j'activerais le mode de débogage de la mangouste, mongoose.set('debug', true) , qui imprimera des messages de débogage sur la console pour chaque opération de la mangouste. Que ces messages soient imprimés ou non aiderait certainement le débogage.

@vkarpov15 C'est le code que j'utilise pour me connecter : https://gist.github.com/sommestad/c0c6a7fa4feaadf84ecbb59cc3432c90
Cela fait partie d'un module privé, d'où le Gist.

_(La raison du module d'encapsulation de connexion est que la connexion est parfois réutilisée dans d'autres modules NPM privés, ce qui n'a pas bien fonctionné avec la mangouste historiquement.)_

J'avais activé la journalisation de l'état de la connexion lorsque ces problèmes se sont produits, mais la connexion elle-même ne semblait pas changer (aucun événement émis au moins). Donc, il ne semblait pas que le client était déconnecté.

Il ne se connecte pas avec un replicaSet dans l'URI (la base de données cible est un Compose Deployment ). Comme mentionné, SSL est utilisé. De plus, le trafic est acheminé via une passerelle AWS NAT. AWS NAT a un délai d'inactivité de 5 minutes, mais ces problèmes se sont produits même lors d'un trafic élevé.

un problème pour moi, l'avez-vous résolu?

@youth7 non aucune idée. Vous connectez-vous également à un déploiement de composition ? Veuillez fournir des informations supplémentaires

Y a-t-il autre chose que je puisse faire pour essayer de trouver la cause de cela? Des théories qui peuvent être étudiées?

@sommestad rien de concret. Avez-vous des journaux de compose?

J'ai essayé le mongoose 4.7.2 mais j'ai rencontré le même problème après 48 heures. À ce stade, je suis paralysé par 4.4.11 qui a ses propres problèmes.

J'ai installé 4.7.2 sur notre serveur de démonstration dans l'espoir de collecter des journaux, mais il fonctionne depuis deux semaines sans problème. Peut-être que le volume de données/connexions/etc provoque le problème ?

Une fois que cela commence à se produire, le code pour le reproduire est très simple. Fondamentalement, le rappel de toute requête disparaît :

db.Something.find({}, callback); //this callback is never fired

Comme je l'ai mentionné, lorsque cela se produit, mongoose.connection.readyState === CONNECTED

Bizarre. Je voulais dire les journaux mongodb - pouvez-vous vider les journaux du serveur mongodb à partir du moment où cette erreur s'est produite?

J'ai scanné les journaux de la base de données lorsque cet échec s'est produit et je n'ai rien vu d'anormal. Il n'y a eu aucun problème lié au nombre de connexions, de déconnexions, de problèmes de connexion ou similaires. J'ai également contacté Compose qui n'a rien vu d'anormal non plus.

@bendytree Pouvez-vous lancer du volume sur ce serveur de démonstration, peut-être en utilisant un outil de test de charge HTTP ?

@bendytree Aussi, cela se produit-il sur toutes les lignes de version de Node (4.x, 6.x et 7.x) ?

@vkarpov15 Pouvons-nous sortir 4.7.7 pour que je puisse tester avec le dernier pilote MongoDB ? 4.7.6 est épinglé à une version qui semble obsolète.

@vkarpov15 66d559b19a86c70e30a8f083d03eb22566571b7e Merci ! Je vais essayer.

Idem ici, une réplique SSL ici, après quelques minutes/heures, les requêtes s'arrêtent simplement à callback() (j'ai ajouté une journalisation plus détaillée) et aucune erreur. J'ai mis à niveau vers 4.7.7 la nuit dernière mais j'ai toujours le problème.

EDIT : j'ai essayé de rétrograder à 4.7.0 mais c'était pire (avoir le problème en moins de 5 minutes). J'essaye 4.5.10.

@gierschv Nœud 6.9.x ?

@djanowski non, toujours sur 4.6/4.7.x LTS.

@djanowski Je ne l'ai essayé que sur le nœud 4.4.2

Tenir une liste des doublons probables de ce problème :

  • #4638
  • #4690
  • #4901

Pour info, la 4.5.10 semble bien fonctionner ici, aucun problème depuis 5 heures. J'ai également eu une utilisation élevée du processeur comme #4690 sur toutes les machines qui semble également être corrigée par le downgrade.

Pas de nouveautés ici ? Que pouvons-nous faire de plus pour aider à trouver le bogue ?

@sommestad avez-vous toujours ce problème avec la dernière mangouste ?

Je n'ai pas encore osé faire de mise à niveau, car cela a essentiellement cassé tout notre système la dernière fois (les changements apparaissent après "un certain" temps, il est donc difficile de vérifier sans le faire fonctionner longtemps). Si vous avez des raisons de croire que c'est réparé, nous pourrions essayer une partie plus isolée du système ?

Edit : ping @varunjayaraman

De mon côté, nous sommes passés à Node 6.x LTS et sommes passés à la dernière version mangouste il y a quelques semaines, aucun problème depuis.

Nous sommes passés à Node 6.10.2, Mongoose 4.9.9 il y a environ deux mois. Toujours le même problème.

Notre chaîne de connexion a le format mongodb://username:[email protected]:1234,db2.com:2345/db-name?ssl=true .

console.log("fetching user...");
userCollection.findOne({_id: userId}, function(err, user){
  console.log("done"); // this never happens
  ...
});

Une fois que les rappels s'arrêtent, ils ne recommencent plus. Si nous redémarrons le processus, il se connecte bien.

S'il y a des données que nous pouvons collecter pour aider à résoudre les problèmes, alors nous aimerions vous aider.

Je vois le même problème et je peux le reproduire avec le code ci-dessous (désolé pour le style maquette / code rapide).

Pour simuler le délai d'expiration de la connexion, j'ai abaissé le poolSize à 1 et déclenché les requêtes à 2 points de terminaison (normal / lent) qui sont appelés dans cet ordre :

  1. point de terminaison normal (qui renvoie les résultats immédiatement)
  2. point de terminaison lent (qui lancera une requête lente)
  3. point de terminaison normal (qui attendra une connexion disponible car il est utilisé par 2)
  4. point de terminaison normal (qui se déclenchera après une minute, lorsque 2 et 3 ont entraîné une MongoError / timeout de connexion)

Reproduire en utilisant :

// Require modules
const mongoose = require('mongoose');
const express = require('express');
const request = require('request');
const parseJson = require('body-parser').json();

// Use native Promises in Mongoose
mongoose.Promise = Promise;

// Create the test schema
const testSchema = new mongoose.Schema({
  name: {
    type: String,
    trim: true,
    default: 'test'
  }
});

// Create the mongoose model
mongoose.model('Test', testSchema, 'test');

// Create some test data
mongoose.model('Test').create([
  {name: 'test1'},
  {name: 'test2'},
  {name: 'test3'},
  {name: 'test4'},
  {name: 'test5'}
]);

// Create the Express based app
const app = express();

// Create the express router
const router = express.Router();

// Create a normal route
router.route('/normal').get(parseJson, function (req, res, next) {
  mongoose.model('Test').find({}).then(
    function (data) {
      res.status(200).json(data);
    }
  ).catch(
    function (err) {
      res.status(400).json(err);
    }
  );
});

// Create up a slow route
router.route('/slow').get(parseJson, function (req, res, next) {
  mongoose.model('Test').find({$where: 'sleep(10000) || true'}).then(
    function (data) {
      res.status(200).json(data);
    }
  ).catch(
    function (err) {
      res.status(400).json(err);
    }
  );
});

// Add middleware to console log every request
var requestNumber = 1;
app.use(function (req, res, next) {
  console.log('Request ' + requestNumber, req.method, req.url);
  requestNumber++;
  next();
});

// Use the router
app.use('/', router);

// Listen for requests
app.listen(4000, function () {
  console.log('Server listening on port 4000');
});

// Catch any uncaught exceptions
process.on('uncaughtException', function (err) {
  console.log('Uncaught exception', err);
});

// Catch any unhandled rejections
process.on('unhandledRejection', function (reason) {
  console.error('Unhandled Rejection', reason);
});

// Database connection options
const connectionOpts = {
  // Use the new connection logic
  useMongoClient: true,

  // Do not auto reconnect (the Node code will auto reconnect)
  autoReconnect: false,

  // Use a poolsize of 1 to simulate a timeout using multiple request to slow and normal endpoints
  poolSize: 1
};

// Connection method with retry
const connectWithRetry = function () {
  // Check if we still need to connect
  if (mongoose.connection.readyState !== mongoose.Connection.STATES.connected) {
    // Connect
    return mongoose.connect('mongodb://localhost:27017/test', connectionOpts).catch(
      function (err) {
        console.log('Can not connect to mongo', err);
      }
    );
  }
};

// Helper function to execute a request to the normal endpoint
const doRequestNormal = function () {
  request({
    url: 'http://localhost:4000/normal',
    json: true,
    timeout: 60000
  }, function (error, response, body) {
    if (error) {
      console.log('Error on normal request', error, response);
    }
    console.log('Performed normal request, body:', body);
  });
};

// Helper function to execute a request to the normal endpoint
const doRequestSlow = function () {
  request({
    url: 'http://localhost:4000/slow',
    json: true,
    timeout: 60000
  }, function (error, response, body) {
    if (error) {
      console.log('Error on slow request', error, response);
    }
    console.log('Performed slow request', body);
  });
};

// Helper function to simulate requests
const doRequests = function () {
  doRequestNormal();
  doRequestSlow();
  doRequestNormal();

  setTimeout(function () {
    console.log('Do normal request after 1 minute');
    doRequestNormal();
  }, 60000);
};

// Connecting event
mongoose.connection.on('connecting', function () {
  console.log('Connecting to database...');
});

// Connected event
mongoose.connection.on('connected', function () {
  console.log('Database is connected, start requesting...');
  doRequests();
});

// Timeout event
mongoose.connection.on('timeout', function (err) {
  console.log('Database timeout error', err);
});

// Error event
mongoose.connection.on('error', function (err) {
  console.log('Database connection error', err);
});

// Disconnected event
mongoose.connection.on('disconnected', function () {
  console.error('Database got disconnected, reconnecting in 5 sec...');

  // Reconnect in 5 seconds
  setTimeout(connectWithRetry, 5000);
});

// Connect to the database with retry
connectWithRetry();

// Log the connection state every 10 seconds
setInterval(function () {
  console.log('Mongoose connection readystate is', mongoose.connection.readyState);
}, 10000);

La sortie de la console est la suivante :

Connecting to database...
Server listening on port 4000
Database is connected, start requesting...
Request 1 GET /normal
Request 2 GET /slow
Request 3 GET /normal
Performed normal request, body: [ { _id: '597f8c6f41bf2e119594ba1a', __v: 0, name: 'test1' },
  { _id: '597f8c6f41bf2e119594ba1b', __v: 0, name: 'test2' },
  { _id: '597f8c6f41bf2e119594ba1c', __v: 0, name: 'test3' },
  { _id: '597f8c6f41bf2e119594ba1d', __v: 0, name: 'test4' },
  { _id: '597f8c6f41bf2e119594ba1e', __v: 0, name: 'test5' } ]
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Performed slow request { name: 'MongoError',
  message: 'connection 0 to localhost:27017 timed out' }
Performed normal request, body: { name: 'MongoError',
  message: 'connection 0 to localhost:27017 timed out' }
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Do normal request after 1 minute
Request 4 GET /normal
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Error on normal request { Error: ESOCKETTIMEDOUT
    at ClientRequest.<anonymous> (/Users/adriaanmeuris/Documents/Projecten/Mongoose timeout/node_modules/request/request.js:819:19)
    at ClientRequest.g (events.js:291:16)
    at emitNone (events.js:86:13)
    at ClientRequest.emit (events.js:185:7)
    at Socket.emitTimeout (_http_client.js:620:10)
    at Socket.g (events.js:291:16)
    at emitNone (events.js:86:13)
    at Socket.emit (events.js:185:7)
    at Socket._onTimeout (net.js:339:8)
    at ontimeout (timers.js:365:14)
    at tryOnTimeout (timers.js:237:5)
    at Timer.listOnTimeout (timers.js:207:5) code: 'ESOCKETTIMEDOUT', connect: false } undefined
Performed normal request, body: undefined
Mongoose connection readystate is 1
Mongoose connection readystate is 1

Je surveille le readyState de la base de données toutes les 10 secondes, ce qui indique que la connexion est toujours active. Aucun événement d'erreur/d'expiration de Mongoose ne se déclenche, et le rappel de la demande 4 n'est jamais appelé (il expire après 60 s en utilisant le package de demande). Vous pouvez également tester en visitant http://localhost :4000/slow et ensuite http://localhost :4000/normal.

J'utilise une logique de reconnexion automatique basée sur https://team.goodeggs.com/reconnecting-to-mongodb-when-mongoose-connect-fails-at-startup-83ca8496ca02 , mais cela n'est jamais déclenché car mongo ne se déconnecte pas.

À certaines occasions, je ne suis pas en mesure de reproduire lorsque je commente le code qui crée des données de test (donc interrogeant essentiellement une collection vide)

Problèmes connexes possibles :

  • #4789
  • #4660

Toute aide me serait grandement appréciée.

J'ai donc creusé un peu plus et réécrit le script ci-dessus pour travailler avec node-mongodb-native sans utiliser mongoose. Les résultats sont très différents maintenant :

Les paramètres de connexion et les demandes sont les mêmes :

  1. point de terminaison normal (qui renvoie les résultats immédiatement)
  2. point de terminaison lent (qui lancera une requête lente)
  3. point de terminaison normal (qui attendra une connexion disponible car il est utilisé par 2)
  4. point final normal (qui se déclenchera après une minute)

Code:

// Require modules
const MongoClient = require('mongodb').MongoClient;
const Server = require('mongodb').Server;
const express = require('express');
const request = require('request');
const parseJson = require('body-parser').json();

// The db reference
var db;

// Function to insert test data
function insertTestData() {
  return new Promise(function (resolve, reject) {
    // Get the test collection
    var test = db.collection('test');

    // Insert some documents
    test.insertMany([
      {name: 'test1'},
      {name: 'test2'},
      {name: 'test3'},
      {name: 'test4'},
      {name: 'test5'}
    ], function (err, result) {
      if (err) {
        reject(err);
      } else {
        resolve();
      }
    });
  });
}

// Create the Express based app
const app = express();

// Create the express router
const router = express.Router();

// Create a normal route
router.route('/normal').get(parseJson, function (req, res, next) {
  // Get the documents collection
  var collection = db.collection('test');

  // Find some documents
  collection.find({}).toArray(function (err, data) {
    if (err) {
      res.status(400).json(err);
    } else {
      res.status(200).json(data);
    }
  });
});

// Create up a slow route
router.route('/slow').get(parseJson, function (req, res, next) {
  // Get the documents collection
  var collection = db.collection('test');

  // Find some documents
  collection.find({$where: 'sleep(10000) || true'}).toArray(function (err, data) {
    if (err) {
      res.status(400).json(err);
    } else {
      res.status(200).json(data);
    }
  });
});

// Add middleware to console log every request
var requestNumber = 1;
app.use(function (req, res, next) {
  console.log('Request ' + requestNumber, req.method, req.url);
  requestNumber++;
  next();
});

// Use the router
app.use('/', router);

// Listen for requests
app.listen(4000, function () {
  console.log('Server listening on port 4000');
});

// Catch any uncaught exceptions
process.on('uncaughtException', function (err) {
  console.log('Uncaught exception', err);
});

// Catch any unhandled rejections
process.on('unhandledRejection', function (reason) {
  console.error('Unhandled Rejection', reason);
});

// Database connection options
const connectionOpts = {
  // Do not auto reconnect (the Node code will auto reconnect)
  autoReconnect: false,

  // Use a poolsize of 1 to simulate a timeout using multiple request to slow and normal endpoints
  poolSize: 1
};

// Connection method with retry
const connectWithRetry = function () {

  return MongoClient.connect('mongodb://localhost:27017/test', connectionOpts).then(
    function (database) {
      db = database;
      setupEvents();
      console.log('Connected to mongo');
    }
  ).catch(
    function (err) {
      console.log('Can not connect to mongo', err);
      return err;
    }
  );
};

// Helper function to execute a request to the normal endpoint
const doRequestNormal = function () {
  request({
    url: 'http://localhost:4000/normal',
    json: true,
    timeout: 60000
  }, function (error, response, body) {
    if (error) {
      console.log('Error on normal request', error, response);
    }
    console.log('Performed normal request, body:', body);
  });
};

// Helper function to execute a request to the normal endpoint
const doRequestSlow = function () {
  request({
    url: 'http://localhost:4000/slow',
    json: true,
    timeout: 60000
  }, function (error, response, body) {
    if (error) {
      console.log('Error on slow request', error, response);
    }
    console.log('Performed slow request', body);
  });
};

// Helper function to simulate requests
const doRequests = function () {
  doRequestNormal();
  doRequestSlow();
  doRequestNormal();

  setTimeout(function () {
    console.log('Do normal request after 1 minute');
    doRequestNormal();
  }, 60000);
};

// Helper function to setup mongo events
function setupEvents() {
// Connecting event
  db.on('connecting', function () {
    console.log('Connecting to database...');
  });

// Connected event
  db.on('connected', function () {
    console.log('Database is connected, start requesting...');
  });

// Timeout event
  db.on('timeout', function (err) {
    console.log('Database timeout error', err);
  });

// Error event
  db.on('error', function (err) {
    console.log('Database connection error', err);
  });

// Disconnected event
  db.on('close', function () {
    console.error('Database got disconnected, reconnecting in 5 sec...');

    // Reconnect in 5 seconds
    setTimeout(connectWithRetry, 5000);
  });
}

// Connect to the database with retry
connectWithRetry().then(
  function () {
    return insertTestData();
  }
).then(
  function () {
    doRequests();
  }
);

La sortie de la console est la suivante :

Server listening on port 4000
Connected to mongo
Request 1 GET /normal
Request 2 GET /slow
Request 3 GET /normal
Performed normal request, body: [ { _id: '598207c16caf9224cf3b8897', name: 'test1' },
  { _id: '598207c16caf9224cf3b8898', name: 'test2' },
  { _id: '598207c16caf9224cf3b8899', name: 'test3' },
  { _id: '598207c16caf9224cf3b889a', name: 'test4' },
  { _id: '598207c16caf9224cf3b889b', name: 'test5' } ]
Performed slow request [ { _id: '598207c16caf9224cf3b8897', name: 'test1' },
  { _id: '598207c16caf9224cf3b8898', name: 'test2' },
  { _id: '598207c16caf9224cf3b8899', name: 'test3' },
  { _id: '598207c16caf9224cf3b889a', name: 'test4' },
  { _id: '598207c16caf9224cf3b889b', name: 'test5' } ]
Performed normal request, body: [ { _id: '598207c16caf9224cf3b8897', name: 'test1' },
  { _id: '598207c16caf9224cf3b8898', name: 'test2' },
  { _id: '598207c16caf9224cf3b8899', name: 'test3' },
  { _id: '598207c16caf9224cf3b889a', name: 'test4' },
  { _id: '598207c16caf9224cf3b889b', name: 'test5' } ]
Do normal request after 1 minute
Request 4 GET /normal
Performed normal request, body: [ { _id: '598207c16caf9224cf3b8897', name: 'test1' },
  { _id: '598207c16caf9224cf3b8898', name: 'test2' },
  { _id: '598207c16caf9224cf3b8899', name: 'test3' },
  { _id: '598207c16caf9224cf3b889a', name: 'test4' },
  { _id: '598207c16caf9224cf3b889b', name: 'test5' } ]

Aucun événement d'erreur/d'expiration Mongo ne se déclenche comme le script précédent, mais les requêtes 2 et 3 se résolvent correctement et le rappel de la requête 4 est également déclenché, contrairement au script qui utilise Mongoose.

J'essaie d'exclure s'il s'agit d'un problème Mongoose vs. Mongo - donc toute aide concernant la comparaison des résultats de ces scripts serait appréciée. Peut-être que @vkarpov15 peut intervenir sur celui-ci ? Merci!

@adriaanmeuris c'est étrange, voici le résultat que je reçois en exécutant votre premier script :

$ node gh-4513.js 
Connecting to database...
Server listening on port 4000
Database is connected, start requesting...
Request 1 GET /normal
Request 2 GET /slow
Request 3 GET /normal
Performed normal request, body: [ { _id: '5998e4a79bb81d37ac4b1a26', __v: 0, name: 'test1' },
  { _id: '5998e4a79bb81d37ac4b1a27', __v: 0, name: 'test2' },
  { _id: '5998e4a79bb81d37ac4b1a28', __v: 0, name: 'test3' },
  { _id: '5998e4a79bb81d37ac4b1a29', __v: 0, name: 'test4' },
  { _id: '5998e4a79bb81d37ac4b1a2a', __v: 0, name: 'test5' } ]
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Mongoose connection readystate is 1
Performed slow request [ { _id: '5998e4a79bb81d37ac4b1a26', __v: 0, name: 'test1' },
  { _id: '5998e4a79bb81d37ac4b1a27', __v: 0, name: 'test2' },
  { _id: '5998e4a79bb81d37ac4b1a28', __v: 0, name: 'test3' },
  { _id: '5998e4a79bb81d37ac4b1a29', __v: 0, name: 'test4' },
  { _id: '5998e4a79bb81d37ac4b1a2a', __v: 0, name: 'test5' } ]
Performed normal request, body: [ { _id: '5998e4a79bb81d37ac4b1a26', __v: 0, name: 'test1' },
  { _id: '5998e4a79bb81d37ac4b1a27', __v: 0, name: 'test2' },
  { _id: '5998e4a79bb81d37ac4b1a28', __v: 0, name: 'test3' },
  { _id: '5998e4a79bb81d37ac4b1a29', __v: 0, name: 'test4' },
  { _id: '5998e4a79bb81d37ac4b1a2a', __v: 0, name: 'test5' } ]
Mongoose connection readystate is 1
Do normal request after 1 minute
Request 4 GET /normal
Performed normal request, body: [ { _id: '5998e4a79bb81d37ac4b1a26', __v: 0, name: 'test1' },
  { _id: '5998e4a79bb81d37ac4b1a27', __v: 0, name: 'test2' },
  { _id: '5998e4a79bb81d37ac4b1a28', __v: 0, name: 'test3' },
  { _id: '5998e4a79bb81d37ac4b1a29', __v: 0, name: 'test4' },
  { _id: '5998e4a79bb81d37ac4b1a2a', __v: 0, name: 'test5' } ]
Mongoose connection readystate is 1
Mongoose connection readystate is 1
^C
$ 

Mongoose reste connecté. Pouvez-vous clarifier vos versions de mongodb, node et mongoose ?

merci d'avoir testé ! En effet, Mongoose reste toujours connecté, mais les rappels cessent de se produire (vous pouvez voir la demande entrer dans Express, mais ils expireront)

Je testais avec :

  • nœud 6.8.1
  • mongodb 2.2.30
  • mangouste 4.11.5

J'ai mis à jour :

  • nœud 6.8.1
  • mongodb 2.2.31
  • mangouste 4.11.7

Je peux toujours reproduire le problème localement avec le premier script que j'ai posté. Cela ne se produit que lorsqu'une connexion Mongo expire, vous pouvez donc modifier la requête qui simule ceci :

mongoose.model('Test').find({$where: 'sleep(10000) || true'})

(Je ne sais pas si cela déclenchera un délai d'attente sur chaque machine, une modification de cette requête pourrait donc être nécessaire pour se reproduire)

Nous ne voyons plus ces problèmes après la mise à jour vers la mangouste 4.10.4.
Cela semble un peu étrange, car d'autres semblent rencontrer des problèmes, mais de nombreux services fonctionnent depuis quelques semaines sans qu'aucun signe du problème ne revienne. ??

Cependant, nous avons vu des problèmes de la même manière que @adriaanmeuris précédemment, il se peut donc que nous n'ayons tout simplement pas rencontré de délais d'attente .

Exécution du nœud 8.

Nous voyons encore ce problème périodiquement.

Je ne pouvais pas reproduire avec l » @adriaanmeuris scénario , mais voici une version modifiée qu'il reproduit à chaque fois pour moi:

bendytree/mongoose-issue-4513

Merci @bendytree , avec votre script je peux aussi reproduire :

7) Database connecting...
49) Database connected...
49) Dropping...
57) Creating...
156) FastQuery-A: starting...
166) FastQuery-A: success: 1
1158) SlowQuery: starting...
2158) FastQuery-B: starting...
362165) SlowQuery: failed: MongoError: connection 0 to localhost:27017 timed out
362167) FastQuery-B: failed: MongoError: connection 0 to localhost:27017 timed out
605159) FastQuery-C: starting...
1210149) Giving Up...

C'est essentiellement le même script que le mien mais avec un délai d'attente plus long. Je suppose que simuler un délai d'attente localement prend plus/moins de temps en fonction de votre configuration, car tout le monde ne pourrait pas reproduire avec mon script (qui utilise un sommeil de 10 secondes).

@vkarpov15 pouvez-vous essayer ce script mis à jour. J'ai testé et reproduit avec la mangouste 4.8.11.

J'ai réussi à reproduire avec @bendytree , c'est un cas de socketTimeoutMS trop bas. Les

362591) SlowQuery: failed: MongoError: connection 0 to localhost:27017 timed out
362591) FastQuery-B: failed: MongoError: connection 0 to localhost:27017 timed out

les messages indiquent que le socket du pilote mongodb expire. Le pilote mongodb expire après 30 secondes par défaut, mais votre système d'exploitation a également un délai d'expiration de socket dont vous devez être conscient, c'est donc généralement une bonne idée d'éviter les requêtes qui prennent plus de 15 secondes. Cependant, vous pouvez configurer le délai d'expiration du socket de mongodb en utilisant l'option socketTimeoutMS :

mongoose.connect(DB_URL, {
  useMongoClient: true,
  autoReconnect: false,
  poolSize: 1,
  socketTimeoutMS: 0
}).catch((err)=>{ log('Connect failed', err); });

Avec cette option définie sur 0, votre script réussit presque sur ma machine, la requête de 10 minutes meurt toujours mais les rappels continuent

$ node gh-4513.js 
9) Database connecting...
43) Database connected...
43) Dropping...
53) Creating...
177) FastQuery-A: starting...
189) FastQuery-A: success: 1
1174) SlowQuery: starting...
2174) FastQuery-B: starting...
601181) SlowQuery: failed: MongoError: Interrupted by the host
601182) FastQuery-B: success: 1
605173) FastQuery-C: starting...
605174) FastQuery-C: success: 1

Mais la requête suivante réussit.

Dans tous les cas, la mangouste devrait émettre un événement de délai d'attente dans ce cas, nous allons donc résoudre ce problème.

Je pense que vous confondez connectTimeoutMS (avec une valeur par défaut de 30000ms) avec socketTimeoutMS (avec une valeur par défaut de 360000ms).

Il est conseillé de définir socketTimeoutMS sur deux à trois fois la durée de l'opération la plus lente qui passe par le pilote, définir socketTimeoutMS sur 0 signifie en fait appliquer la valeur de délai d'attente de socket par défaut du système d'exploitation (plus d'informations sur http://mongodb.github.io/ node-mongodb-native/2.2/reference/faq/).

Cette question demeure donc : pourquoi ces rappels cessent-ils de se produire lorsque cette limite (délai d'expiration fixe ou par défaut du système d'exploitation) est atteinte ?

@ vkarpov15 avec [email protected] , je vois maintenant l'événement de délai d'attente, merci pour cela. Cependant, vous voyez toujours ce problème de rappel (en utilisant le script d'origine de @bendytree , pour des tests plus rapides, vous pouvez définir LONG_QUERY_DURATION_IN_MS sur 2000 et socketTimeoutMS sur 1000)

J'ai fait d'autres tests et j'ai découvert que cela peut également être reproduit sans Mongoose. En lisant http://mongodb.github.io/node-mongodb-native/2.2/reference/faq/ , j'ai découvert que : Closing the socket forces a reconnect of the driver’s connection pool and introduces latency to any other operations which are queued up

Même si mongoose.connection.readyState vaut toujours 1, j'ai essayé de définir autoReconnect sur true , et de relancer le script :

8) Database connecting...
47) Database connected...
48) Dropping...
73) Creating...
123) FastQuery-A: starting...
129) FastQuery-A: success: 1
1122) SlowQuery: starting...
2126) FastQuery-B: starting...
3130) Database timeout...
3133) SlowQuery: failed: MongoError: connection 0 to localhost:27017 timed out
3133) FastQuery-B: failed: MongoError: connection 0 to localhost:27017 timed out
6063) Database timeout...
7122) FastQuery-C: starting...
7125) FastQuery-C: success: 1
8129) Database timeout...
11070) Database timeout...
13073) Database timeout...

Il semble donc que les rappels fonctionnent avec autoReconnect défini sur true 👍

En poursuivant la lecture de cet article : http://mongodb.github.io/node-mongodb-native/2.0/tutorials/connection_failures/ , j'ai réalisé que la raison pour laquelle les rappels ne se produisent pas après le délai d'attente, c'est parce que l'opération est mis en mémoire tampon en attendant que le serveur se reconnecte.

J'ai donc essayé autoReconnect remettre false + remettre bufferMaxEntries sur 0 , voici le résultat :

8) Database connecting...
51) Database connected...
51) Dropping...
58) Creating...
112) FastQuery-A: starting...
116) FastQuery-A: success: 1
1115) SlowQuery: starting...
2115) FastQuery-B: starting...
3123) Database timeout...
3123) SlowQuery: failed: MongoError: connection 0 to localhost:27017 timed out
3123) FastQuery-B: failed: MongoError: connection 0 to localhost:27017 timed out
7115) FastQuery-C: starting...
7117) FastQuery-C: failed: MongoError: no connection available for operation and number of stored operation > 0

Ce qui explique pourquoi le rappel ne s'est pas déclenché dans la première version du script de

@vkarpov15 Cela nous laisse maintenant avec 2 questions :

  • pourquoi n'y a-t-il aucun événement de connexion émis (je m'attendrais à voir un événement déconnecté)
  • l'événement timeout semble continuer à émettre

voici le script mis à jour (basé sur script de @bendytree, testé avec [email protected] et [email protected]):

const LONG_QUERY_DURATION_IN_MS = 2000;
const DB_URL = 'mongodb://localhost:27017/test_mongoose_callbacks';

const mongoose = require('mongoose');
mongoose.Promise = Promise;

var startTime = new Date().getTime();
var log = (msg) => {
  var seconds = Math.round(new Date().getTime() - startTime);
  console.log(seconds+") "+msg);
};

var Model = mongoose.model('Test', new mongoose.Schema({
  name: { type: String, trim: true, default: 'test' }
}), 'test');

mongoose.connection.on('connecting',   () => { log('Database connecting...'); });
mongoose.connection.on('timeout',      () => { log('Database timeout...'); });
mongoose.connection.on('error',        () => { log('Database error...'); });
mongoose.connection.on('disconnected', () => { log('Database disconnected...'); });

const doRequest = function (title, slow) {
  var logRequest = (msg) => { log(title+": "+msg); };

  logRequest("starting...");
  var filter = slow ? {$where: 'sleep('+LONG_QUERY_DURATION_IN_MS+') || true'} : {};
  Model.count(filter).exec((err, count) => {
    logRequest(err ? "failed: "+err : "success: "+count);
  });
};

mongoose.connection.on('connected', () => {
  log("Database connected...");
 log("Dropping...");
 mongoose.connection.db.dropDatabase(function(){
   log("Creating...");
   Model.create([ {name: 'test1'} ], function(){
      setTimeout(() => { doRequest("FastQuery-A", false); }, 0);
      setTimeout(() => { doRequest("SlowQuery", true);  }, 1000);
      setTimeout(() => { doRequest("FastQuery-B", false); }, 2000);
      setTimeout(() => { doRequest("FastQuery-C", false); }, LONG_QUERY_DURATION_IN_MS+5000);
      setTimeout(() => { log("Giving Up..."); }, LONG_QUERY_DURATION_IN_MS+30000);
    });
  });
});

mongoose.connect(DB_URL, {
  useMongoClient: true,
  autoReconnect: true,
  //bufferMaxEntries: 0, // Uncomment to disable buffering operations 
  poolSize: 1,
  socketTimeoutMS: 1000
}).catch((err)=>{ log('Connect failed', err); });

setInterval(()=>{
  if (mongoose.connection.readyState === 1) return;
  log('Mongoose.readystate = ', mongoose.connection.readyState);
}, 1000);

Oui, vous avez raison, nous devons faire mieux à ce sujet. L'événement timeout est nécessaire, mais pas vraiment suffisant pour gérer ce cas.

La raison pour laquelle 'déconnecté' n'est pas émis est que 'timeout' signifie qu'un socket a expiré, pas le pool entier. Mongoose aurait besoin de savoir combien de sockets dans le pool sont encore actifs lorsque l'événement de délai d'attente s'est produit, et j'aurais besoin d'enquêter davantage pour comprendre comment nous procédons.

Examinera plus en détail pourquoi l'événement de délai d'attente continue de se réémettre. Merci de votre patience :+1:

J'ai trouvé cela à quelques bogues dans le pilote mongodb : https://github.com/mongodb-js/mongodb-core/pull/215 . Il va falloir attendre que cela soit fusionné et déployé avant de pouvoir le patcher sur la mangouste.

@vkarpov15 dans mes propres tests, je mongoose.connection.readyState ne change jamais. est-ce le même problème ?

@r3wt ce problème devrait être résolu dans 4.11.13, quelle version utilisez-vous ?

@ vkarpov15 J'utilisais 4.11.11 au moment de mon message. c'est décourageant qu'un comportement aussi manifestement mauvais puisse se faufiler dans la production d'une bibliothèque aussi remarquable. peut-être que de meilleures procédures de test sont nécessaires. (Je n'essaie pas d'être irrespectueux, je sais à quel point il est difficile de maintenir des projets open source. Parfois, ça craint)

@ vkarpov15 je développe un plugin mangouste. il est nécessaire que le plugin se bloque jusqu'à ce que la connexion soit prête, sinon il se bloque en attente de connexion (pour des raisons). le code que j'utilise pour attendre que la connexion soit prête est le suivant :

function ready(){
    console.log('ready()');
    return new Promise((resolve,reject)=>{
        var _int = setInterval(()=>{
            if(mongoose.connection.readyState == 1 || forceReady){
                clearInterval(_int);
                console.log('mongoose connected ready().resolve()')
                resolve();
            }
        },200);
    })
}

J'ai aussi essayé d'écouter via mongoose.connnection.on('connected') mais hélas cela n'a pas fonctionné non plus. sur une installation vierge, cela a bien fonctionné. mais en conjonction avec d'autres plugins de mangouste et des trucs à l'écoute des événements, c'est là que le problème se produit. connection.readyState reste 0 pour toujours et l'événement connecté n'est jamais émis.

il semble que require('mongoose') !== require('mongoose') à mon avis. j'espère que cela a du sens.

c'est à dire:

  1. script utilisateur require('mongoose')
  2. plugin un require('mongoose')
  3. plugin b require('mongoose')

le script utilisateur et le plugin reçoivent les informations sur la connexion. mais le plugin b ne le fait jamais. Est-ce que ça fait du sens?

@r3wt en effet, c'est vraiment décourageant. Depuis, nous avons ajouté des tests qui arrêtent/démarrent un serveur mongodb et affirment que mongoose fait ce qu'il faut pour empêcher que cela ne se reproduise à l'avenir. Nous sommes désolés pour le problème.

Quant au blocage jusqu'à ce que la connexion soit prête, mongoose.connection.on('connected') devrait fonctionner. Quels autres plugins utilisez-vous ? Essayez d'exécuter npm list | grep "mongoose" , votre affirmation selon laquelle require('mongoose') !== require('mongoose') me fait penser que vous avez plusieurs versions de mongoose quelque part.

Malheureusement, je vais devoir repousser celui-ci car le pilote mongodb est bloqué pour être libéré. Désolé et merci pour votre patience :+1: 🌴

@ vkarpov15 Je voulais juste vous remercier pour votre travail sur ce correctif. Nous l'utilisons en production depuis quelques mois maintenant avec 0 problème.

@bendytree merci pour vos aimables paroles et votre patience pour faire reproduire ce problème. L'équipe de pilotes mongodb a été submergée par la publication de la version 3.0 à temps pour la version mongodb 3.6, ils n'ont donc pas encore été en mesure de publier une version 2.x avec ce correctif. Joyeuses fêtes!

Cette page vous a été utile?
0 / 5 - 0 notes