导致我们生产数据库宕机的连接泄漏

发布: (2026年1月1日 GMT+8 05:35)
3 min read
原文: Dev.to

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 点的叫醒电话。

Back to Blog

相关文章

阅读更多 »