Mongoose: Обратные вызовы перестают происходить

Созданный на 13 сент. 2016  ·  53Комментарии  ·  Источник: Automattic/mongoose

Я использую mongoose в течение нескольких лет, но в последнее время у меня много проблем, когда кажется, что соединение пропадает.

Настройки соединения

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

Эта проблема

Мангуст просто перестает перезванивать. Он не выдает никаких ошибок, память / процессор в порядке на моем сервере и базе данных, и я могу подключиться с любого другого сервера. Перезагрузка всегда исправляет:

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

Версии

На mongoose 4.4.11 , mongoose.connection.readyState === UNAUTHORIZED при остановке обратных вызовов.

Поэтому я обновился до mongoose 4.6.0 но в течение 4 часов обратные вызовы снова прекратились, теперь с mongoose.connection.readyState === CONNECTED .

Любые идеи

Есть идеи, как я могу заставить это перестать происходить?

Я счастлив собрать дополнительную информацию об отладке, просто дайте мне знать, какую информацию получить.

underlying library issue

Самый полезный комментарий

К сожалению, придется снова отодвинуть это, потому что драйвер mongodb задерживается от выпуска. Приносим извинения и благодарим за терпение: +1: 🌴

Все 53 Комментарий

Вы подключаетесь к автономному кластеру, набору реплик или сегментированному кластеру? Кроме того, можете ли вы подтвердить, что используете SSL?

Последние несколько дней это было для меня огромной проблемой. Каждое запущенное приложение заканчивалось более медленным и медленным циклом обработки событий, что приводило к увеличению задержки во всей системе. Каждый раз перезапуск приложений Node решал проблему, и она возвращалась через несколько минут или часов работы.

Возврат к mongoose 4.5.10 решил проблему.

  • Подключение к набору реплик
  • Использование SSL

Аналогичная проблема здесь с 4.6.0, я не уверен, подключаюсь ли я к набору реплик или нет, но определенно использую SSL.

FWIW, 4.6.0 постоянно сбрасывал соединения. 4.5.10 это хорошо.

@djanowski, можете ли вы предоставить дополнительную информацию, например, как выглядит ваш код подключения и какая у вас версия mongodb?

@ vkarpov15 Я использовал MongoDB 3.2.6.

Варианты подключения:

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

Одиночные монго или несколько монго?

Несколько монго. Использование развертывания Compose Mongo.

Что еще я могу сделать, чтобы помочь в устранении этой проблемы? Это мешает нам перейти на более новые версии ...

Мне сложно выдвигать какие-либо реальные гипотезы, не видя вашего кода. Если вы подключаетесь к mongoose.connect я бы проверил значение mongoose.connection.readyState когда эта проблема начнется. Кроме того, я бы включил режим отладки мангуста, mongoose.set('debug', true) , который будет выводить сообщения отладки на консоль для каждой операции мангуста. Независимо от того, печатаются ли эти сообщения или нет, определенно поможет отладить.

@ vkarpov15 Это код, который я использую для подключения: https://gist.github.com/sommestad/c0c6a7fa4feaadf84ecbb59cc3432c90
Это часть частного модуля, отсюда и Gist.

_ (Причина использования модуля упаковки соединения заключается в том, что соединение время от времени повторно используется в других частных модулях NPM, что исторически плохо работало с мангустом.) _

У меня было включено ведение журнала состояния подключения, когда возникли эти проблемы, но само соединение, похоже, не изменилось (по крайней мере, никаких событий не было). Итак, не похоже, что клиент отключился.

Он не соединяется с replicaSet в URI (целевая база данных - это Compose Deployment ). Как уже упоминалось, используется SSL. Кроме того, трафик маршрутизируется через шлюз AWS NAT. AWS NAT имеет тайм-аут простоя 5 минут, но эти проблемы возникали даже при высоком трафике.

какая-то проблема для меня, вы ее решили?

@ youth7 нет идей. Вы также подключаетесь к развертыванию Compose? Пожалуйста, предоставьте дополнительную информацию

Что еще я могу сделать, чтобы попытаться найти причину этого? Любые теории, которые можно исследовать?

@sommestad ничего конкретного. Есть ли у вас логи от compose?

Я попробовал mongoose 4.7.2 но через 48 часов возникла та же проблема. На данный момент я подкован на 4.4.11 которого есть собственные проблемы.

Я установил 4.7.2 на наш демонстрационный сервер в надежде собрать логи, но он работал в течение двух недель без проблем. Может быть, проблема связана с объемом данных / подключений / и т. Д.?

Когда это начинает происходить, код для его воспроизведения очень прост. Обычно обратный вызов любого запроса просто исчезает:

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

Как я уже упоминал, когда это происходит, mongoose.connection.readyState === CONNECTED

Странный. Я имел в виду журналы mongodb - можете ли вы сбросить журналы сервера mongodb, когда произошла эта ошибка?

Я просмотрел журналы БД, когда произошел этот сбой, и не увидел ничего необычного. Не было проблем, связанных с количеством подключений, отключений, проблем с подключением и т.п. Я также обратился к Compose, который тоже не увидел ничего не в порядке.

@bendytree. Не могли бы вы

@bendytree Также, происходит ли это во всех линиях выпуска Node (4.x, 6.x и 7.x)?

@ vkarpov15 Можем ли мы выпустить 4.7.7, чтобы я мог протестировать последнюю версию драйвера MongoDB? 4.7.6 привязан к версии, которая кажется устаревшей.

@ vkarpov15 66d559b19a86c70e30a8f083d03eb22566571b7e Спасибо! Я попробую.

То же самое здесь, здесь реплика SSL, через несколько минут / часов запросы просто прекращаются на callback () (я добавил более подробное ведение журнала) и никаких ошибок. Прошлой ночью я обновился до 4.7.7, но проблема все еще не решена.

РЕДАКТИРОВАТЬ: я попытался перейти на версию 4.7.0, но это было хуже (проблема возникла менее чем за 5 минут). Пробую 4.5.10.

@gierschv Узел 6.9.x?

@djanowski нет, все еще на 4.6 / 4.7.x LTS.

@djanowski Я пробовал только на Node 4.4.2

Ведение списка возможных дубликатов этой проблемы:

  • # 4638
  • # 4690
  • # 4901

К вашему сведению, 4.5.10, похоже, здесь работает хорошо, никаких проблем за последние 5 часов. У меня также была высокая загрузка ЦП, например, # 4690 на всех машинах, что, похоже, также было исправлено с помощью перехода на более раннюю версию.

Здесь нет новых разработок? Что еще мы можем сделать, чтобы помочь найти ошибку?

@sommestad у вас все еще есть эта проблема с последней

Я еще не осмелился выполнить обновление, так как в прошлый раз оно в основном сломало всю нашу систему (изменения появляются через "некоторое" время, поэтому их сложно проверить, не запуская долгое время). Если у вас есть основания полагать, что это исправлено, мы могли бы попробовать более изолированную часть системы?

Изменить: пинг @varunjayaraman

Со своей стороны, мы перешли на Node 6.x LTS и обновились до последней версии mongoose несколько недель назад, с тех пор никаких проблем.

Мы обновились до Node 6.10.2, Mongoose 4.9.9 около двух месяцев назад. По-прежнему наблюдается та же проблема.

Наша строка подключения имеет формат 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
  ...
});

Как только обратные вызовы прекращаются, они никогда не запускаются снова. Если мы перезапустим процесс, он подключится нормально.

Если есть какие-то данные, которые мы можем собрать для устранения неполадок, мы будем рады вам помочь.

Я вижу ту же проблему и могу воспроизвести ее с помощью приведенного ниже кода (извините за быстрый макет / стиль кода).

Чтобы смоделировать тайм-аут соединения, я снизил размер poolSize до 1 и инициировал запросы к 2 конечным точкам (нормальный / медленный), которые вызываются в следующем порядке:

  1. нормальная конечная точка (которая немедленно возвращает результаты)
  2. медленная конечная точка (которая запустит медленный запрос)
  3. обычная конечная точка (которая будет ждать доступного соединения, так как она используется 2)
  4. нормальная конечная точка (которая сработает через минуту, когда 2 и 3 привели к таймауту MongoError / соединения)

Воспроизвести, используя:

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

Консольный вывод выглядит следующим образом:

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

Я отслеживаю состояние готовности базы данных каждые 10 секунд, что указывает на то, что соединение все еще активно. Нет триггеров ошибок / тайм-аута Mongoose, и обратный вызов запроса 4 никогда не вызывается (время ожидания истекает через 60 секунд с использованием пакета запроса). Вы также можете проверить, посетив http: // localhost : 4000 / slow, а затем http: // localhost : 4000 / normal.

Я использую логику автоматического переподключения на основе https://team.goodeggs.com/reconnecting-to-mongodb-when-mongoose-connect-fails-at-startup-83ca8496ca02 , но это никогда не срабатывает, поскольку mongo не отключается.

В некоторых случаях я не могу воспроизвести, когда я комментирую код, который создает некоторые тестовые данные (поэтому в основном запрашивает пустую коллекцию)

Возможные связанные проблемы:

  • # 4789
  • # 4660

Любая помощь была бы мне очень признательна.

Итак, я немного покопался и переписал приведенный выше скрипт для работы с node-mongodb-native без использования mongoose. Результаты сейчас совсем другие:

Настройки подключения и запросы такие же:

  1. нормальная конечная точка (которая немедленно возвращает результаты)
  2. медленная конечная точка (которая запустит медленный запрос)
  3. обычная конечная точка (которая будет ждать доступного соединения, так как она используется 2)
  4. нормальная конечная точка (которая сработает через минуту)

Код:

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

Консольный вывод выглядит следующим образом:

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

События ошибок / тайм-аута Mongo не запускаются, как в предыдущем сценарии, но запросы 2 и 3 разрешаются правильно, а также запускается обратный вызов запроса 4 - в отличие от сценария, который использует Mongoose.

Я пытаюсь исключить, является ли это проблемой Mongoose или Mongo, поэтому будет приветствоваться любая помощь в сравнении результатов этих скриптов. Может быть, @ vkarpov15 сможет

@adriaanmeuris , это странно, вот результат, который я получил после запуска вашего первого скрипта:

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

Мангуст остается на связи. Можете ли вы прояснить свои версии mongodb, node и mongoose?

спасибо за тестирование! Действительно, Mongoose всегда остается подключенным, но обратные вызовы перестают выполняться (вы можете видеть, что запрос поступает в Express, но у них будет тайм-аут)

Я тестировал:

  • узел 6.8.1
  • mongodb 2.2.30
  • мангуст 4.11.5

Я обновился до:

  • узел 6.8.1
  • mongodb 2.2.31
  • мангуст 4.11.7

Я все еще могу воспроизвести проблему локально с помощью первого опубликованного мной сценария. Это происходит только тогда, когда соединение Mongo истекает, поэтому вы можете изменить запрос, который имитирует это:

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

(Я не уверен, вызовет ли это тайм-аут на каждой машине, поэтому для воспроизведения может потребоваться изменение этого запроса)

Мы больше не видим этих проблем после обновления до mongoose 4.10.4.
Это кажется немного странным, так как другие, похоже, испытывают проблемы, но многие службы работали в течение нескольких недель без каких-либо признаков возвращения проблемы. 🤔

Однако мы действительно видели проблемы так же, как и ранее

Запуск узла 8.

Мы по-прежнему периодически сталкиваемся с этой проблемой.

Я не смог воспроизвести его с помощью сценария @adriaanmeuris , но вот модифицированная версия, которая воспроизводит его каждый раз для меня:

bendytree / мангуст-проблема-4513

Спасибо @bendytree , с вашим скриптом я тоже могу воспроизвести:

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

По сути, это тот же сценарий, что и мой, но с более длительным таймаутом. Я предполагаю, что имитация тайм-аута локально занимает больше / меньше времени в зависимости от вашей настройки, поскольку не все могут воспроизвести мой скрипт (который использует 10-секундный сон).

@ vkarpov15 можешь попробовать этот обновленный скрипт. Я тестировал и воспроизводил с помощью mongoose 4.8.11.

Мне удалось воспроизвести с помощью socketTimeoutMS . В

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

сообщения указывают на тайм-аут сокета драйвера mongodb. Драйвер mongodb по умолчанию отключается через 30 секунд, но в вашей ОС также есть время ожидания сокета, о котором вам нужно знать, поэтому обычно рекомендуется избегать запросов, которые занимают более 15 секунд. Однако вы можете настроить тайм-аут сокета mongodb, используя параметр socketTimeoutMS :

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

Если для этого параметра установлено значение 0, ваш сценарий почти успешно работает на моей машине, 10-минутный запрос все еще умирает, но обратные вызовы продолжаются

$ 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

Но последующий запрос выполняется успешно.

В любом случае мангуст должен генерировать событие тайм-аута, поэтому мы исправим эту проблему.

Я думаю, вы путаете connectTimeoutMS (со значением по умолчанию 30000 мс) с socketTimeoutMS (со значением по умолчанию 360000 мс).

Рекомендуется установить socketTimeoutMS в два-три раза больше длины самой медленной операции, которая выполняется через драйвер, установка socketTimeoutMS на 0 фактически означает применение значения тайм-аута сокета по умолчанию для операционной системы (дополнительная информация на http://mongodb.github.io/ узел-mongodb-native / 2.2 / ссылка / faq /).

Итак, остается вопрос: почему эти обратные вызовы перестают выполняться, когда достигается этот предел (фиксированный или тайм-аут ОС по умолчанию)?

@ vkarpov15 с [email protected] , теперь я вижу событие тайм-аута, спасибо за это. Однако вы все еще видите эту проблему обратного вызова (используя исходный сценарий @bendytree , для более быстрого тестирования вы можете установить LONG_QUERY_DURATION_IN_MS на 2000 и socketTimeoutMS на 1000)

Я провел еще несколько тестов и обнаружил, что это можно воспроизвести и без Mongoose. При чтении http://mongodb.github.io/node-mongodb-native/2.2/reference/faq/ я обнаружил, что: Closing the socket forces a reconnect of the driver’s connection pool and introduces latency to any other operations which are queued up

Несмотря на то, что mongoose.connection.readyState всегда равно 1, я попытался установить autoReconnect равным true , и перезапустил скрипт:

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

Похоже, что обратные вызовы работают с autoReconnect, установленным в true 👍

После дальнейшего чтения этой статьи: http://mongodb.github.io/node-mongodb-native/2.0/tutorials/connection_failures/ , я понял, что причина, по которой обратные вызовы не происходят после тайм-аута, заключается в том, что операция буферизуется в ожидании повторного подключения сервера.

Итак, я попытался установить autoReconnect обратно в false + установить bufferMaxEntries в 0 , вот результат:

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

Это объясняет, почему обратный вызов не сработал в первой версии скрипта @bendytree .

@ vkarpov15 Осталось 2 вопроса:

  • почему не генерируются события подключения (я ожидал бы увидеть отключенное событие)
  • событие события тайм-аута, кажется, продолжает излучать

вот обновленный скрипт (основан на скрипте @bendytree , протестирован с [email protected] и [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);

Да, ты прав, нам нужно лучше с этим справиться. Событие timeout необходимо, но его недостаточно для обработки этого случая.

Причина, по которой «отключен» не генерируется, заключается в том, что «тайм-аут» означает, что истекло время ожидания одного сокета, а не всего пула. Mongoose необходимо знать, сколько сокетов в пуле все еще живы, когда произошло событие тайм-аута, и мне нужно было бы больше исследовать, чтобы выяснить, как мы это делаем.

Мы рассмотрим подробнее, почему событие тайм-аута продолжает повторяться. Благодарим за терпение: +1:

Я отследил это до пары ошибок в драйвере mongodb: https://github.com/mongodb-js/mongodb-core/pull/215 . Придется подождать, пока это будет объединено и развернуто, прежде чем мы сможем подключить его к мангусту.

@ vkarpov15 в моем собственном тестировании я mongoose.connection.readyState никогда не меняется. это та же проблема?

@ r3wt эта проблема должна быть исправлена ​​в 4.11.13, какую версию вы используете?

@ vkarpov15 На момент

@ vkarpov15 Я разрабатываю плагин мангуста. необходимо, чтобы плагин заблокировался до тех пор, пока соединение не будет готово, иначе он зайдет в тупик в ожидании соединения (по причинам). код, который я использую, чтобы дождаться готовности соединения, следующий:

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

Я также пробовал слушать через mongoose.connnection.on('connected') но, увы, это тоже не сработало. на пустой установке все работало нормально. но в сочетании с другими плагинами мангуста и прочими вещами, которые отслеживают события, именно здесь возникает проблема. connection.readyState всегда остается равным 0, и связанное событие никогда не генерируется.

похоже, что require('mongoose') !== require('mongoose') на мой взгляд. Надеюсь, это имеет смысл.

то есть:

  1. пользовательский скрипт require('mongoose')
  2. плагин require('mongoose')
  3. плагин b require('mongoose')

пользовательский скрипт и плагин получают информацию о соединении. но плагин b никогда этого не делает. имеет ли это смысл?

@ r3wt действительно разочаровывает. С тех пор мы добавили тесты, которые фактически останавливают / запускают сервер mongodb и утверждают, что mongoose делает правильные вещи, чтобы этого не произошло в будущем. Приносим извинения за беспокойство.

Что касается блокировки до готовности соединения, mongoose.connection.on('connected') должно работать. Какие еще плагины вы используете? Попробуйте запустить npm list | grep "mongoose" , ваше утверждение, что require('mongoose') !== require('mongoose') заставляет меня думать, что у вас где-то есть несколько версий мангуста.

К сожалению, придется снова отодвинуть это, потому что драйвер mongodb задерживается от выпуска. Приносим извинения и благодарим за терпение: +1: 🌴

@ vkarpov15 Просто хотел поблагодарить вас за работу над этим исправлением. Мы используем его в продакшене уже несколько месяцев без проблем.

@bendytree благодарит вас за добрые слова и терпение при

Была ли эта страница полезной?
0 / 5 - 0 рейтинги