Mongoose: Callbacks param de acontecer

Criado em 13 set. 2016  ·  53Comentários  ·  Fonte: Automattic/mongoose

Tenho usado mongoose por alguns anos, mas ultimamente estou tendo muitos problemas onde a conexão parece ter desaparecido.

Configurações de conexão

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

O problema

Mongoose simplesmente para de ligar de volta. Ele não emite nenhum erro, a memória / cpu está bem no meu servidor e db e posso me conectar de qualquer outro servidor. Reiniciar sempre corrige:

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

Versões

Em mongoose 4.4.11 , mongoose.connection.readyState === UNAUTHORIZED quando os callbacks param.

Então, eu atualizei para mongoose 4.6.0 mas em 4 horas os retornos de chamada pararam novamente, agora com mongoose.connection.readyState === CONNECTED .

Alguma ideia

Alguma ideia de como posso fazer com que isso pare de acontecer?

Estou feliz em reunir mais informações de depuração, apenas me diga quais informações devo obter.

underlying library issue

Comentários muito úteis

Infelizmente, vou ter que empurrar este de volta porque o driver mongodb está impedido de ser liberado. Desculpe por isso e obrigado pela sua paciência: +1: 🌴

Todos 53 comentários

Você está se conectando a um cluster autônomo, conjunto de réplicas ou fragmentado? Além disso, você pode confirmar que está usando SSL?

Isso tem sido um grande problema para mim nos últimos dias. Cada aplicativo em execução terminava com um loop de eventos cada vez mais lento, aumentando a latência em todo o sistema. Reiniciar os aplicativos do Node sempre resolveu o problema, voltando depois de minutos ou horas de operação.

Reverter para o mangusto 4.5.10 resolveu o problema.

  • Conectando ao conjunto de réplicas
  • Usando SSL

Problema semelhante aqui com 4.6.0, não tenho certeza se estou me conectando a um conjunto de réplicas ou não, mas definitivamente usando SSL.

FWIW, 4.6.0 estava perdendo conexões constantemente. 4.5.10 é bom.

@djanowski você pode fornecer mais informações, como a aparência do seu código de conexão e qual versão do mongodb você possui?

@ vkarpov15 Eu estava usando o MongoDB 3.2.6.

Opções de conexão:

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

Mongos únicos ou vários mongos?

Vários mongos. Usando uma implantação do Compose Mongo.

Posso fazer mais alguma coisa para ajudar a solucionar isso? Isso está nos impedindo de mudar para versões mais recentes ...

É difícil para mim fazer qualquer hipótese real sem ver seu código. Se você estiver se conectando com mongoose.connect , verificaria o valor de mongoose.connection.readyState quando esse problema começar a acontecer. Além disso, eu habilitaria o modo de depuração do mangusto, mongoose.set('debug', true) , que imprimirá mensagens de depuração no console para cada operação do mangusto. Se essas mensagens estão sendo impressas ou não, definitivamente ajudaria na depuração.

@ vkarpov15 Este é o código que estou usando para me conectar: https://gist.github.com/sommestad/c0c6a7fa4feaadf84ecbb59cc3432c90
Faz parte de um módulo privado, daí o Gist.

_ (O motivo do módulo de quebra de conexão é que a conexão é ocasionalmente reutilizada em outros módulos NPM privados, que não funcionaram bem com o mangusto historicamente.) _

Eu tinha o registro do estado da conexão habilitado quando esses problemas ocorreram, mas a conexão em si não parecia mudar (pelo menos nenhum evento foi emitido). Portanto, não parecia que o cliente estava desconectado.

Ele não está se conectando a replicaSet no URI (o banco de dados de destino é um Compose Deployment ). Conforme mencionado, SSL é usado. Além disso, o tráfego é roteado por meio de um AWS NAT Gateway. O AWS NAT tem um tempo limite de inatividade de 5 minutos, mas esses problemas aconteciam mesmo durante tráfego intenso.

algum problema pra mim, você resolveu?

@ youth7 não, sem idéias. Você também está se conectando a uma implantação composta? Forneça informações adicionais

Posso fazer mais alguma coisa para tentar encontrar a causa disso? Alguma teoria que pode ser investigada?

@sommestad nada de concreto. Você tem algum registro do compose?

Tentei mongoose 4.7.2 mas tive o mesmo problema após 48 horas. Neste ponto, estou paralisado em 4.4.11 que tem seus próprios problemas.

Instalei 4.7.2 em nosso servidor de demonstração na esperança de coletar logs, mas ele está funcionando há duas semanas sem problemas. Talvez o volume de dados / conexões / etc provoque o problema?

Assim que começa a acontecer, o código para reproduzi-lo é muito simples. Basicamente, o retorno de chamada de qualquer consulta simplesmente desaparece:

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

