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
相关产品推荐
相关产品推荐

