DEV Community

Cover image for Post-Mortem: The Connection Leak Outage (And the Static Analysis Standard)
Ofri Peretz
Ofri Peretz

Posted on • Edited on • Originally published at ofriperetz.dev

Post-Mortem: The Connection Leak Outage (And the Static Analysis Standard)

Connection leaks aren't just bugs—they are production-killing events. Here is the post-mortem of an outage we survived, and the automated static analysis standard we built to make it biologically impossible to repeat.

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.

The Problem

FATAL: too many connections for role "app_user"
Enter fullscreen mode Exit fullscreen mode

We had exhausted our 100-connection limit. But our traffic was normal. Where were all the connections going?

The Leak

After hours of debugging, we found it:

// ❌ The connection leak hiding in our codebase
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()? 🤔
}
Enter fullscreen mode Exit fullscreen mode

Every call leaked a connection. With 50 requests/minute, we exhausted the pool in 2 minutes.

Why This Happens

Scenario Result
Forgot release() entirely Connection never returned
Early return before release() Connection leaked
Exception thrown finally block missing
Async error Unhandled rejection, no cleanup

The Correct Pattern

// ✅ Always release in finally block
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
  }
}
Enter fullscreen mode Exit fullscreen mode

Or even better—don't use connect() at all for simple queries:

// ✅ 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;
}
Enter fullscreen mode Exit fullscreen mode

Let ESLint Catch This

npm install --save-dev eslint-plugin-pg
Enter fullscreen mode Exit fullscreen mode
import pg from 'eslint-plugin-pg';
export default [pg.configs.recommended];
Enter fullscreen mode Exit fullscreen mode

Now every missing release is caught:

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
Enter fullscreen mode Exit fullscreen mode

The Rule: no-missing-client-release

This rule tracks:

  • Every pool.connect() call
  • Every code path through the function
  • Whether client.release() is called on all paths
  • Whether it's in a finally block (recommended)

Production Impact

After deploying this rule:

  • 0 connection leaks in 6 months
  • No more 3 AM pages for connection exhaustion
  • CI catches issues before they reach staging

Quick Install

npm install --save-dev eslint-plugin-pg
Enter fullscreen mode Exit fullscreen mode
import pg from 'eslint-plugin-pg';
export default [pg.configs.recommended];
Enter fullscreen mode Exit fullscreen mode

Don't wait for the 3 AM wake-up call.


📦 npm: eslint-plugin-pg
📖 Rule docs: no-missing-client-release

⭐ Star on GitHub


The Interlace ESLint Ecosystem
Interlace is a high-fidelity suite of static code analyzers designed to automate security, performance, and reliability for the modern Node.js stack. With over 330 rules across 18 specialized plugins, it provides 100% coverage for OWASP Top 10, LLM Security, and Database Hardening.

Explore the full Documentation

© 2026 Ofri Peretz. All rights reserved.


Build Securely.
I'm Ofri Peretz, a Security Engineering Leader and the architect of the Interlace Ecosystem. I build static analysis standards that automate security and performance for Node.js fleets at scale.

ofriperetz.dev | LinkedIn | GitHub

Top comments (0)