Como mencionei, quando isso estiver acontecendo, mongoose.connection.readyState === CONNECTED

Esquisito. Eu quis dizer logs do mongodb - você pode descartar os logs do servidor mongodb de quando o erro aconteceu?

Eu fiz a varredura dos logs do banco de dados quando essa falha aconteceu e não consegui ver nada fora do comum. Não houve problemas relacionados ao número de conexões, desconexões, problemas de conexão ou semelhantes. Também procurei Compose, que também não conseguiu ver nada fora do pedido.

@bendytree Você pode jogar algum volume naquele servidor de demonstração, talvez usando uma ferramenta de teste de carga HTTP?

@bendytree Além disso, isso acontece em todas as linhas de lançamento do Node (4.x, 6.xe 7.x)?

@ vkarpov15 Podemos

@ vkarpov15 66d559b19a86c70e30a8f083d03eb22566571b7e Obrigado! Vou dar uma chance.

O mesmo aqui, uma réplica SSL aqui, depois de alguns minutos / horas, as solicitações param para callback () (adicionei um pouco mais de registro detalhado) e sem erros. Eu atualizei para 4.7.7 na noite passada, mas ainda tenho o problema.

EDIT: Eu tentei fazer o downgrade para 4.7.0, mas foi pior (tendo o problema em menos de 5 minutos). Estou tentando 4.5.10.

@gierschv Node 6.9.x?

@djanowski não, ainda em 4.6 / 4.7.x LTS.

@djanowski Eu só tentei no Nó 4.4.2

Manter uma lista de prováveis ​​repetições deste problema:

  • # 4638
  • # 4690
  • # 4901

Para sua informação, 4.5.10 parece funcionar bem aqui, sem problemas nas últimas 5 horas. Eu também tive um alto uso da CPU, como # 4690 em todas as máquinas, que também parece ter sido corrigido pelo downgrade.

Não há novos desenvolvimentos aqui? Podemos fazer mais alguma coisa para ajudar a encontrar o bug?

@sommestad , você ainda tem esse problema com o mangusto mais recente?

Não ousei fazer uma atualização ainda, pois basicamente quebrou todo o nosso sistema da última vez (as alterações aparecem depois de "algum" tempo, por isso é difícil verificar sem ter que funcionar por muito tempo). Se você tem motivos para acreditar que está consertado, podemos tentar uma parte mais isolada do sistema.

Editar: ping @varunjayaraman

Do meu lado, mudamos para o Node 6.x LTS e atualizamos para a última versão do mangusto há algumas semanas, sem problemas desde então.

Atualizamos para o Node 6.10.2, Mongoose 4.9.9 há cerca de dois meses. Ainda vendo o mesmo problema.

Nossa string de conexão tem o 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
  ...
});

Uma vez que os callbacks param, eles nunca começam novamente. Se reiniciarmos o processo, ele se conecta bem.

Se houver algum dado que possamos coletar para ajudar a solucionar o problema, adoraríamos ajudar.

Estou tendo o mesmo problema e posso reproduzi-lo com o código abaixo (desculpe pelo estilo de maquete / código rápido).

Para simular o tempo limite de conexão, reduzi o poolSize para 1 e acionei as solicitações para 2 endpoints (normal / lento), que são chamados nesta ordem:

  1. endpoint normal (que retorna resultados imediatamente)
  2. endpoint lento (que iniciará uma consulta lenta)
  3. endpoint normal (que irá esperar por uma conexão disponível, uma vez que está em uso por 2)
  4. endpoint normal (que será acionado após um minuto, quando 2 e 3 resultaram em um MongoError / tempo limite de conexão)

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

A saída do console é a seguinte:

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

Eu monitoro o readyState do banco de dados a cada 10 segundos, o que indica que a conexão ainda está ativa. Nenhum evento de erro / tempo limite do Mongoose é acionado e o retorno de chamada da solicitação 4 nunca é chamado (ele expira após 60s usando o pacote de solicitação). Você também pode testar visitando http: // localhost : 4000 / lento e em seguida http: // localhost : 4000 / normal.

Estou usando a lógica de reconexão automática com base em https://team.goodeggs.com/reconnecting-to-mongodb-when-mongoose-connect-fails-at-startup-83ca8496ca02 , mas isso nunca é acionado porque o mongo não se desconecta.

Em algumas ocasiões, não consigo reproduzir quando comento o código que cria alguns dados de teste (basicamente, consultar uma coleção vazia)

Possíveis problemas relacionados:

  • # 4789
  • # 4660

Qualquer ajuda seria muito apreciada.

Então, eu fiz mais pesquisas e reescrevi o script acima para trabalhar com node-mongodb-native sem usar mongoose. Os resultados são muito diferentes agora:

