Mongoose: Panggilan Balik Berhenti Terjadi

Dibuat pada 13 Sep 2016  ·  53Komentar  ·  Sumber: Automattic/mongoose

Saya telah menggunakan mongoose selama beberapa tahun, tetapi akhir-akhir ini saya mengalami banyak masalah di mana koneksi tampaknya hilang.

Pengaturan Koneksi

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

Masalah

Luwak hanya berhenti menelepon kembali. Itu tidak memancarkan kesalahan apa pun, memori/cpu baik-baik saja di server & db saya, dan saya dapat terhubung dari server lain. Restart selalu memperbaikinya:

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

Versi

Pada mongoose 4.4.11 , mongoose.connection.readyState === UNAUTHORIZED saat panggilan balik berhenti.

Jadi saya memutakhirkan ke mongoose 4.6.0 tetapi dalam 4 jam panggilan balik berhenti lagi, sekarang dengan mongoose.connection.readyState === CONNECTED .

Ide apa pun

Adakah ide bagaimana saya bisa menghentikan ini terjadi?

Saya senang mengumpulkan info debug lagi, beri tahu saya info apa yang harus saya dapatkan.

underlying library issue

Komentar yang paling membantu

Sayangnya harus mendorong yang ini kembali karena driver mongodb tertahan dari rilis. Maaf tentang itu dan terima kasih atas kesabaran Anda :+1:

Semua 53 komentar

Apakah Anda terhubung ke kluster mandiri, set replika, atau sharded? Juga, dapatkah Anda mengonfirmasi bahwa Anda menggunakan SSL?

Ini telah menjadi masalah besar bagi saya beberapa hari terakhir. Setiap aplikasi yang berjalan berakhir dengan loop peristiwa yang lebih lambat dan lebih lambat, menyebabkan peningkatan latensi di seluruh sistem. Memulai ulang aplikasi Node menyelesaikan masalah setiap kali, dengan itu kembali setelah beberapa menit atau jam operasi.

Mengembalikan ke luwak 4.5.10 menyelesaikan masalah.

  • Menghubungkan ke Replika Set
  • Menggunakan SSL

Masalah serupa di sini dengan 4.6.0, saya tidak yakin apakah saya terhubung ke set replika atau tidak, tetapi pasti menggunakan SSL.

FWIW, 4.6.0 terus-menerus menjatuhkan koneksi. 4.5.10 bagus.

@djanowski dapatkah Anda memberikan info lebih lanjut, seperti apa tampilan kode koneksi Anda dan versi mongodb apa yang Anda miliki?

@vkarpov15 saya menggunakan MongoDB 3.2.6.

Opsi koneksi:

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

Mongo tunggal atau banyak mongo?

Beberapa mongo. Menggunakan penerapan Compose Mongo.

Adakah hal lain yang dapat saya lakukan untuk membantu memecahkan masalah ini? Ini menghalangi kami untuk pindah ke versi yang lebih baru...

Sulit bagi saya untuk membuat hipotesis nyata tanpa melihat kode Anda. Jika Anda terhubung dengan mongoose.connect Saya akan memeriksa nilai mongoose.connection.readyState ketika masalah ini mulai terjadi. Juga, saya akan mengaktifkan mode debug luwak, mongoose.set('debug', true) , yang akan mencetak pesan debug ke konsol untuk setiap operasi luwak. Apakah pesan-pesan itu dicetak atau tidak pasti akan membantu debug.

@vkarpov15 Ini adalah kode yang saya gunakan untuk menghubungkan: https://Gist.github.com/sommestad/c0c6a7fa4feaadf84ecbb59cc3432c90
Itu bagian dari modul pribadi, karenanya Intisari.

_(Alasan untuk modul pembungkus koneksi adalah bahwa koneksi kadang-kadang digunakan kembali di modul NPM pribadi lainnya, yang secara historis tidak bekerja dengan baik dengan luwak.)_

Saya telah mengaktifkan pencatatan status koneksi ketika masalah ini terjadi, tetapi koneksi itu sendiri tampaknya tidak berubah (setidaknya tidak ada peristiwa yang dipancarkan). Jadi, sepertinya klien tidak terputus.

Itu tidak terhubung dengan replicaSet di URI (database target adalah Compose Deployment ). Seperti disebutkan, SSL digunakan. Selain itu, lalu lintas dirutekan melalui AWS NAT Gateway. AWS NAT memiliki waktu tunggu idle 5 menit, tetapi masalah ini terjadi bahkan selama lalu lintas tinggi.

beberapa masalah bagi saya, apakah Anda menyelesaikannya?

@youth7 tidak ada ide. Apakah Anda juga terhubung ke penerapan penulisan? Mohon info tambahan

Adakah yang lebih jauh yang dapat saya lakukan untuk mencoba dan menemukan penyebabnya? Adakah teori yang bisa diselidiki?

@sommestad tidak ada yang konkret. Apakah Anda memiliki log dari compose?

Saya mencoba mongoose 4.7.2 tetapi mengalami masalah yang sama setelah 48 jam. Pada titik ini, saya buntu pada 4.4.11 yang memiliki masalah sendiri.

Saya menginstal 4.7.2 di server demo kami berharap untuk mengumpulkan log, tetapi sudah berjalan selama dua minggu tanpa masalah. Mungkin volume data/koneksi/dll memicu masalah?

Setelah itu mulai terjadi, kode untuk mereproduksinya sangat sederhana. Pada dasarnya panggilan balik kueri apa pun menghilang begitu saja:

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

Seperti yang saya sebutkan, ketika ini terjadi, mongoose.connection.readyState === CONNECTED

Aneh. Maksud saya log mongodb - dapatkah Anda membuang log server mongodb sejak kesalahan itu terjadi?

Saya memindai log DB ketika kegagalan ini terjadi dan tidak dapat melihat sesuatu yang luar biasa. Tidak ada masalah terkait jumlah koneksi, pemutusan, masalah dengan koneksi atau serupa. Saya juga menghubungi Compose yang juga tidak bisa melihat apa pun yang salah.

@bendytree Bisakah Anda melempar volume ke server demo itu, mungkin menggunakan alat pengujian beban HTTP?

@bendytree Juga, apakah itu terjadi pada semua jalur rilis Node (4.x, 6.x dan 7.x)?

@vkarpov15 Bisakah kita mengeluarkan 4.7.7 sehingga saya dapat menguji dengan driver MongoDB terbaru? 4.7.6 disematkan ke versi yang tampaknya tidak digunakan lagi.

@vkarpov15 66d559b19a86c70e30a8f083d03eb2256657b7e Terima kasih! Saya akan mencobanya.

Sama di sini, replika SSL di sini, setelah beberapa menit/jam, permintaan berhenti ke callback() (saya telah menambahkan beberapa pencatatan verbose lagi) dan tidak ada kesalahan. Saya telah meningkatkan ke 4.7.7 tadi malam tetapi masih memiliki masalah.

EDIT: Saya mencoba menurunkan versi ke 4.7.0 tetapi yang terburuk (mengalami masalah dalam waktu kurang dari 5 menit). Saya mencoba 4.5.10.

@gierschv Node 6.9.x?

@djanowski tidak, masih di 4.6/4.7.x LTS.

@djanowski Saya hanya mencobanya di Node 4.4.2

Menyimpan daftar kemungkinan duplikat dari masalah ini:

  • #4638
  • #4690
  • #4901

FYI, 4.5.10 tampaknya berfungsi dengan baik di sini, tidak ada masalah selama 5 jam terakhir. Saya juga memiliki penggunaan CPU yang tinggi seperti #4690 di semua mesin yang tampaknya juga diperbaiki dengan penurunan versi.

Tidak ada perkembangan baru di sini? Ada lagi yang bisa kami lakukan untuk membantu menemukan bug?

@sommestad apakah Anda masih memiliki masalah dengan luwak terbaru ini?

Saya belum berani melakukan peningkatan, karena pada dasarnya merusak seluruh sistem kami terakhir kali (perubahan muncul setelah "beberapa" waktu sehingga sulit untuk memverifikasi tanpa berjalan lama). Jika Anda memiliki alasan untuk percaya bahwa itu sudah diperbaiki, kami dapat mencoba bagian sistem yang lebih terisolasi?

Sunting: ping @varunjayaraman

Di pihak saya, kami beralih ke Node 6.x LTS dan memutakhirkan ke versi luwak terakhir beberapa minggu yang lalu, tidak ada masalah sejak itu.

Kami meningkatkan ke Node 6.10.2, Mongoose 4.9.9 sekitar dua bulan yang lalu. Masih melihat masalah yang sama.

String koneksi kami memiliki 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
  ...
});

Setelah panggilan balik berhenti, mereka tidak akan pernah memulai lagi. Jika kita me-restart proses maka terhubung dengan baik.

