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

Node.js connection pool produces infinite loop on error

我最近将我的通用池包升级到使用 promises 的版本 3 - 我可能不太了解这个概念。尽管在操作上有一些意想不到的差异,但我还是设法让它工作了。

我遇到的问题是当我开始测试错误条件时。我故意设置了错误的密码,并且在测试时,我得到了 "connection failed" 错误的无限循环 - 表明尽管有错误,但某些东西正在触发创建功能。我假设我没有正确配置池,或者我获取不正确。

通用池工厂:

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

触发连接错误的测试查询:

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

我的错误日志有一百万行:

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

我预计会出现一个错误,因为我正在测试错误情况。获得无限循环我做错了什么?我认为 reject(err) 应该将 promise 置于不再回答任何查询的状态?

谁能给我指出正确的方向?

一如既往 - 非常感谢!

编辑:这是一个完整的脚本,如果有人关心第一手的问题,可以说明问题!控制台填满 "ERROR MySQL Connection Error: ER_ACCESS_DENIED_ERROR: Access denied for user 'devUser'@'localhost' (using password: YES)"。再次感谢。

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

我对 github 问题的原始回复的交叉发布

嘿@whiteatom - 是的你已经击中 "known problem" 土地...

一些历史... 在 v2 中,对 pool.acquire 的给定调用直接绑定到对 factory.create 的单个调用,并且 factory.create 中的错误会冒泡到 pool.acquire。 (这通常很糟糕™️) 在 v3 中,对 pool.acquire 的调用不再与对 factory.create 的调用相关联,因此 factory.create 中的任何错误都不会冒泡到 pool.acquire 调用,因为它没有产生任何语义感觉。相反 factory 错误现在通过 Pool 本身通过 event emitters

公开

所以稍微总结一下:pool.acquire 返回的 promise 只会由于与 acquire 调用相关的错误(例如超时)而拒绝,而不是因为池中的任何其他错误。要捕获池的一般错误,您需要将一些事件侦听器附加到您拥有的 Pool 实例。

仍然存在一个问题,如果您的 factory.create returns 承诺只会拒绝,那么池可能会陷入无限循环。为了解决这个问题,您可以在 factory.create 函数中构建退避功能(虽然这有点不合时宜,我真的需要找到一些方法在池本身中加入退避。)