简体   繁体   English

Node.js连接池在错误时产生无限循环

[英]Node.js connection pool produces infinite loop on error

I recently upgraded my generic-pool package to version 3 that makes use of promises - a concept I probably don't understand enough. 我最近将通用池软件包升级到了使用Promise的版本3-这个概念我可能还不太了解。 I have managed to get it working, despite some unexpected differences in operation. 尽管在操作上有一些意外的差异,但我还是设法使其工作。

The problem I have is when I start testing error conditions. 我遇到的问题是当我开始测试错误情况时。 I have purposely set the password wrong, and when testing, I get an infinite loop of "connection failed" errors - indicating something is triggering the create function despite the error. 我故意将密码设置为错误,并且在测试时,出现“连接失败”错误的无限循环-表示尽管有错误,但是某些事件触发了create函数。 I assume I have either configured the pool incorrectly, or I am acquiring improperly. 我认为我配置的池不正确,或者获取不正确。

Generic pool factory: 通用泳池工厂:

const poolFactory = {
  create: function() {
    return new Promise(function(resolve, reject) {
      var client = mysql.createConnection({
        host: config.host,
        user: config.user,
        password: config.pass,
      });
      client.connect(function(err) {
        if (err != null) {
          log.write('ERROR', "Connection Error: MySQL: " + err.message);
          reject(err);
        } else {
          log.write('INFO', "MySQL Connection created.");
          resolve(client);
        }
      });

    })
  },
  destroy: function(client) {
    return new Promise(function(resolve) {
      client.end(function(err) {
        if (err != null) {
          log.write('ERROR', "DB Error: MySQL: " + err.message);
        } else {
          log.write('INFO', "Database connection closed.");
          resolve();
        }
      });
    })
  }
}

const cp = genericPool.createPool(poolFactory);

Test query that triggers the connection error: 测试触发连接错误的查询:

cp.acquire().then(
  function(client) {
    client.query('USE ' + config.db, function(err, results, fields) {
      if (err != null) {
        log.write('ERROR', "DB test error: MySQL: " + err.message);
      } else {
        log.write('INFO', "MySQL connection tested successfully.");
        cp.release(client)
      }
    });
  }).catch(function(err) {
  cp.release(client);
  log.write('ERROR', "Pool Error: " + err.message);
});

My error log fills up with a million lines of: 我的错误日志填满了以下内容的一百万行:

 Connection Error: MySQL: ER_ACCESS_DENIED_ERROR: Access denied for user 'user'@'localhost' (using password: YES)

I am expecting a single error because I am testing error conditions. 我正在期待一个错误,因为我正在测试错误情况。 What am I doing wrong to get the infinite loop? 我在做无限循环时做错了什么? I thought the reject(err) was supposed to put the promise in a state where it would not answer any more queries? 我以为拒绝(err)应该将诺言置于无法回答更多查询的状态?

Can anyone point me in the right direction? 谁能指出我正确的方向?

As always - thank-you very much! 一如既往-非常感谢!

EDIT : Here is a complete script that illustrate the problem if anyone cares to see the problem first hand! 编辑 :这是一个完整的脚本,用于说明问题,如果有人愿意亲自看问题! The console fills up with "ERROR MySQL Connection Error: ER_ACCESS_DENIED_ERROR: Access denied for user 'devUser'@'localhost' (using password: YES)". 控制台填满“ ERROR MySQL Connection Error:ER_ACCESS_DENIED_ERROR:用户'devUser'@'localhost'的访问被拒绝(使用密码:是)”。 Thanks again. 再次感谢。

// Test App to show logging issue

var pool = require('generic-pool'),
  mysql = require('mysql')

var config = {
  port: 8880,
  host: 'localhost',
  user: 'devUser',
  pass: 'wrong-pass',
  db: 'node-app-db'
}

const poolConfig = {
  max: 3
};

const poolFactory = {
  create: function() {
    return new Promise(function(resolve, reject) {
      var client = mysql.createConnection({
        host: config.host,
        user: config.user,
        password: config.pass,
      });
      client.connect(function(err) {
        if (err != null) {
          console.log('ERROR', "MySQL Connection Error: " + err.message);
          reject(err);
        } else {
          console.log('USAGE', "MySQL Connection created. " + cp.size + " of " + config.poolSize + " connections to DB in use.");
          resolve(client);
        }
      });

    })
  },
  destroy: function(client) {
    return new Promise(function(resolve) {
      client.end(function(err) {
        if (err != null) {
          console.log('ERROR', "DB Error: MySQL: " + err.message);
        } else {
          console.log('USAGE', "Database connection closed. Pool contains ' + cp.size + ' more connections.");
          resolve();
        }
      });
    })
  }
}

const cp = pool.createPool(poolFactory, poolConfig);

cp.acquire().then(
  function(client) {
    client.query('USE ' + config.db, function(err, results, fields) {
      if (err != null) {
        console.log('ERROR', "DB test error: MySQL: " + err.message);
      } else {
        console.log('READY', "MySQL connection tested successfully. DataServer ready for connections.");
        cp.release(client)
      }
    });
  }).catch(function(err) {
  cp.release(client);
  console.log('ERROR', "Pool Error: " + err.message);
});

Cross posting from my original response on the github issue 我对github问题的原始回复中的交叉发布

hey @whiteatom - yep you've hit "known problem" land... 嘿@whiteatom-是的,你打“已知问题”土地...

for some history... In v2 a given call to pool.acquire was directly tied to a single call to factory.create and an error in the factory.create would bubble through to the pool.acquire . 对于一些历史...在V2给定的通话pool.acquire直接绑定到一个呼叫factory.create ,并在错误factory.create将通过气泡pool.acquire (this was generally bad ™️ ) In v3 calls to pool.acquire were no longed tied to calls to factory.create and therefore any errors in factory.create could not bubble up to pool.acquire calls because it didn't make any semantic sense. (这是普遍不好™️)在v3中调用pool.acquire没有渴望绑调用factory.create ,因此在任何错误factory.create不能泡高达pool.acquire电话,因为它没有做任何语义意义。 Instead factory errors are now exposed via the Pool itself via event emitters 现在, factory错误现在通过事件发送器通过Pool本身公开

So to sum up slightly: the promise returned by pool.acquire will only reject due to errors specifically related to the acquire call (such as timeout), and not because of any other errors in the pool. 所以稍微总结一下:在promise由归国pool.acquire只会拒绝因具体涉及到的错误acquire调用(如超时),并没有因为池中的任何其他错误的。 To catch general errors with the pool you will need to attach some event listeners to the Pool instance you have. 要捕获池中的一般错误,您需要将一些事件侦听器附加到您拥有的Pool实例上。

There is still the problem that it's possible for the pool to end up in some infinite loop if your factory.create returns promises that only ever reject. 仍然存在一个问题,如果factory.create返回的允诺只会拒绝,则池可能会陷入无限循环。 To get around this you can build backoff functionality into your factory.create function (this is a bit of a hack though, and I really need to find some way to put backoff in the pool itself.) 为了解决这个问题,您可以在factory.create函数中构建退避功能(不过这有点hack,我确实需要找到某种方法来将退避放到池中。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM