可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am using Mongoose with a very large Mongo database, and I want costly queries like MySet.find({})
to time out after 10 seconds.
I've tried setting a socket timeout on my connection, but the server crashes if the timeout is exceeded:
var options = {server: {socketOptions: {socketTimeoutMS: 10000}}};
var conn = mongoose.connect('mongodb://localhost/my_db', options);
I've tried passing the maxTimeMS option to the find function, but that doesn't have any effect at all:
MySet.find({}, {}, {timeout: true, maxTimeMS: 10000}, function(err, doc) {});
Any ideas?
回答1:
You can do this with the Query#maxTime
method that's not documented in Mongoose itself, but is part of the mquery
library that Mongoose uses for its fluent query interface.
So in your case, you would call it as:
MySet.find({}).maxTime(10000).exec(function(err, doc) { ... });
You can confirm it's correctly setting the maxTimeMS
option by enabling Mongoose debugging via mongoose.set('debug', true);
and then you'll see console output for this query that looks like:
Mongoose: myset.find({}) { maxTimeMS: 10000, safe: true, fields: {} }
回答2:
TL;DR:
MySet.find({ $query: { /*Query here*/ }, $maxTimeMS: 10000 });
You can test with this query :
MySet.find({ $query: {"$where": "sleep(100) || true"}, $maxTimeMS: 10000 });
Why it works:
You can use Query modifiers
And especialy this one : $maxTimeMS
Be careful : this operator is deprecated in mongo Shell since v3.2
回答3:
I think this should work.
db.mycoll.find().maxTimeMS(50)
回答4:
This is the pattern I have started to use a lot.
// Default time out timeout in ms
const DEFAULT_TIME_OUT = 500;
// Default timeout message
const DEFAULT_MESSAGE = `Timeout fetching data(${DEFAULT_TIME_OUT}ms)`;
// Function that triggers a Promise's reject after a set amount of time
function timeoutReject(reject, message, timeout) {
setTimeout(function(){
// Reject the Promise if the time is reached
reject(message || DEFAULT_MESSAGE);
}, timeout || DEFAULT_TIME_OUT);
};
function youAreNotAuthentic() {
// Logic to validate user and request return boolean
};
// Throw error if the user cannot access this data
function youCantTouchThis() {
throw new Error('You cannot access this data.');
};
// Function to request data
function getDataById(_id) {
// First check if this is authentic
if (youAreNotAuthentic()) youCantTouchThis();
// Return a Promise
return new Promise((resolve, reject) => {
// Set a timeout to reject if we do not get a response in x time
timeoutReject(reject, 'Custom Message', 300);
// Could look like this to just use the defaults
// timeoutReject(reject);
// Query for the data
Collection.findOne({ _id }).then(data => {
// Resolve the Promise with the retrieved data
resolve(data);
});
});
};
This way I have a default timeout applied to most requests but if I need to adjust on an per call basis then I can. Or I can be made aware of areas that need better indexing.
回答5:
I finally got it working. First I prevent the server from crashing when the socket (i.e. a query) times out:
//don't crash the server if a query times out
mongoose.connection.on('error', function() {});
Then, every time I want to query the database, I disconnect from and reconnect to the database, setting socketTimeoutMS to 10000:
mongoose.disconnect();
mongoose.connect('mongodb://localhost/my_db', {
server: {socketOptions: {socketTimeoutMS: 10000}}
});
MySet.find({}, function(err, doc) {});
This cuts off the query after precisely 10 seconds of execution.