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
函数中构建退避功能(虽然这有点不合时宜,我真的需要找到一些方法在池本身中加入退避。)
我最近将我的通用池包升级到使用 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
函数中构建退避功能(虽然这有点不合时宜,我真的需要找到一些方法在池本身中加入退避。)