Mongoose: Rückrufe sind nicht mehr möglich

Erstellt am 13. Sept. 2016  ·  53Kommentare  ·  Quelle: Automattic/mongoose

Ich benutze mongoose seit ein paar Jahren, aber in letzter Zeit habe ich viele Probleme, wo die Verbindung zu verschwinden scheint.

Verbindungseinstellungen

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

Das Problem

Mongoose hört einfach auf, zurückzurufen. Es gibt keine Fehler aus, Speicher / CPU ist auf meinem Server und meiner Datenbank in Ordnung, und ich kann von jedem anderen Server aus eine Verbindung herstellen. Neustart behebt es immer:

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

Versionen

Am mongoose 4.4.11 , mongoose.connection.readyState === UNAUTHORIZED wenn Rückrufe aufhören.

Also habe ich auf mongoose 4.6.0 aktualisiert , aber innerhalb von 4 Stunden hörten die Rückrufe wieder auf, jetzt mit mongoose.connection.readyState === CONNECTED .

Irgendwelche Ideen

Irgendwelche Ideen, wie ich das verhindern kann?

Ich freue mich, weitere Debugging-Informationen zu sammeln, lassen Sie mich einfach wissen, welche Informationen ich erhalten soll.

underlying library issue

Hilfreichster Kommentar

Leider muss ich diesen wieder zurückschieben, weil der mongodb-Treiber daran gehindert wird, ihn freizugeben. Tut mir leid und danke für deine Geduld :+1: 🌴

Alle 53 Kommentare

Stellen Sie eine Verbindung zu einem Standalone-, Replikat- oder Shard-Cluster her? Können Sie auch bestätigen, dass Sie SSL verwenden?

Das war in den letzten Tagen ein riesiges Problem für mich. Jede laufende Anwendung endete mit einer langsameren und langsameren Ereignisschleife, was zu einer zunehmenden Latenz im gesamten System führte. Durch Neustarten der Node-Anwendungen wurde das Problem jedes Mal behoben, wobei es entweder nach Minuten oder Stunden des Betriebs zurückkehrte.

Die Rückkehr zu Mungo 4.5.10 hat das Problem behoben.

  • Verbinden mit Replikat-Set
  • SSL verwenden

Ähnliches Problem hier mit 4.6.0, ich bin mir nicht sicher, ob ich eine Verbindung zu einem Replikatsatz herstelle oder nicht, aber definitiv mit SSL.

FWIW, 4.6.0 brach ständig Verbindungen ab. 4.5.10 ist gut.

@djanowski können Sie weitere Informationen bereitstellen, z. B. wie Ihr Verbindungscode aussieht und welche Version von mongodb Sie haben?

@vkarpov15 Ich habe MongoDB 3.2.6 verwendet.

Anschlussmöglichkeiten:

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

Einzelne Mongos oder mehrere Mongos?

Mehrere Mongos. Verwenden einer Compose Mongo-Bereitstellung.

Kann ich sonst noch etwas tun, um bei der Fehlersuche zu helfen? Dies hindert uns daran, auf neuere Versionen umzusteigen...

Es ist schwer für mich, eine echte Hypothese aufzustellen, ohne Ihren Code zu sehen. Wenn Sie sich mit mongoose.connect würde ich den Wert von mongoose.connection.readyState überprüfen, wenn dieses Problem auftritt. Außerdem würde ich den Mungo-Debug-Modus mongoose.set('debug', true) aktivieren, der bei jeder Mungo-Operation Debug-Meldungen an die Konsole ausgibt. Ob diese Nachrichten gedruckt werden oder nicht, würde definitiv beim Debuggen helfen.

@vkarpov15 Dies ist der Code, den ich verwende, um eine Verbindung https://gist.github.com/sommestad/c0c6a7fa4feaadf84ecbb59cc3432c90
Es ist Teil eines privaten Moduls, daher das Wesentliche.

_(Der Grund für das Connection Wrapping-Modul ist, dass die Verbindung gelegentlich in anderen privaten NPM-Modulen wiederverwendet wird, was bei Mungos in der Vergangenheit nicht gut funktioniert hat.)_

Ich hatte die Protokollierung des Verbindungsstatus aktiviert, als diese Probleme auftraten, aber die Verbindung selbst schien sich nicht zu ändern (zumindest keine Ereignisse ausgegeben). Es sah also nicht so aus, als ob der Client getrennt wurde.

