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

Jest-ts集成测试中PostgreSQL连接致TCPWRAP句柄未关闭问题

Jest + TypeScript 集成测试中TCPWRAP句柄未关闭的解决方案

以下是针对你遇到的问题的具体排查和解决步骤:

1. 排查测试用例中的异步残留

所有数据库操作必须确保完全await,任何未处理的异步查询/事务都会占用连接池连接,导致pool.end()无法正常关闭:

  • 检查测试用例里的pool.query、事务操作是否都加了await
  • 如果使用了连接池的客户端(pool.connect()),必须在finally块中调用client.release()释放连接回池,示例:
test('test database transaction', async () => {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    await client.query('INSERT INTO users (name) VALUES ($1)', ['test']);
    await client.query('COMMIT');
  } catch (err) {
    await client.query('ROLLBACK');
    throw err;
  } finally {
    client.release(); // 必须释放,否则连接会被永久占用
  }
});

2. 正确配置afterAll的超时与关闭逻辑

直接调用pool.end()不会等待连接关闭完成,而默认的Jest钩子超时(5秒)可能不足以让连接池完成所有连接的关闭,因此需要:

  • 用await等待pool.end()完成
  • 手动设置更长的超时时间(比如10秒),示例:
import pool from './your-pool-file';

afterAll(async () => {
  await pool.end();
}, 10000); // 延长超时到10秒,根据实际情况调整

3. 优化连接池配置

给连接池添加空闲连接自动回收配置,避免闲置连接残留:

import { Pool } from 'pg';

const pool = new Pool({
  // 你的数据库配置(host, user, password等)
  idleTimeoutMillis: 30000, // 空闲30秒后自动关闭连接
  connectionTimeoutMillis: 2000, // 连接超时时间
  max: 5, // 最大连接数,避免创建过多连接
});

export default pool;

4. 定位具体未关闭的句柄

如果以上步骤无效,可以打印活跃句柄来定位问题根源:

afterAll(async () => {
  await pool.end();
  // 在进程退出前打印所有活跃句柄
  process.on('exit', () => {
    console.log('活跃句柄列表:', process._getActiveHandles());
  });
});

通过输出可以确认是否是连接池的TCP连接,还是其他服务(比如Redis、API客户端)的未关闭连接。

5. 确保连接池是单例实例

避免在每个测试用例中重复创建连接池实例,否则会导致多个未被关闭的池。建议将连接池实例导出为单例,所有测试用例共用同一个,仅在afterAll中关闭一次。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:02:39