【问题标题】:How do I set a timeout on a Mongoose query?如何在 Mongoose 查询上设置超时?
【发布时间】:2019-06-26 20:25:22
【问题描述】:

我正在使用带有非常大的 Mongo 数据库的 Mongoose,我希望像 MySet.find({}) 这样的昂贵查询在 10 秒后超时。

我尝试在我的连接上设置套接字超时,但如果超过超时,服务器就会崩溃:

var options = {server: {socketOptions: {socketTimeoutMS: 10000}}};
var conn = mongoose.connect('mongodb://localhost/my_db', options);

我尝试将maxTimeMS 选项传递给find 函数,但这根本没有任何效果:

MySet.find({}, {}, {timeout: true, maxTimeMS: 10000}, function(err, doc) {});

有什么想法吗?

【问题讨论】:

  • 您使用的是 MongoDB 2.6 或更高版本吗?
  • mongo --versionMongoDB shell version: 2.6.8
  • 看起来 Mongoose 对 maxTimeMS 选项的支持是 added in 3.8.13。你的猫鼬版本是什么?
  • Mongoose 在 3.8.24
  • MySet.find({}, {}, {maxTime: 10}, function(err, doc) {}) 之类的作品,但仅适用于 maxTime 的小值。起初我认为 maxTime 需要几秒钟或十分之一秒,但值 10 使它几乎立即超时,而值 100 使它永远不会超时。

标签: node.js mongodb mongoose


【解决方案1】:

您可以使用Query#maxTime 方法来做到这一点。

所以在你的情况下,你可以这样称呼它:

MySet.find({}).maxTime(10000).exec(function(err, doc) { ... });

您可以通过 mongoose.set('debug', true); 启用 Mongoose 调试来确认它正确设置了 maxTimeMS 选项,然后您将看到此查询的控制台输出如下所示:

Mongoose: myset.find({}) { maxTimeMS: 10000, safe: true, fields: {} }

【讨论】:

【解决方案2】:

TL;DR:

MySet.find({ $query: { /*Query here*/ }, $maxTimeMS: 10000 });

您可以使用此查询进行测试:

MySet.find({ $query: {"$where": "sleep(100) || true"}, $maxTimeMS: 10000 });

为什么有效:

您可以使用Query modifiers

尤其是这个:$maxTimeMS

小心:从 v3.2 开始,此运算符在 mongo Shell 中已弃用

【讨论】:

    【解决方案3】:

    这是我开始大量使用的模式。

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

    这样,我对大多数请求都应用了默认超时,但如果我需要根据每个呼叫进行调整,那么我可以。或者我可以了解需要更好索引的区域。

    【讨论】:

    • 有些人建议并批准“单角报价”为正常报价。这行不通。它应该是一个模板化的字符串。 ` 是正确的,不是 '
    【解决方案4】:

    我认为这应该可行。

    db.mycoll.find().maxTimeMS(50)

    【讨论】:

    • 这适用于本机 MongoDB 驱动程序,但不适用于 Mongoose。
    【解决方案5】:

    我终于让它工作了。首先,我防止服务器在套接字(即查询)超时时崩溃:

    //don't crash the server if a query times out
    mongoose.connection.on('error', function() {});
    

    然后,每次我想查询数据库,我都会断开并重新连接数据库,将socketTimeoutMS设置为10000:

    mongoose.disconnect();
    mongoose.connect('mongodb://localhost/my_db', {
      server: {socketOptions: {socketTimeoutMS: 10000}}
    });
    MySet.find({}, function(err, doc) {});
    

    这会在恰好执行 10 秒后切断查询。

    【讨论】:

    • 在每个查询上断开/重新连接会很昂贵,对吧?
    • 您通过重新连接每个查询所支付的开销使得这种方法无法用于除最简单的应用程序之外的所有应用程序。
    猜你喜欢
    • 1970-01-01
    • 2011-09-15
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 2016-06-20
    • 2011-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多