Es stellt keine Verbindung mit einem replicaSet im URI her (die Zieldatenbank ist ein Compose Deployment ). Wie bereits erwähnt, wird SSL verwendet. Außerdem wird der Datenverkehr über ein AWS NAT-Gateway geleitet. AWS NAT hat eine Leerlaufzeitüberschreitung von 5 Minuten, aber diese Probleme traten auch bei hohem Datenverkehr auf.

ein Problem für mich, hast du es gelöst?

@jugend7 nein keine ideen. Stellen Sie auch eine Verbindung zu einer Compose-Bereitstellung her? Bitte geben Sie zusätzliche Informationen an

Kann ich noch irgendwas tun um die Ursache zu finden? Gibt es Theorien, die untersucht werden können?

@sommestad nichts konkretes. Hast du irgendwelche Protokolle von Compose?

Ich habe das mongoose 4.7.2 ausprobiert, aber nach 48 Stunden das gleiche Problem festgestellt. An diesem Punkt bin ich von 4.4.11 gelähmt, was seine eigenen Probleme hat.

Ich habe 4.7.2 auf unserem Demoserver installiert, in der Hoffnung, Protokolle zu sammeln, aber es läuft seit zwei Wochen ohne Probleme. Vielleicht provoziert das Volumen der Daten/Verbindungen/usw. das Problem?

Sobald es passiert, ist der Code, um es zu reproduzieren, sehr einfach. Im Grunde verschwindet der Rückruf jeder Abfrage einfach:

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

Wie ich bereits erwähnt habe, wenn das passiert, mongoose.connection.readyState === CONNECTED

Seltsam. Ich meinte Mongodb-Protokolle - können Sie die Mongodb-Serverprotokolle ablegen, als dieser Fehler aufgetreten ist?

Ich habe die DB-Protokolle gescannt, als dieser Fehler aufgetreten ist, und konnte nichts Außergewöhnliches feststellen. Es gab keine Probleme in Bezug auf die Anzahl der Verbindungen, Verbindungsabbrüche, Verbindungsprobleme oder ähnliches. Ich habe auch Compose kontaktiert, der auch nichts außerhalb der Reihenfolge sehen konnte.

@bendytree Können Sie etwas Volumen auf diesen

@bendytree Tritt es auch auf allen Release-Linien von Node (4.x, 6.x und 7.x) auf?

@vkarpov15 Können wir 4.7.7 aus der Tür holen, damit ich mit dem neuesten MongoDB-Treiber testen kann? 4.7.6 ist an eine Version angeheftet, die veraltet zu sein scheint.

@vkarpov15 66d559b19a86c70e30a8f083d03eb22566571b7e Danke! Ich werde es versuchen.

Das gleiche hier, ein SSL-Replikat hier, nach wenigen Minuten / Stunden stoppen die Anfragen einfach an callback() (ich habe mehr ausführliche Protokollierung hinzugefügt) und keine Fehler. Ich habe gestern Abend auf 4.7.7 aktualisiert, habe aber immer noch das Problem.

BEARBEITEN: Ich habe versucht, auf 4.7.0 herunterzustufen, aber es war am schlimmsten (das Problem in weniger als 5 Minuten). Ich versuche 4.5.10.

@gierschv Node 6.9.x?

@djanowski nein, noch auf 4.6/4.7.x LTS.

@djanowski Ich habe es nur auf Node 4.4.2 ausprobiert

Führen Sie eine Liste wahrscheinlicher Duplikate dieses Problems:

  • #4638
  • #4690
  • #4901

Zu Ihrer Information, 4.5.10 scheint hier gut zu funktionieren, in den letzten 5 Stunden keine Probleme. Ich hatte auch eine hohe CPU-Auslastung wie #4690 auf allen Maschinen, die auch durch das Downgrade behoben zu sein scheint.

Keine neuen Entwicklungen hier? Können wir noch etwas tun, um den Fehler zu finden?

@sommestad hast du dieses Problem mit dem neuesten Mungo immer noch?

Ich habe es noch nicht gewagt, ein Upgrade durchzuführen, da es beim letzten Mal im Grunde unser gesamtes System kaputt gemacht hat (die Änderungen erscheinen nach "einer" Zeit, daher ist es schwer zu überprüfen, ohne es lange zu haben). Wenn Sie Grund zu der Annahme haben, dass es behoben ist, könnten wir einen isolierteren Teil des Systems ausprobieren?

Edit: ping @varunjayaraman

Auf meiner Seite haben wir vor einigen Wochen auf Node 6.x LTS umgestellt und auf die letzte Mungo-Version aktualisiert, seitdem kein Problem.

Wir haben vor etwa zwei Monaten auf Node 6.10.2, Mongoose 4.9.9 aktualisiert. Sehe immer noch das gleiche Problem.