Jika ada data yang dapat kami kumpulkan untuk membantu memecahkan masalah, kami akan dengan senang hati membantu.

Saya melihat masalah yang sama dan dapat mereproduksinya dengan kode di bawah ini (maaf untuk gaya mock-up/kode yang cepat).

Untuk mensimulasikan batas waktu koneksi, saya menurunkan poolSize ke 1 dan memicu permintaan ke 2 titik akhir (normal/lambat) yang dipanggil dalam urutan ini:

  1. titik akhir normal (yang segera mengembalikan hasil)
  2. titik akhir lambat (yang akan memulai kueri lambat)
  3. titik akhir normal (yang akan menunggu koneksi yang tersedia karena sedang digunakan oleh 2)
  4. titik akhir normal (yang akan terpicu setelah satu menit, ketika 2 dan 3 menghasilkan MongoError / waktu koneksi habis)

Reproduksi menggunakan:

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

Keluaran konsol adalah sebagai berikut:

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

Saya memantau readyState database setiap 10 detik, yang menunjukkan koneksi masih aktif. Tidak ada pemicu kesalahan Mongoose / waktu habis, dan panggilan balik permintaan 4 tidak pernah dipanggil (waktu habis setelah 60-an menggunakan paket permintaan). Anda juga dapat menguji dengan mengunjungi http://localhost :4000/slow dan selanjutnya http://localhost :4000/normal.

Saya menggunakan logika rekoneksi otomatis berdasarkan https://team.goodeggs.com/reconnecting-to-mongodb-when-mongoose-connect-fails-at-startup-83ca8496ca02 , tetapi ini tidak pernah dipicu karena mongo tidak terputus.

Suatu saat, saya tidak dapat mengulangi ketika saya mengomentari kode yang membuat beberapa data uji (jadi pada dasarnya menanyakan koleksi kosong)

Kemungkinan masalah terkait:

  • #4789
  • #4660

Bantuan apa pun akan sangat saya hargai.

Jadi saya melakukan penggalian lagi dan menulis ulang skrip di atas untuk bekerja dengan node-mongodb-native tanpa menggunakan luwak. Hasilnya sangat berbeda sekarang:

Pengaturan dan permintaan koneksi sama:

  1. titik akhir normal (yang segera mengembalikan hasil)
  2. titik akhir lambat (yang akan memulai kueri lambat)
  3. titik akhir normal (yang akan menunggu koneksi yang tersedia karena sedang digunakan oleh 2)
  4. titik akhir normal (yang akan dipicu setelah satu menit)

Kode:

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

Keluaran konsol adalah sebagai berikut:

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

Tidak ada kesalahan Mongo / peristiwa batas waktu yang dipicu seperti skrip sebelumnya, tetapi permintaan 2 dan 3 diselesaikan dengan benar, dan panggilan balik permintaan 4 juga dipicu - berbeda dengan skrip yang menggunakan Mongoose.

Saya mencoba mengesampingkan apakah ini masalah Mongoose vs. Mongo - jadi bantuan apa pun terkait membandingkan hasil skrip ini akan dihargai. Mungkin @vkarpov15 bisa

@adriaanmeuris itu aneh, inilah output yang saya dapatkan dari menjalankan skrip pertama Anda:

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

Mongoose tetap terhubung. Bisakah Anda mengklarifikasi versi mongodb, node, dan luwak Anda?

terima kasih untuk pengujian! Memang Mongoose selalu tetap terhubung, tetapi panggilan balik berhenti terjadi (Anda dapat melihat permintaan masuk ke Express, tetapi mereka akan kehabisan waktu)

Saya sedang menguji dengan:

  • simpul 6.8.1
  • mongodb 2.2.30
  • luwak 4.11.5

Saya telah memperbarui ke:

  • simpul 6.8.1
  • mongodb 2.2.31
  • luwak 4.11.7

Saya masih dapat mereproduksi masalah secara lokal dengan skrip pertama yang saya posting. Itu hanya terjadi ketika koneksi Mongo habis, jadi Anda mungkin ingin mengubah kueri yang mensimulasikan ini:

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

(Saya tidak yakin apakah ini akan memicu batas waktu pada setiap mesin sehingga perubahan pada kueri ini mungkin diperlukan untuk mereproduksi)

Kami tidak lagi melihat masalah ini setelah memperbarui ke luwak 4.10.4.
Tampaknya agak aneh, karena yang lain tampaknya mengalami masalah, tetapi memiliki banyak layanan yang berjalan selama beberapa minggu tanpa tanda-tanda masalah kembali. 🤔

Namun, kami memang melihat masalah dengan cara yang sama seperti @adriaanmeuris sebelumnya, jadi bisa jadi kami tidak mengalami timeout.

Menjalankan Node 8.

Kami masih melihat masalah ini secara berkala.

Aku tidak bisa mereproduksi dengan @adriaanmeuris 's naskah , tapi di sini adalah versi modifikasi yang mereproduksi setiap kali bagi saya:

bendytree/luwak-isu-4513

Terima kasih @bendytree , dengan skrip Anda, saya juga dapat mereproduksi:

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

Ini pada dasarnya skrip yang sama dengan milik saya tetapi dengan durasi waktu tunggu yang lebih lama. Saya kira mensimulasikan batas waktu secara lokal membutuhkan lebih banyak/lebih sedikit waktu tergantung pada pengaturan Anda, karena tidak semua orang dapat melakukan repro dengan skrip saya (yang menggunakan tidur 10 detik).

@vkarpov15 dapatkah Anda mencoba skrip yang diperbarui ini. Saya telah menguji & mereproduksi dengan luwak 4.8.11.

Saya berhasil repro dengan @bendytree , ini adalah kasus socketTimeoutMS terlalu rendah. NS

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

pesan menunjukkan waktu soket driver mongodb habis. Waktu driver mongodb habis setelah 30 detik secara default, tetapi OS Anda juga memiliki batas waktu soket yang perlu Anda waspadai, jadi sebaiknya hindari kueri yang membutuhkan waktu lebih dari 15 detik. Namun, Anda dapat mengonfigurasi batas waktu soket mongodb menggunakan opsi socketTimeoutMS :

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

Dengan opsi itu disetel ke 0, skrip Anda hampir berhasil di mesin saya, kueri 10 menit masih mati tetapi panggilan balik berlanjut

$ 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

Tetapi kueri berikutnya berhasil.

Either way, luwak harus mengeluarkan acara batas waktu dalam kasus ini, jadi kami akan memperbaiki masalah itu.

Saya pikir Anda membingungkan connectTimeoutMS (dengan default 30000ms) dengan socketTimeoutMS (dengan default 360000ms).

Disarankan untuk mengatur socketTimeoutMS menjadi dua hingga tiga kali panjang operasi paling lambat yang berjalan melalui driver, mengatur socketTimeoutMS ke 0 sebenarnya berarti menerapkan nilai batas waktu soket default sistem operasi (info lebih lanjut di http://mongodb.github.io/ simpul-mongodb-asli/2.2/referensi/faq/).

Jadi pertanyaan ini tetap ada: mengapa panggilan balik ini berhenti terjadi ketika batas ini (batas waktu OS tetap atau default) tercapai?

@ vkarpov15 dengan [email protected] , sekarang saya melihat acara timeout, terima kasih untuk itu. Namun Anda masih melihat masalah panggilan balik ini (menggunakan skrip asli @bendytree , untuk pengujian yang lebih cepat, Anda dapat mengatur LONG_QUERY_DURATION_IN_MS ke 2000 dan socketTimeoutMS ke 1000)

Saya telah melakukan beberapa pengujian lagi dan menemukan ini dapat direproduksi tanpa luwak juga. Saat membaca http://mongodb.github.io/node-mongodb-native/2.2/reference/faq/ , saya menemukan bahwa: Closing the socket forces a reconnect of the driver’s connection pool and introduces latency to any other operations which are queued up

Meskipun mongoose.connection.readyState selalu 1, saya mencoba mengatur autoReconnect ke true , menjalankan ulang skrip:

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

Jadi sepertinya panggilan balik berfungsi dengan autoReconnect disetel ke true 👍

Setelah membaca lebih lanjut artikel ini: http://mongodb.github.io/node-mongodb-native/2.0/tutorials/connection_failures/ , saya menyadari bahwa alasan mengapa panggilan balik tidak terjadi setelah batas waktu, adalah karena operasinya buffer sambil menunggu server terhubung kembali.

Jadi saya mencoba mengatur autoReconnect kembali ke false + set bufferMaxEntries ke 0 , ini adalah outputnya:

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

Yang menjelaskan mengapa panggilan balik tidak terpicu di versi pertama skrip @bendytree .

@vkarpov15 Sekarang ini meninggalkan kita dengan 2 pertanyaan:

  • mengapa tidak ada acara koneksi yang dipancarkan (saya berharap melihat acara yang terputus)
  • acara acara batas waktu tampaknya terus memancarkan

inilah skrip yang diperbarui (berdasarkan skrip @bendytree , diuji dengan [email protected] dan [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);

Ya, Anda benar, kita perlu berbuat lebih baik tentang ini. Acara timeout diperlukan, tetapi tidak cukup untuk menangani kasus ini.

Alasan mengapa 'terputus' tidak dipancarkan adalah bahwa 'batas waktu' berarti satu soket habis waktu, bukan seluruh kumpulan. Luwak perlu tahu berapa banyak soket di kolam yang masih hidup ketika peristiwa batas waktu terjadi, dan saya perlu menyelidiki lebih lanjut untuk mencari tahu bagaimana kita melakukannya.

Akan menyelidiki lebih lanjut mengapa acara batas waktu terus memancarkan kembali. Terima kasih atas kesabaran Anda :+1:

Saya melacak ini ke beberapa bug di driver mongodb: https://github.com/mongodb-js/mongodb-core/pull/215 . Harus menunggu itu untuk digabungkan dan digunakan sebelum kita dapat menambalnya ke luwak.

@vkarpov15 dalam pengujian saya sendiri, saya mengalami mongoose.connection.readyState tidak pernah berubah. apakah ini masalah yang sama?

@r3wt masalah itu harus diperbaiki di 4.11.13, versi mana yang Anda gunakan?

@vkarpov15 Saya menggunakan 4.11.11 pada saat posting saya. sungguh menyedihkan bahwa perilaku buruk yang begitu jelas dapat menyelinap ke dalam produksi untuk perpustakaan yang begitu terkenal. mungkin diperlukan beberapa prosedur pengujian yang lebih baik. (Saya tidak berusaha untuk tidak sopan, saya tahu betapa sulitnya mempertahankan proyek open source. terkadang menyebalkan)

@vkarpov15 saya sedang mengembangkan plugin luwak. plugin perlu diblokir sampai koneksi siap, jika tidak maka akan menemui jalan buntu menunggu koneksi (karena alasan). kode yang saya gunakan untuk menunggu koneksi siap adalah sebagai berikut:

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

saya juga mencoba mendengarkan melalui mongoose.connnection.on('connected') tetapi sayangnya itu juga tidak berhasil. pada instalasi kosong itu berfungsi dengan baik. tetapi dalam hubungannya dengan plugin luwak lain dan hal-hal yang mendengarkan acara, di situlah masalah terjadi. connection.readyState tetap 0 selamanya dan acara yang terhubung tidak pernah dipancarkan.

sepertinya require('mongoose') !== require('mongoose') menurut saya. saya harap ini masuk akal.

yaitu:

  1. skrip pengguna require('mongoose')
  2. plugin require('mongoose')
  3. plugin b require('mongoose')

skrip pengguna dan plugin menerima info tentang koneksi. tetapi plugin b tidak pernah melakukannya. Apakah masuk akal?

@r3wt memang, itu pasti mengecewakan. Kami telah menambahkan tes yang benar-benar menghentikan/memulai server mongodb dan menegaskan luwak melakukan hal yang benar untuk mencegah hal ini terjadi di masa mendatang. Kami mohon maaf atas masalah ini.

Sedangkan untuk memblokir hingga koneksi siap, mongoose.connection.on('connected') akan berfungsi. Apa plugin lain yang Anda gunakan? Coba jalankan npm list | grep "mongoose" , pernyataan Anda bahwa require('mongoose') !== require('mongoose') membuat saya berpikir bahwa Anda memiliki banyak versi luwak di suatu tempat.

Sayangnya harus mendorong yang ini kembali karena driver mongodb tertahan dari rilis. Maaf tentang itu dan terima kasih atas kesabaran Anda :+1:

@vkarpov15 Hanya ingin mengucapkan terima kasih banyak atas pekerjaan Anda dalam perbaikan ini. Kami telah menggunakannya dalam produksi selama beberapa bulan sekarang dengan 0 masalah.

@bendytree terima kasih atas kata-kata baik Anda dan kesabaran Anda dalam membuat masalah ini diulang. Tim driver mongodb sedang sibuk mengerjakan rilis 3.0 tepat waktu untuk rilis mongodb 3.6, jadi mereka belum bisa mengeluarkan rilis 2.x dengan perbaikan ini, saya akan ping mereka lagi. Selamat berlibur!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat