我已经使用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); }
});
猫鼬只是停止回电。 它不会发出任何错误,我的服务器和数据库上的内存/cpu 很好,我可以从任何其他服务器连接。 重新启动总是修复它:
db.Something.count({}, callback);
在mongoose 4.4.11
,当回调停止时mongoose.connection.readyState === UNAUTHORIZED
。
所以我升级到mongoose 4.6.0
但在 4 小时内回调再次停止,现在是mongoose.connection.readyState === CONNECTED
。
我有什么想法可以让这种情况停止发生吗?
我很高兴收集更多调试信息,只要让我知道要获取什么信息即可。
您是连接到独立的、副本集还是分片集群? 另外,您能否确认您正在使用 SSL?
这几天对我来说是个大问题。 每个正在运行的应用程序都以越来越慢的事件循环结束,从而导致整个系统的延迟增加。 每次重新启动 Node 应用程序都解决了该问题,并且在运行几分钟或几小时后它又会再次出现。
恢复到 mongoose 4.5.10 解决了这个问题。
与 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],
},
单个 mongos 还是多个 mongos?
多个mongos。 使用 Compose Mongo 部署。
我还能做些什么来帮助解决此问题? 这阻碍了我们转向更新的版本......
在没有看到您的代码的情况下,我很难做出任何真正的假设。 如果您使用mongoose.connect
连接,我会在此问题开始发生时检查mongoose.connection.readyState
的值。 另外,我会启用 mongoose 调试模式mongoose.set('debug', true)
,它将为每个 mongoose 操作将调试消息打印到控制台。 这些消息是否正在打印肯定有助于调试。
@vkarpov15这是我用来连接的代码: https : //gist.github.com/sommestad/c0c6a7fa4feaadf84ecbb59cc3432c90
它是私有模块的一部分,因此是 Gist。
_(连接包装模块的原因是该连接偶尔会在其他私有 NPM 模块中重用,这在历史上与 mongoose 没有很好的配合。)_
当这些问题发生时,我启用了连接状态的日志记录,但连接本身似乎没有改变(至少没有发出任何事件)。 因此,客户端似乎没有断开连接。
它没有与 URI 中的replicaSet
连接(目标数据库是Compose Deployment )。 如前所述,使用了 SSL。 此外,流量通过 AWS NAT 网关进行路由。 AWS NAT 有 5 分钟的空闲超时,但即使在高流量期间也会出现这些问题。
对我来说有些问题,你解决了吗?
@youth7没有想法。 您是否还连接到组合部署? 请提供更多信息
我还能做些什么来尝试找出造成这种情况的原因? 有什么理论可以研究吗?
@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你能在那个演示服务器上扔一些卷吗,也许使用 HTTP 负载测试工具?
@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 上试过
保留此问题的可能重复列表:
仅供参考,4.5.10 在这里似乎运行良好,过去 5 小时没有问题。 我在所有机器上的 CPU 使用率也很高,如 #4690,这似乎也被降级修复了。
这里没有新进展? 我们还能做些什么来帮助找到错误?
@sommestad你对最新的猫鼬还有这个问题吗?
我还不敢升级,因为上次它基本上破坏了我们的整个系统(更改出现在“一段时间”之后,因此如果不长时间运行就很难验证)。 如果你有理由相信它是固定的,我们可以尝试系统中更孤立的部分吗?
编辑:ping @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 个端点(正常/慢速)的请求,这些端点按以下顺序调用:
使用:
// 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 秒监视一次数据库的 readyState,这表明连接仍然处于活动状态。 没有Mongoose错误/超时事件触发,并且从不调用请求4的回调(使用请求包60s后超时)。 您还可以通过访问http://localhost :4000/slow 和下一个http://localhost :4000/normal 进行测试。
我正在使用基于https://team.goodeggs.com/reconnecting-to-mongodb-when-mongoose-connect-fails-at-startup-83ca8496ca02 的自动重新连接逻辑,但这永远不会触发,因为 mongo 不会断开连接。
在某些情况下,当我评论创建一些测试数据的代码时,我无法重现(所以基本上是查询一个空集合)
可能的相关问题:
任何帮助都会让我不胜感激。
所以我做了更多的挖掘并重新编写了上面的脚本,以便在不使用 mongoose 的情况下使用 node-mongodb-native。 现在的结果大不相同:
连接设置和请求是一样的:
代码:
// 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 版本吗?
感谢测试! 事实上猫鼬总是保持连接,但回调停止发生(你可以看到请求进入 Express,但它们会超时)
我正在测试:
我已经更新为:
我仍然可以使用我发布的第一个脚本在本地重现该问题。 它仅在 Mongo 连接超时时发生,因此您可能需要更改模拟此的查询:
mongoose.model('Test').find({$where: 'sleep(10000) || true'})
(我不确定这是否会在每台机器上触发超时,因此可能需要对此查询进行更改才能重现)
更新到 mongoose 4.10.4 后,我们不再看到这些问题。
似乎有点奇怪,因为其他人似乎遇到了问题,但许多服务运行了几周,没有任何问题再次出现的迹象。 🤔
但是,我们确实以与之前@adriaanmeuris相同的方式看到了问题,因此可能是我们没有遇到任何超时。
运行节点 8。
谢谢@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你能试试这个更新的脚本吗? 我已经用猫鼬 4.8.11 进行了测试和复制。
我设法用@bendytree重现,这是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 秒的查询。 但是,您可以使用socketTimeoutMS
选项配置 mongodb 的套接字超时:
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/ node-mongodb-native/2.2/reference/faq/)。
所以这个问题仍然存在:当达到这个限制(固定或默认操作系统超时)时,为什么这些回调停止发生?
@ vkarpov15与[email protected] ,我现在看到的超时事件,感谢。 但是,您仍然会看到此回调问题(使用@bendytree的原始脚本,为了更快地测试,您可以将 LONG_QUERY_DURATION_IN_MS 设置为 2000,将 socketTimeoutMS 设置为 1000)
我做了一些更多的测试,发现这也可以在没有猫鼬的情况下重现。 在阅读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 :
所以我尝试将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现在留给我们两个问题:
这是更新后的脚本(基于@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
事件是必要的,但不足以处理这种情况。
未发出 'disconnected' 的原因是 'timeout' 意味着一个套接字超时,而不是整个池。 Mongoose 需要知道在超时事件发生时池中有多少套接字仍然处于活动状态,我需要进行更多调查以弄清楚我们如何做到这一点。
将进一步调查超时事件不断重新发射的原因。 感谢您的耐心等待:+1:
我将其追溯到 mongodb 驱动程序中的几个错误: https :
@vkarpov15在我自己的测试中,我遇到mongoose.connection.readyState
永远不会改变。 这是同样的问题吗?
@r3wt该问题应该在 4.11.13 中修复,您使用的是哪个版本?
@vkarpov15我在发帖时使用的是 4.11.11。 令人沮丧的是,这种明显的不良行为可以潜入如此著名的图书馆的生产中。 也许需要一些更好的测试程序。 (我不是想不尊重,我知道维护开源项目是多么困难。有时它很糟糕)
@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,并且永远不会发出 connected 事件。
在我看来,似乎require('mongoose') !== require('mongoose')
。 我希望这是有道理的。
IE:
require('mongoose')
require('mongoose')
require('mongoose')
用户脚本和插件接收有关连接的信息。 但插件 b 从来没有。 是否有意义?
@r3wt确实,这绝对令人沮丧。 我们已经添加了实际停止/启动 mongodb 服务器的测试,并断言 mongoose 做了正确的事情以防止将来发生这种情况。 我们很抱歉给您带来麻烦。
至于阻塞直到连接准备好, mongoose.connection.on('connected')
应该可以工作。 你在用什么其他插件? 尝试运行npm list | grep "mongoose"
,您断言require('mongoose') !== require('mongoose')
使我认为您在某处有多个版本的猫鼬。
不幸的是,由于 mongodb 驱动程序无法发布,因此不得不再次将其推回。 很抱歉,感谢您的耐心等待 :+1: 🌴
@vkarpov15只想对您在此修复程序上所做的工作表示感谢。 我们已经在生产中使用它几个月了,问题是 0。
@bendytree感谢您的客气话和您的耐心让这个问题重现。 mongodb 驱动程序团队一直忙于为 mongodb 3.6 版本及时发布 3.0,所以他们还没有能够发布带有此修复程序的 2.x 版本,我将再次 ping 他们。 节日快乐!
最有用的评论
不幸的是,由于 mongodb 驱动程序无法发布,因此不得不再次将其推回。 很抱歉,感谢您的耐心等待 :+1: 🌴