Unsere Verbindungszeichenfolge hat das Format mongodb://username:[email protected]:1234,db2.com:2345/db-name?ssl=true .

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

Sobald die Rückrufe aufhören, beginnen sie nie wieder. Wenn wir den Prozess neu starten, verbindet er sich gut.

Wenn es Daten gibt, die wir zur Fehlerbehebung sammeln können, helfen wir Ihnen gerne weiter.

Ich sehe das gleiche Problem und kann es mit dem folgenden Code reproduzieren (sorry für das schnelle Mock-up / den Code-Stil).

Um das Verbindungs-Timeout zu simulieren, habe ich die poolSize auf 1 gesenkt und Anfragen an 2 Endpunkte (normal / langsam) getriggert, die in dieser Reihenfolge aufgerufen werden:

  1. normaler Endpunkt (der Ergebnisse sofort zurückgibt)
  2. langsamer Endpunkt (der eine langsame Abfrage startet)
  3. normaler Endpunkt (der auf eine verfügbare Verbindung wartet, da er von 2 verwendet wird)
  4. normaler Endpunkt (wird nach einer Minute ausgelöst, wenn 2 und 3 zu einem MongoError / Verbindungs-Timeout führten)

Reproduzieren Sie mit:

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

Die Konsolenausgabe ist wie folgt:

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

Ich überwache alle 10 Sekunden den readyState der Datenbank, was anzeigt, dass die Verbindung noch aktiv ist. Es wird kein Mongoose-Fehler-/Timeout-Ereignis ausgelöst, und der Rückruf von Anforderung 4 wird nie aufgerufen (es wird nach 60s mit dem Anforderungspaket eine Zeitüberschreitung vorgenommen). Sie können auch testen, indem Sie http://localhost :4000/slow und dann http://localhost :4000/normal besuchen.

Ich verwende die Logik für die automatische Wiederverbindung basierend auf https://team.goodeggs.com/reconnecting-to-mongodb-when-mongoose-connect-fails-at-startup-83ca8496ca02 , aber dies wird nie ausgelöst, da Mongo die Verbindung nicht trennt.

Manchmal kann ich nicht reproduzieren, wenn ich den Code kommentiere, der einige Testdaten erstellt (also im Grunde eine leere Sammlung abfrage).

Mögliche verwandte Probleme:

  • #4789
  • #4660

Jede Hilfe wäre mir sehr dankbar.

Also habe ich weiter gegraben und das obige Skript neu geschrieben, um mit node-mongodb-native zu arbeiten, ohne Mungo zu verwenden. Die Ergebnisse sind jetzt ganz anders:

Verbindungseinstellungen und Anfragen sind gleich:

  1. normaler Endpunkt (der Ergebnisse sofort zurückgibt)
  2. langsamer Endpunkt (der eine langsame Abfrage startet)
  3. normaler Endpunkt (der auf eine verfügbare Verbindung wartet, da er von 2 verwendet wird)
  4. normaler Endpunkt (der nach einer Minute ausgelöst wird)

Code:

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

// The db reference
var db;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Die Konsolenausgabe ist wie folgt:

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

Es werden keine Mongo-Fehler-/Timeout-Ereignisse wie beim vorherigen Skript ausgelöst, aber Request 2 und 3 werden korrekt aufgelöst, und der Callback von Request 4 wird ebenfalls ausgelöst - im Gegensatz zum Skript, das Mongoose verwendet.

Ich versuche auszuschließen, ob dies ein Mongoose vs. ein Mongo-Problem ist - daher wäre jede Hilfe beim Vergleich der Ergebnisse dieser Skripte dankbar. Vielleicht kann sich @vkarpov15 hier einklinken ? Vielen Dank!

@adriaanmeuris das ist seltsam, hier ist die Ausgabe, die ich beim Ausführen Ihres ersten Skripts erhalte:

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

Mungo bleibt verbunden. Können Sie Ihre Versionen von mongodb, node und mongoose erläutern?

danke fürs testen! In der Tat bleibt Mongoose immer verbunden, aber Rückrufe werden nicht mehr ausgeführt (Sie können sehen, dass die Anfrage in Express eingeht, aber es wird eine Zeitüberschreitung erfolgen).

Ich habe getestet mit:

  • Knoten 6.8.1
  • mongodb 2.2.30
  • Mungo 4.11.5

Ich habe aktualisiert auf:

  • Knoten 6.8.1
  • mongodb 2.2.31
  • Mungo 4.11.7

