Node v12与v14环境下pg连接池结合Promise.all执行异常问题排查
pg连接池跨Node版本挂起异常排查
异常表现
- Node v12.20.1环境下运行代码,流程可正常走完,日志输出符合预期(依次打印2、1)
- 切换到Node v14.16.0环境后,仅打印日志2,
Promise.all的then、catch回调均未触发,无任何错误抛出 - 逐步断点排查后定位卡点在
updateTask函数的客户端获取环节:执行到await pool.connect()后,后续的console.log(4)完全没有输出 - 复现使用的pg驱动版本为8.5.1
复现代码
连接池初始化与调度逻辑
const pool = new Pool({ user: 'hoangvu', host: '127.0.0.1', database: 'hoangvu', password: 'hoangvu', port: 5432, query_timeout: 10000 }); const promiseArray = [updateTask(pool)]; console.log(2); Promise.all(promiseArray) .then(result => { console.log(1); pool.end(); }) .catch(err => { console.log(err); });
业务查询函数实现
async function updateTask(pool) { console.log(3); const client = await pool.connect().catch(err => { throw err; }); await client.query('BEGIN'); // 业务查询逻辑 await client.query('COMMIT'); return 1; }
定位到的卡点代码段
const client = await pool.connect().catch(err => { throw err; }); console.log(4);
问题成因
- 首先是Node不同版本的事件循环机制差异:Node12版本中,只要存在处于pending状态的Promise,事件循环就会保持进程存活,不会主动退出;Node14开始,当所有已注册的IO事件、定时器执行完成后,即使存在未settle的pending Promise,事件循环也会判定无待处理任务,直接触发进程退出。
- 其次是pg 8.5.1版本的兼容性bug:该版本的连接池在首次建立客户端连接时,没有正确覆盖连接失败的异常分支——当TCP连接建立过程中出现网络超时、静默丢包、PostgreSQL侧无响应时,
pool.connect()返回的Promise会一直停留在pending状态,既不resolve也不reject。 - 两者叠加后就出现了Node12下流程可以正常走完、Node14下直接无报错退出的现象:Node12下即使连接卡住,进程也会一直挂着等待最终的连接结果,最终连接建立完成后顺利走完流程;Node14下连接卡住后,事件循环发现没有其他待处理任务,直接退出进程,后续的日志、回调都没有机会执行。
解决方向
- 先确认本地PostgreSQL服务的连通性:验证127.0.0.1:5432端口可正常访问,对应账号的数据库权限匹配,没有防火墙拦截连接请求
- 给
pool.connect()增加超时兜底逻辑,避免Promise永久挂起:const client = await Promise.race([ pool.connect(), new Promise((_, reject) => setTimeout(() => reject(new Error('数据库连接超时')), 10000)) ]) - 升级pg驱动版本到8.6.0及以上,官方在该版本修复了连接建立阶段无理由挂起的兼容问题
- 注意
pool.end()的调用时机,需要等待所有客户端连接释放完成后再执行关闭逻辑,避免提前关闭导致的连接异常
内容的提问来源于stack exchange,提问作者Nguyen Hoang Vu
相关产品推荐
相关产品推荐

