导致我们生产数据库宕机的连接泄漏
Source: Dev.to
It was 3 AM. PagerDuty woke me up. Our API was returning 500 errors.
The database was fine. CPU was fine. Memory was fine. But every query was timing out.
FATAL: too many connections for role "app_user"
We had exhausted our 100‑connection limit even though traffic was normal. Where were all the connections going? After hours of debugging we found the culprit.
隐藏在代码库中的连接泄漏
// ❌ Bad: missing client.release()
async function getUserOrders(userId) {
const client = await pool.connect();
const orders = await client.query('SELECT * FROM orders WHERE user_id = $1', [
userId,
]);
return orders.rows;
// Where's client.release()? 🤔
}
Every call leaked a connection. With 50 requests/minute we exhausted the pool in 2 minutes.
常见泄漏场景
| 场景 | 结果 |
|---|---|
完全忘记 release() | 连接从未归还 |
在 release() 之前提前返回 | 连接泄漏 |
| 抛出异常 | finally 块缺失 |
| 异步错误 | 未处理的拒绝,没有清理 |
修复泄漏
始终在 finally 块中释放
// ✅ Good: release in finally
async function getUserOrders(userId) {
const client = await pool.connect();
try {
const orders = await client.query(
'SELECT * FROM orders WHERE user_id = $1',
[userId],
);
return orders.rows;
} finally {
client.release(); // Always executes
}
}
对于简单查询,优先使用 pool.query()
// ✅ Best pattern: use pool.query() directly
async function getUserOrders(userId) {
const orders = await pool.query('SELECT * FROM orders WHERE user_id = $1', [
userId,
]);
return orders.rows;
}
使用 ESLint 检测缺失的 release
npm install --save-dev eslint-plugin-pg
// .eslintrc.js
import pg from 'eslint-plugin-pg';
export default [pg.configs.recommended];
The rule no-missing-client-release flags any pool.connect() call where client.release() is not guaranteed on all code paths.
src/orders.ts
3:17 error 🔒 CWE-772 | Missing client.release() detected
Fix: Add client.release() in finally block or use pool.query() for simple queries
规则检查内容
- 每个
pool.connect()调用 - 函数的每条可能的执行路径
- 是否在所有路径上调用了
client.release() - 是否放在
finally块中(推荐)
采用后的结果
- 6 个月内 0 次连接泄漏
- 不再有因连接耗尽而在凌晨 3 点的警报
- CI 现在在问题进入预发布环境前就能捕获
入门指南
npm install --save-dev eslint-plugin-pg
import pg from 'eslint-plugin-pg';
export default [pg.configs.recommended];
规则文档: no-missing-client-release
包: eslint-plugin-pg (npm)
不要等到下一个凌晨 3 点的叫醒电话。