Ich kann das Problem immer noch lokal mit dem ersten Skript reproduzieren, das ich gepostet habe. Es tritt nur auf, wenn eine Mongo-Verbindung das Zeitlimit überschreitet, daher möchten Sie möglicherweise die Abfrage ändern, die dies simuliert:

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

(Ich bin mir nicht sicher, ob dies auf jedem Computer eine Zeitüberschreitung auslöst, sodass möglicherweise eine Änderung dieser Abfrage erforderlich ist, um sie zu reproduzieren.)

Diese Probleme treten nach dem Update auf Mongoose 4.10.4 nicht mehr auf.
Scheint ein bisschen seltsam, da andere Probleme zu haben scheinen, aber viele Dienste seit ein paar Wochen laufen, ohne dass Anzeichen dafür auftreten, dass das Problem erneut auftritt. 🤔

Wir haben jedoch ähnliche Probleme wie zuvor bei @adriaanmeuris gesehen , es könnte also sein, dass wir einfach keine Zeitüberschreitungen erlebt haben.

Laufender Knoten 8.

Wir sehen dieses Problem immer noch regelmäßig.

Ich konnte es mit dem Skript von @adriaanmeuris nicht reproduzieren, aber hier ist eine modifizierte Version, die es jedes Mal für mich reproduziert:

Bendytree/Mungo-Ausgabe-4513

Danke @bendytree , mit deinem Skript kann ich das auch reproduzieren:

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 ist im Wesentlichen das gleiche Skript wie meines, jedoch mit einer längeren Timeout-Dauer. Ich denke, die lokale Simulation eines Timeouts dauert abhängig von Ihrem Setup mehr / weniger Zeit, da nicht jeder mit meinem Skript (das einen Schlaf von 10 Sekunden verwendet) reproduzieren kann.

@vkarpov15 können Sie dieses aktualisierte Skript ausprobieren. Ich habe mit Mungo 4.8.11 getestet und reproduziert.

Ich habe es geschafft, mit @bendytree zu socketTimeoutMS zu niedrig. Die

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

Meldungen weisen auf das Socket-Timeout des Mongodb-Treibers hin. Der mongodb-Treiber läuft standardmäßig nach 30 Sekunden ab, aber Ihr Betriebssystem hat auch eine Socket-Zeitüberschreitung, die Sie beachten müssen. Daher ist es im Allgemeinen eine gute Idee, Abfragen zu vermeiden, die länger als 15 Sekunden dauern. Sie können jedoch das Socket-Timeout von mongodb mit der Option socketTimeoutMS konfigurieren:

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

Wenn diese Option auf 0 gesetzt ist, ist Ihr Skript auf meinem Computer fast erfolgreich, die 10-Minuten-Abfrage stirbt immer noch, aber Rückrufe werden fortgesetzt

$ 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

Aber die nachfolgende Abfrage ist erfolgreich.

In jedem Fall sollte Mungo in diesem Fall ein Timeout-Ereignis ausgeben, also werden wir dieses Problem beheben.

Ich denke, Sie verwechseln connectTimeoutMS (mit einem Standard von 30000 ms) mit socketTimeoutMS (mit einem Standard von 360000 ms).

Es wird empfohlen, socketTimeoutMS auf das Zwei- bis Dreifache der Länge des langsamsten Vorgangs einzustellen, der durch den Treiber ausgeführt wird. node-mongodb-native/2.2/reference/faq/).

Es bleibt also die Frage: Warum hören diese Rückrufe auf, wenn dieses Limit (festes oder standardmäßiges Betriebssystem-Timeout) erreicht wird?

@ vkarpov15 mit [email protected] , habe ich jetzt das Timeout - Ereignis, vielen Dank für das sehen. Dieses Rückrufproblem wird jedoch weiterhin

Ich habe noch einige Tests durchgeführt und herausgefunden, dass dies auch ohne Mungo reproduziert werden kann. Beim Lesen von http://mongodb.github.io/node-mongodb-native/2.2/reference/faq/ fand ich Folgendes heraus: Closing the socket forces a reconnect of the driver’s connection pool and introduces latency to any other operations which are queued up

Obwohl mongoose.connection.readyState immer 1 ist, habe ich versucht, autoReconnect auf true und das Skript erneut auszuführen:

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

Es sieht also so aus, als ob Rückrufe funktionieren, wenn autoReconnect auf true gesetzt ist 👍

Beim weiteren Lesen dieses Artikels: http://mongodb.github.io/node-mongodb-native/2.0/tutorials/connection_failures/ wurde mir klar, dass der Grund, warum die Rückrufe nach dem Timeout nicht erfolgen, darin besteht, dass die Operation gepuffert, während darauf gewartet wird, dass sich der Server wieder verbindet.

Also habe ich versucht, autoReconnect wieder auf false + bufferMaxEntries auf 0 , das ist die Ausgabe:

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

Dies erklärt, warum der Rückruf in der ersten Version des @bendytree nicht ausgelöst wurde.

@vkarpov15 Damit

  • Warum werden keine Verbindungsereignisse ausgegeben (ich würde erwarten, dass ein getrenntes Ereignis angezeigt wird)
  • das Timeout-Ereignisereignis scheint weiterhin zu emittieren

Hier ist das aktualisierte Skript (basierend auf dem Skript von @bendytree , getestet mit [email protected] und [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);

Ja, du hast Recht, wir müssen das besser machen. Das Ereignis timeout ist notwendig, aber nicht wirklich ausreichend, um diesen Fall zu behandeln.

Der Grund, warum 'disconnected' nicht ausgegeben wird, ist, dass 'timeout' bedeutet, dass ein Socket abgelaufen ist, nicht der gesamte Pool. Mungo müsste wissen, wie viele Sockets im Pool zum Zeitpunkt des Timeout-Ereignisses noch am Leben sind, und ich müsste mehr untersuchen, um herauszufinden, wie wir das machen.

Wird genauer untersuchen, warum das Timeout-Ereignis immer wieder emittiert wird. Danke für deine Geduld :+1:

Ich habe dies bis auf ein paar Fehler im mongodb-Treiber aufgespürt: https://github.com/mongodb-js/mongodb-core/pull/215 . Wir müssen darauf warten, dass es zusammengeführt und bereitgestellt wird, bevor wir es in Mungo einbinden können.

@vkarpov15 In meinen eigenen Tests erlebe ich, dass sich mongoose.connection.readyState nie ändert. ist das das gleiche problem?

@r3wt dieses Problem sollte in 4.11.13 behoben sein, welche Version verwenden Sie?

@vkarpov15 Ich habe zum Zeitpunkt meines Beitrags 4.11.11 verwendet. Es ist entmutigend, dass sich solch ein offensichtlich schlechtes Verhalten in die Produktion einer so bemerkenswerten Bibliothek einschleichen kann. vielleicht sind einige bessere Testverfahren erforderlich. (Ich versuche nicht, respektlos zu sein, ich weiß, wie schwer es ist, Open-Source-Projekte zu pflegen. Manchmal ist es scheiße)

@vkarpov15 Ich entwickle ein Mungo-Plugin. es ist notwendig, dass das Plugin blockiert, bis die Verbindung bereit ist, andernfalls blockiert es (aus Gründen) beim Warten auf die Verbindung. Der Code, den ich verwende, um darauf zu warten, dass die Verbindung bereit ist, ist der folgende:

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

Ich habe auch versucht, über mongoose.connnection.on('connected') zuzuhören, aber das hat leider auch nicht funktioniert. Bei einer leeren Installation hat es gut funktioniert. aber in Verbindung mit anderen Mungo-Plugins und anderem Zeug, das auf die Ereignisse lauscht, tritt das Problem hier auf. connection.readyState bleibt für immer 0 und das verbundene Ereignis wird nie ausgegeben.

es scheint, dass require('mongoose') !== require('mongoose') meiner Meinung nach. ich hoffe das macht sinn.

dh:

  1. Benutzerskript require('mongoose')
  2. Plugin ein require('mongoose')
  3. Plugin b require('mongoose')

das Benutzerskript und das Plugin erhalten die Informationen über die Verbindung. aber Plugin b tut es nie. macht das Sinn?

@r3wt in der Tat, es ist definitiv entmutigend. Seitdem haben wir , die einen Mongodb-Server tatsächlich stoppen/starten und bestätigen, dass Mongoose das Richtige tut , um dies in Zukunft zu verhindern. Es tut uns leid für die Mühe.

Was das Blockieren angeht, bis die Verbindung bereit ist, sollte mongoose.connection.on('connected') funktionieren. Welche anderen Plugins verwendest du? Versuchen Sie es mit npm list | grep "mongoose" , Ihre Behauptung, dass require('mongoose') !== require('mongoose') mich glauben lässt, dass Sie irgendwo mehrere Versionen von Mungo haben.

Leider muss ich diesen wieder zurückschieben, weil der mongodb-Treiber daran gehindert wird, ihn freizugeben. Tut mir leid und danke für deine Geduld :+1: 🌴

@vkarpov15 Ich wollte bedanken . Wir verwenden es jetzt seit einigen Monaten in der Produktion mit 0 Problemen.

@bendytree danke für deine freundlichen Worte und deine Geduld, dieses Problem

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen