You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express应用中pg-promise随机停止建立新连接且无报错

解决pg-promise在Express应用中随机阻塞连接的问题

你的问题确实挺棘手的——pg-promise随机停掉数据库连接,简单查询都卡壳,重启应用还不一定能恢复,而且完全没错误日志。结合你给出的pg-monitor输出(只有连接和task启动记录,没结束或错误事件),核心大概率是连接池被未释放的连接占满阻塞了,我整理了几个排查和解决方向:

1. 先检查任务/事务的收尾逻辑

你用db.task包裹查询的代码里,藏着一个潜在风险:如果异步流程中出现未捕获的错误,或者代码路径没走完,task可能无法正确释放连接。比如你的测试路由:

app.get(apiPrefix + '/fail', async (req: express.Request, res: express.Response, next: NextFunction) => { 
  await db.task(uuid(), async (tx) => { 
    const time = await tx.one('select now() as time'); 
    res.json({status: 'OK', time: time.time}); 
  }); 
});

建议改成用try/catch包裹整个task流程,确保无论成功失败,连接都能被正确回收:

app.get(apiPrefix + '/fail', async (req: express.Request, res: express.Response, next: NextFunction) => { 
  try {
    // 把查询结果从task里返回,再处理响应
    const timeResult = await db.task(uuid(), async (tx) => { 
      return tx.one('select now() as time'); 
    });
    res.json({status: 'OK', time: timeResult.time}); 
  } catch (err) {
    next(err); // 交给错误中间件,确保task能正常结束释放连接
  }
});

另外要注意:如果task内部有遗漏的await,会导致异步操作脱离管控,task无法感知到流程结束,连接就会一直被占着。

2. 给连接池加上超时和监控配置

默认的pg-promise连接池没有超时机制,一旦连接被意外占用,新请求会无限等待。建议显式配置池的参数,同时加强监控:

const pgp = require('pg-promise')({
  pool: {
    max: 10, // 别超过PostgreSQL的max_connections配置,按需调整
    idleTimeoutMillis: 30000, // 闲置30秒的连接自动释放
    acquireTimeoutMillis: 10000, // 获取连接超时10秒就抛出错误
  },
  monitor: {
    event: (e) => {
      // 记录task结束事件
      if (e.finish) {
        console.log(`Task ${e.taskId} 完成,连接已释放`);
      }
      // 捕获错误事件
      if (e.error) {
        console.error(`Task ${e.taskId} 出错:`, e.error);
      }
      // 实时打印连接池状态
      if (e.event === 'connect' || e.event === 'release') {
        console.log(`连接池状态:活跃${e.pool.count}个,闲置${e.pool.idle}个`);
      }
    }
  }
});

这样当连接获取超时的时候,错误中间件就能捕获到日志,你也能实时看到连接池是不是被占满了。

3. 去PostgreSQL端查连接状态

有时候问题不在Node这边,而是PostgreSQL里有挂起的连接。问题发生时,登录数据库执行这条查询:

SELECT pid, datname, usename, state, query_start, query 
FROM pg_stat_activity 
WHERE datname = 'database-name';

重点看有没有state为idle in transaction的连接——这类连接说明事务没正确提交/回滚,导致连接一直被占用。如果有,那你的代码里肯定有未正确收尾的事务逻辑。

4. 捕获全局未处理的Promise异常

Node里未处理的Promise rejection会让进程状态异常,虽然不会直接崩掉,但可能干扰pg-promise的连接池逻辑。在应用顶部加个全局监听:

process.on('unhandledRejection', (reason, promise) => {
  console.error('未捕获的Promise异常:', promise, '原因:', reason);
  // 必要时可以在这里触发应用重启,避免连接池一直阻塞
});

这样能帮你找到那些藏在代码里、没被捕获的异步错误,它们很可能就是连接没释放的元凶。

5. 检查pg-promise和pg的版本兼容性

版本不兼容也会导致奇怪的连接池问题,你可以执行npm list pg pg-promise查看当前版本,然后去pg-promise的官方仓库确认它和依赖的pg包的版本匹配要求,尽量升级到最新稳定版。

最后提醒下:问题发生时先看连接池的活跃连接数是不是达到了max值,如果是,那肯定是有连接没被释放,结合pg_stat_activity的结果和未处理rejection日志,基本就能定位到具体的代码路径了。

内容的提问来源于stack exchange,提问作者Johan Nordberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:59:23