Mongoose: Las devoluciones de llamada dejan de suceder

Creado en 13 sept. 2016  ·  53Comentarios  ·  Fuente: Automattic/mongoose

He estado usando mongoose durante algunos años, pero últimamente tengo muchos problemas donde la conexión parece desaparecer.

Configuración de conexión

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

El problema

Mongoose simplemente deja de llamar. No emite ningún error, la memoria / cpu está bien en mi servidor y base de datos, y puedo conectarme desde cualquier otro servidor. Reiniciar siempre lo soluciona:

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

Versiones

En mongoose 4.4.11 , mongoose.connection.readyState === UNAUTHORIZED cuando se detienen las devoluciones de llamada.

Así que actualicé a mongoose 4.6.0 pero en 4 horas las devoluciones de llamada se detuvieron nuevamente, ahora con mongoose.connection.readyState === CONNECTED .

Algunas ideas

¿Alguna idea de cómo puedo hacer que esto deje de suceder?

Estoy feliz de recopilar más información de depuración, solo avíseme qué información obtener.

underlying library issue

Comentario más útil

Desafortunadamente, tendré que retrasar este de nuevo porque el controlador mongodb no se libera. Lo siento y gracias por tu paciencia: +1: 🌴

Todos 53 comentarios

¿Se está conectando a un clúster fragmentado, conjunto de réplicas o independiente? Además, ¿puede confirmar que está usando SSL?

Este ha sido un gran problema para mí en los últimos días. Cada aplicación en ejecución terminó con un ciclo de eventos cada vez más lento, lo que provocó un aumento de la latencia en todo el sistema. El reinicio de las aplicaciones de nodo resolvió el problema cada vez, y volvía después de minutos u horas de funcionamiento.

Volver a mongoose 4.5.10 resolvió el problema.

  • Conexión al conjunto de réplicas
  • Usando SSL

Problema similar aquí con 4.6.0, no estoy seguro de si me estoy conectando a un conjunto de réplicas o no, pero definitivamente estoy usando SSL.

FWIW, 4.6.0 estaba cayendo conexiones constantemente. 4.5.10 es bueno.

@djanowski, ¿ puede proporcionar más información, como el aspecto de su código de conexión y qué versión de mongodb tiene?

@ vkarpov15 Estaba usando MongoDB 3.2.6.

Opciones de conexión:

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

¿Mongo único o mongo múltiple?

Varios mongos. Usando una implementación de Compose Mongo.

¿Algo más que pueda hacer para ayudar a solucionar este problema? Esto nos impide pasar a versiones más nuevas ...

Es difícil para mí hacer una hipótesis real sin ver su código. Si se está conectando con mongoose.connect , verificaría el valor de mongoose.connection.readyState cuando este problema comience a suceder. Además, habilitaría el modo de depuración de mangosta, mongoose.set('debug', true) , que imprimirá mensajes de depuración en la consola para cada operación de mangosta. Si esos mensajes se están imprimiendo o no, definitivamente ayudaría a depurar.

@ vkarpov15 Este es el código que estoy usando para conectarme: https://gist.github.com/sommestad/c0c6a7fa4feaadf84ecbb59cc3432c90
Es parte de un módulo privado, de ahí la esencia.

_ (La razón del módulo de envoltura de conexión es que la conexión se reutiliza ocasionalmente en otros módulos privados de NPM, lo que no ha funcionado bien con mangosta históricamente). _

Tenía el registro del estado de la conexión habilitado cuando ocurrieron estos problemas, pero la conexión en sí no pareció cambiar (al menos no se emitieron eventos). Entonces, no parecía que el cliente estuviera desconectado.

No se conecta con replicaSet en el URI (la base de datos de destino es una implementación de composición ). Como se mencionó, se usa SSL. Además, el tráfico se enruta a través de una puerta de enlace de AWS NAT. AWS NAT tiene un tiempo de espera inactivo de 5 minutos, pero estos problemas ocurrieron incluso durante un tráfico alto.

Algún problema para mí, ¿lo resolviste?

@ youth7 nope sin ideas. ¿También se está conectando a una implementación de redacción? Proporcione información adicional

¿Hay algo más que pueda hacer para intentar encontrar la causa de esto? ¿Alguna teoría que se pueda investigar?

@sommestad nada concreto. ¿Tiene algún registro de componer?

Probé el mongoose 4.7.2 pero experimenté el mismo problema después de 48 horas. En este punto, estoy paralizado en 4.4.11 que tiene sus propios problemas.

Instalé 4.7.2 en nuestro servidor de demostración con la esperanza de recopilar registros, pero ha estado funcionando durante dos semanas sin problemas. ¿Quizás el volumen de datos / conexiones / etc. provoque el problema?

Una vez que comienza a suceder, el código para reproducirlo es muy simple. Básicamente, la devolución de llamada de cualquier consulta simplemente desaparece:

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

Como mencioné, cuando esto sucede, mongoose.connection.readyState === CONNECTED

Extraño. Me refiero a los registros de mongodb: ¿puedes volcar los registros del servidor de mongodb de cuando ocurrió ese error?

Escaneé los registros de la base de datos cuando ocurrió esta falla y no pude ver nada fuera de lo común. No hubo problemas relacionados con la cantidad de conexiones, desconexiones, problemas de conexión o similares. También me comuniqué con Compose, quien tampoco pudo ver nada fuera del pedido.

@bendytree ¿Puede lanzar algo de volumen en ese servidor de demostración, tal vez usando una herramienta de prueba de carga HTTP?

@bendytree Además, ¿ocurre en todas las líneas de lanzamiento de Node (4.x, 6.xy 7.x)?

@ vkarpov15 ¿Podemos sacar 4.7.7 por la puerta para poder probar con el último controlador de MongoDB? 4.7.6 está anclado a una versión que parece obsoleta.

@ vkarpov15 66d559b19a86c70e30a8f083d03eb22566571b7e ¡Gracias! Lo probaré.

Lo mismo aquí, una réplica SSL aquí, después de unos minutos / horas, las solicitudes simplemente se detienen para devolver la llamada () (agregué algunos registros más detallados) y sin errores. Me actualicé a 4.7.7 anoche, pero todavía tengo el problema.

EDITAR: Traté de degradar a 4.7.0 pero fue peor (tener el problema en menos de 5 minutos). Estoy intentando 4.5.10.

@gierschv Nodo 6.9.x?

@djanowski no, todavía en 4.6 / 4.7.x LTS.

@djanowski Solo lo probé en el nodo 4.4.2

Mantener una lista de posibles duplicados de este problema:

  • N.º 4638
  • N.º 4690
  • N.º 4901

Para su información, 4.5.10 parece funcionar bien aquí, sin problemas durante las últimas 5 horas. También tuve un alto uso de CPU como el # 4690 en todas las máquinas que también parece estar solucionado por la degradación.

¿No hay nuevos desarrollos aquí? ¿Hay algo más que podamos hacer para ayudar a encontrar el error?

@sommestad , ¿todavía tienes este problema con la última mangosta?

No me he atrevido a hacer una actualización todavía, ya que básicamente rompió todo nuestro sistema la última vez (los cambios aparecen después de "algún" tiempo, por lo que es difícil de verificar sin tenerlo en funcionamiento durante mucho tiempo). Si tiene motivos para creer que está arreglado, ¿podríamos probar en una parte más aislada del sistema?

Editar: ping @varunjayaraman

Por mi parte, cambiamos a Node 6.xy LTS y actualizamos a la última versión de mangosta hace unas semanas, sin problemas desde entonces.

Actualizamos a Node 6.10.2, Mongoose 4.9.9 hace aproximadamente dos meses. Sigo viendo el mismo problema.

Nuestra cadena de conexión tiene el formato 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
  ...
});

Una vez que se detienen las devoluciones de llamada, nunca vuelven a comenzar. Si reiniciamos el proceso, se conecta bien.

Si hay algún dato que podamos recopilar para ayudar a solucionar problemas, nos encantaría ayudarlo.

Veo el mismo problema y puedo reproducirlo con el siguiente código (lo siento por el estilo rápido de maqueta / código).

Para simular el tiempo de espera de la conexión, bajé el poolSize a 1 y activé las solicitudes a 2 puntos finales (normal / lento) que se llaman en este orden:

  1. punto final normal (que devuelve resultados inmediatamente)
  2. punto final lento (que iniciará una consulta lenta)
  3. punto final normal (que esperará una conexión disponible ya que está en uso por 2)
  4. punto final normal (que se activará después de un minuto, cuando 2 y 3 resultaron en un MongoError / tiempo de espera de conexión)

Reproducir usando:

// 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 salida de la consola es la siguiente:

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

Superviso el readyState de la base de datos cada 10 segundos, lo que indica que la conexión aún está activa. No se activa ningún evento de error / tiempo de espera de Mongoose, y nunca se llama a la devolución de llamada de la solicitud 4 (se agota después de 60 segundos usando el paquete de solicitud). También puede probar visitando http: // localhost : 4000 / slow y luego http: // localhost : 4000 / normal.

Estoy usando la lógica de reconexión automática basada en https://team.goodeggs.com/reconnecting-to-mongodb-when-mongoose-connect-fails-at-startup-83ca8496ca02 , pero esto nunca se activa porque mongo no se desconecta.

En algunas ocasiones, no puedo reproducir cuando comento el código que crea algunos datos de prueba (básicamente, consulta una colección vacía)

Posibles problemas relacionados:

  • N.º 4789
  • N.º 4660

Cualquier ayuda me agradecería mucho.

Así que investigué un poco más y reescribí el script anterior para trabajar con node-mongodb-native sin usar mongoose. Los resultados son muy diferentes ahora:

La configuración de conexión y las solicitudes son las mismas:

  1. punto final normal (que devuelve resultados inmediatamente)
  2. punto final lento (que iniciará una consulta lenta)
  3. punto final normal (que esperará una conexión disponible ya que está en uso por 2)
  4. punto final normal (que se activará después de un minuto)

Código:

// 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 salida de la consola es la siguiente:

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' } ]

No se activan eventos de error / tiempo de espera de Mongo como el script anterior, pero las solicitudes 2 y 3 se resuelven correctamente, y la devolución de llamada de la solicitud 4 también se activa, en contraste con la secuencia de comandos que usa Mongoose.

Estoy tratando de descartar si se trata de un problema de Mongoose o de Mongo, por lo que se agradecería cualquier ayuda con respecto a la comparación de los resultados de estos scripts. ¿Quizás @ vkarpov15 pueda

@adriaanmeuris eso es extraño, aquí está el resultado que obtengo al ejecutar su primer 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
$ 

Mangosta permanece conectada. ¿Puede aclarar sus versiones de mongodb, node y mongoose?

gracias por probar! De hecho, Mongoose siempre permanece conectado, pero las devoluciones de llamada dejan de ocurrir (puede ver que la solicitud ingresa a Express, pero se agotará el tiempo de espera)

Estaba probando con:

  • nodo 6.8.1
  • mongodb 2.2.30
  • mangosta 4.11.5

Me he actualizado a:

  • nodo 6.8.1
  • mongodb 2.2.31
  • mangosta 4.11.7

Todavía puedo reproducir el problema localmente con el primer script que publiqué. Solo ocurre cuando se agota el tiempo de espera de una conexión de Mongo, por lo que es posible que desee cambiar la consulta que simula esto:

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

(No estoy seguro de si esto activará un tiempo de espera en cada máquina, por lo que podría ser necesario un cambio en esta consulta para reproducir)

Ya no vemos estos problemas después de actualizar a mongoose 4.10.4.
Parece un poco extraño, ya que otros parecen experimentar problemas, pero han tenido muchos servicios en ejecución durante un par de semanas sin que haya señales de que el problema regrese. 🤔

Sin embargo, vimos problemas de la misma manera que @adriaanmeuris anteriormente, por lo que podría ser que no hayamos experimentado tiempos de espera.

Ejecutando Node 8.

Seguimos viendo este problema periódicamente.

No podía reproducir con @adriaanmeuris 's de escritura , pero aquí es una versión modificada que se reproduce cada vez para mí:

bendytree / mangosta-problema-4513

Gracias @bendytree , con tu script también puedo reproducir:

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...

Es esencialmente el mismo script que el mío pero con un tiempo de espera más largo. Supongo que simular un tiempo de espera localmente lleva más / menos tiempo dependiendo de su configuración, ya que no todos pueden reproducir mi script (que usa un sueño de 10 segundos).

@ vkarpov15 ¿puedes probar este script actualizado? He probado y reproducido con mongoose 4.8.11.

Logré reproducir con socketTimeoutMS demasiado bajo. los

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

Los mensajes son indicativos del tiempo de espera del socket del controlador mongodb. El controlador mongodb se agota después de 30 segundos de forma predeterminada, pero su sistema operativo también tiene un tiempo de espera de socket que debe conocer, por lo que generalmente es una buena idea evitar consultas que demoran más de 15 segundos. Sin embargo, puede configurar el tiempo de espera del socket de mongodb usando la opción socketTimeoutMS :

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

Con esa opción establecida en 0, su script casi tiene éxito en mi máquina, la consulta de 10 minutos aún muere pero las devoluciones de llamada continúan

$ 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

Pero la consulta posterior tiene éxito.

De cualquier manera, la mangosta debería emitir un evento de tiempo de espera en este caso, así que solucionaremos ese problema.

Creo que está confundiendo connectTimeoutMS (con un valor predeterminado de 30000ms) con socketTimeoutMS (con un valor predeterminado de 360000ms).

Se recomienda configurar socketTimeoutMS en dos o tres veces la duración de la operación más lenta que se ejecuta a través del controlador, configurar socketTimeoutMS en 0 en realidad significa aplicar el valor de tiempo de espera de socket predeterminado del sistema operativo (más información en http://mongodb.github.io/ node-mongodb-native / 2.2 / reference / faq /).

Entonces, esta pregunta permanece: ¿por qué estas devoluciones de llamada dejan de ocurrir cuando se alcanza este límite (tiempo de espera fijo o predeterminado del sistema operativo)?

@ vkarpov15 con [email protected] , ahora veo el evento de tiempo de espera, gracias por eso. Sin embargo, sigue viendo este problema de devolución de llamada (utilizando el script original de

Hice algunas pruebas más y descubrí que esto también se puede reproducir sin Mongoose. Al leer http://mongodb.github.io/node-mongodb-native/2.2/reference/faq/ , descubrí que: Closing the socket forces a reconnect of the driver’s connection pool and introduces latency to any other operations which are queued up

Aunque mongoose.connection.readyState siempre es 1, intenté configurar autoReconnect en true , y volví a ejecutar el 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...

Entonces parece que las devoluciones de llamada están funcionando con autoReconnect establecido en verdadero 👍

Después de leer más este artículo: http://mongodb.github.io/node-mongodb-native/2.0/tutorials/connection_failures/ , me di cuenta de que la razón por la que las devoluciones de llamada no ocurren después del tiempo de espera, es porque la operación es almacenado en búfer mientras espera que el servidor se vuelva a conectar.

Así que intenté configurar autoReconnect nuevo en false + establecer bufferMaxEntries en 0 , esta es la salida:

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

Lo que explica por qué la devolución de llamada no se activó en la primera versión del @bendytree .

@ vkarpov15 Esto ahora nos deja con 2 preguntas:

  • por qué no se emiten eventos de conexión (esperaría ver un evento desconectado)
  • el evento de evento de tiempo de espera parece seguir emitiendo

aquí está el script actualizado (basado en el script de

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

Sí, tienes razón, tenemos que hacerlo mejor con esto. El evento timeout es necesario, pero no es suficiente para manejar este caso.

La razón por la que no se emite "desconectado" es que "tiempo de espera" significa que se agotó el tiempo de espera de un socket, no todo el grupo. Mongoose necesitaría saber cuántos sockets en el grupo todavía están vivos cuando ocurrió el evento de tiempo de espera, y necesitaría investigar más para averiguar cómo lo hacemos.

Investigaremos más por qué el evento de tiempo de espera se sigue emitiendo. Gracias por tu paciencia: +1:

Rastreé esto hasta un par de errores en el controlador mongodb: https://github.com/mongodb-js/mongodb-core/pull/215 . Tendremos que esperar a que se fusionen e implementen antes de que podamos parchearlo en mangosta.

@ vkarpov15 en mis propias pruebas estoy experimentando mongoose.connection.readyState nunca cambia. ¿Es este el mismo problema?

@ r3wt ese problema debería solucionarse en 4.11.13, ¿qué versión estás usando?

@ vkarpov15 Estaba usando 4.11.11 en el momento de mi publicación. Es desalentador que un mal comportamiento tan obvio pueda colarse en la producción de una biblioteca tan notable. quizás se necesiten mejores procedimientos de prueba. (No estoy tratando de ser irrespetuoso, sé lo difícil que es mantener proyectos de código abierto. A veces apesta)

@ vkarpov15 estoy desarrollando un complemento de mangosta. es necesario que el complemento se bloquee hasta que la conexión esté lista; de lo contrario, se bloquea esperando la conexión (por razones). el código que estoy usando para esperar que la conexión esté lista es el siguiente:

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

También intenté escuchar a través de mongoose.connnection.on('connected') pero, por desgracia, tampoco funcionó. en una instalación en blanco funcionó bien. pero junto con otros complementos de mangosta y cosas que escuchan los eventos, es donde ocurre el problema. connection.readyState permanece 0 para siempre y el evento conectado nunca se emite.

parece que require('mongoose') !== require('mongoose') en mi opinión. Espero que esto tenga sentido.

es decir:

  1. script de usuario require('mongoose')
  2. añadir un require('mongoose')
  3. complemento b require('mongoose')

el script de usuario y el complemento reciben la información sobre la conexión. pero el complemento b nunca lo hace. ¿tiene sentido?

@ r3wt de hecho, definitivamente es desalentador. Desde entonces, hemos agregado pruebas que realmente detienen / inician un servidor mongodb y afirman que mongoose hace lo correcto para evitar que esto suceda en el futuro. Lamentamos las molestias.

En cuanto al bloqueo hasta que la conexión esté lista, mongoose.connection.on('connected') debería funcionar. ¿Qué otros complementos estás usando? Intente ejecutar npm list | grep "mongoose" , su afirmación de que require('mongoose') !== require('mongoose') me hace pensar que tiene varias versiones de mangosta en alguna parte.

Desafortunadamente, tendré que retrasar este de nuevo porque el controlador mongodb no se libera. Lo siento y gracias por tu paciencia: +1: 🌴

@ vkarpov15 Solo quería agradecer mucho su trabajo en esta solución. Lo hemos estado usando en producción durante algunos meses con 0 problemas.

@bendytree, gracias por sus amables palabras y su paciencia para que este tema sea reprobado. El equipo de controladores de mongodb se ha visto abrumado trabajando para lanzar 3.0 a tiempo para la versión 3.6 de mongodb, por lo que aún no han podido lanzar una versión 2.x con esta solución, volveré a hacer ping. ¡Felices vacaciones!

¿Fue útil esta página
0 / 5 - 0 calificaciones