As configurações e solicitações de conexão são as mesmas:

  1. endpoint normal (que retorna resultados imediatamente)
  2. endpoint lento (que iniciará uma consulta lenta)
  3. endpoint normal (que irá esperar por uma conexão disponível, uma vez que está em uso por 2)
  4. endpoint normal (que irá disparar após um 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();
  }
);

A saída do console é a seguinte:

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

Nenhum evento de erro / tempo limite do Mongo é acionado como o script anterior, mas as solicitações 2 e 3 são resolvidas corretamente e o retorno de chamada da solicitação 4 também é acionado - em contraste com o script que está usando o Mongoose.

Estou tentando descartar se este é um problema do Mongoose vs. Mongo - portanto, qualquer ajuda em relação à comparação dos resultados desses scripts seria apreciada. Talvez @ vkarpov15 possa

@adriaanmeuris isso é estranho, aqui está o resultado que recebo ao executar seu primeiro 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
$ 

O Mongoose permanece conectado. Você pode esclarecer suas versões de mongodb, node e mongoose?

obrigado por testar! Na verdade, o Mongoose sempre permanece conectado, mas os retornos de chamada param de acontecer (você pode ver a solicitação chegando ao Express, mas eles expirarão)

Eu estava testando com:

  • nó 6.8.1
  • mongodb 2.2.30
  • mangusto 4.11.5

Eu atualizei para:

  • nó 6.8.1
  • mongodb 2.2.31
  • mangusto 4.11.7

Ainda posso reproduzir o problema localmente com o primeiro script que postei. Ocorre apenas quando uma conexão Mongo atinge o tempo limite, então você pode querer alterar a consulta que simula isso:

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

(Não tenho certeza se isso irá disparar um tempo limite em todas as máquinas, portanto, pode ser necessário alterar esta consulta para reproduzi-la)

Não estamos mais vendo esses problemas depois de atualizar para o mongoose 4.10.4.
Parece um pouco estranho, já que outras pessoas parecem ter problemas, mas muitos serviços estão em execução há algumas semanas sem que nenhum sinal de problema volte. 🤔

No entanto, vimos problemas da mesma forma que @adriaanmeuris anteriormente, então pode ser que não tenhamos tido nenhum tempo limite.

Executando o Nó 8.

Ainda estamos vendo esse problema periodicamente.

Não consegui reproduzi-lo com o script de @adriaanmeuris , mas aqui está uma versão modificada que o reproduz todas as vezes para mim:

bendytree / mongoose-issue-4513

Obrigado @bendytree , com seu script também posso reproduzir:

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

É essencialmente o mesmo script que o meu, mas com uma duração de tempo limite maior. Eu acho que simular um tempo limite localmente leva mais / menos tempo dependendo da sua configuração, já que nem todos poderiam reproduzir com meu script (que usa um sono de 10s).

@ vkarpov15 você pode tentar este script atualizado. Eu testei e reproduzi com o mangusto 4.8.11.

Consegui reproduzir com @bendytree , este é um caso de socketTimeoutMS muito baixo. o

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

mensagens são indicativas do tempo limite do soquete do driver mongodb. O driver mongodb expira após 30 segundos por padrão, mas seu sistema operacional também tem um tempo limite de soquete do qual você precisa estar ciente, portanto, geralmente é uma boa ideia evitar consultas que demorem mais de 15 segundos. No entanto, você pode configurar o tempo limite do soquete do mongodb usando a opção socketTimeoutMS :

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

Com essa opção definida como 0, seu script quase teve sucesso na minha máquina, a consulta de 10 minutos ainda morre, mas os retornos de chamada continuam

$ 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

Mas a consulta subsequente é bem-sucedida.

De qualquer forma, o mongoose deve emitir um evento de tempo limite neste caso, portanto, corrigiremos esse problema.

Eu acho que você está confundindo connectTimeoutMS (com um padrão de 30000ms) com socketTimeoutMS (com um padrão de 360000ms).

É aconselhável definir socketTimeoutMS para duas a três vezes a duração da operação mais lenta que é executada pelo driver, definir socketTimeoutMS para 0 na verdade significa aplicar o valor de tempo limite de soquete padrão do sistema operacional (mais informações em http://mongodb.github.io/ node-mongodb-native / 2.2 / reference / faq /).

Portanto, a pergunta permanece: por que esses retornos de chamada param de acontecer quando esse limite (tempo limite fixo ou padrão do sistema operacional) é atingido?

@ vkarpov15 com [email protected] , agora vejo o evento de tempo limite, obrigado por isso. No entanto, você ainda vê este problema de retorno de chamada (usando o script original de @bendytree , para testes mais rápidos você pode definir LONG_QUERY_DURATION_IN_MS para 2000 e socketTimeoutMS para 1000)

Eu fiz mais alguns testes e descobri que isso pode ser reproduzido sem o Mongoose também. Ao ler http://mongodb.github.io/node-mongodb-native/2.2/reference/faq/ , descobri que: Closing the socket forces a reconnect of the driver’s connection pool and introduces latency to any other operations which are queued up

Mesmo que mongoose.connection.readyState seja sempre 1, tentei definir autoReconnect como true e executei novamente o 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...

Portanto, parece que os callbacks estão funcionando com o autoReconnect definido como verdadeiro 👍

Depois de ler este artigo: http://mongodb.github.io/node-mongodb-native/2.0/tutorials/connection_failures/ , percebi que a razão pela qual os callbacks não acontecem após o tempo limite é porque a operação é armazenado em buffer enquanto aguarda a reconexão do servidor.

Então eu tentei definir autoReconnect volta para false + set bufferMaxEntries para 0 , este é o resultado:

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

O que explica por que o retorno de chamada não foi acionado na primeira versão do script de

@ vkarpov15 Isso agora nos deixa com 2 perguntas:

  • por que não há eventos de conexão emitidos (eu esperaria ver um evento desconectado)
  • o evento de tempo limite parece continuar emitindo

aqui está o script atualizado (baseado no script de @bendytree , testado com [email protected] e [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);

Sim, você está certo, precisamos fazer melhor sobre isso. O evento timeout é necessário, mas não é realmente suficiente para lidar com este caso.

O motivo pelo qual 'desconectado' não é emitido é que 'tempo limite' significa que um soquete expirou, não o pool inteiro. O Mongoose precisaria saber quantos soquetes no pool ainda estavam ativos quando o evento de tempo limite ocorresse, e eu precisaria investigar mais para descobrir como fazemos isso.

Investigará mais por que o evento de tempo limite continua reemitindo. Obrigado pela sua paciência: +1:

Eu rastreei alguns bugs no driver mongodb: https://github.com/mongodb-js/mongodb-core/pull/215 . Teremos que esperar que isso seja mesclado e implantado antes de podermos consertá-lo no mangusto.

@ vkarpov15 em meus próprios testes, estou experimentando mongoose.connection.readyState nunca muda. este é o mesmo problema?

@ r3wt esse problema deve ser corrigido na versão 4.11.13, qual versão você está usando?

@ vkarpov15 Eu estava usando o 4.11.11 no momento da minha postagem. é desanimador que tal comportamento óbvio possa se infiltrar na produção de uma biblioteca tão notável. talvez alguns procedimentos de teste melhores sejam necessários. (Não estou tentando ser desrespeitoso, sei como é difícil manter projetos de código aberto. Às vezes é uma merda)

@ vkarpov15 estou desenvolvendo um plugin do mongoose. é necessário que o plug-in bloqueie até que a conexão esteja pronta, caso contrário, ele bloqueia ao aguardar a conexão (por motivos). o código que estou usando para esperar a conexão ficar pronta é o seguinte:

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

Também tentei ouvir via mongoose.connnection.on('connected') mas também não funcionou. em uma instalação em branco funcionou bem. mas em conjunto com outros plug-ins do mongoose e outras funções que detectam os eventos, é onde o problema ocorre. connection.readyState permanece 0 para sempre e o evento conectado nunca é emitido.

parece que require('mongoose') !== require('mongoose') na minha opinião. Eu espero que isto faça sentido.

ie:

  1. script do usuário require('mongoose')
  2. plugin de require('mongoose')
  3. plugin b require('mongoose')

o script do usuário e o plugin recebem as informações sobre a conexão. mas o plugin b nunca o faz. isso faz sentido?

@ r3wt na verdade, é definitivamente desanimador. Desde então, adicionamos testes que realmente param / iniciam um servidor mongodb e afirmam que o mongoose faz a coisa certa para evitar que isso aconteça no futuro. Lamentamos o problema.

Quanto ao bloqueio até que a conexão esteja pronta, mongoose.connection.on('connected') deve funcionar. Que outros plugins você está usando? Tente executar npm list | grep "mongoose" , sua afirmação de que require('mongoose') !== require('mongoose') me faz pensar que você tem várias versões do mangusto em algum lugar.

Infelizmente, vou ter que empurrar este de volta porque o driver mongodb está impedido de ser liberado. Desculpe por isso e obrigado pela sua paciência: +1: 🌴

@ vkarpov15 Só queria agradecer seu trabalho nessa correção. Estamos usando em produção há alguns meses, sem problemas.

@bendytree, obrigado por suas palavras gentis e sua paciência em reproduzir este problema. A equipe de drivers do mongodb está sobrecarregada de trabalho no lançamento do 3.0 a tempo do lançamento do mongodb 3.6, então eles ainda não foram capazes de lançar um lançamento do 2.x com esta correção. Vou fazer um ping novamente. Boas festas!

Esta página foi útil?
0 / 5 - 0 avaliações