Find and Fix a Poisoned PostgreSQL Connection Pool

Computer Science

Find and Fix a Poisoned PostgreSQL Connection Pool

Node.jsExpressnode-postgres (pg)

Find and Fix a Poisoned PostgreSQL Connection Pool

You will build a small Node.js diagnostic service that reproduces session-level read-only state leaking through a transaction pool, proves why later writes fail, and repairs the leak with ROLLBACK and DISCARD ALL. By the end, you can run the project locally and demonstrate both the failure and the safe reset.

3 modules9 lessonsComputer ScienceNode.jsExpressnode-postgres (pg)PostgreSQLnpmHTTP and JSON

What you learn by building this

  • Explain how transaction-mode pooling reuses underlying PostgreSQL sessions
  • Distinguish transaction-scoped read-only behavior from session-level state leakage
  • Reproduce a poisoned pool with SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY
  • Identify the difference between a poisoned pool error and a genuinely read-only cluster or replica
  • Reset affected connections safely with ROLLBACK and DISCARD ALL
  • Design application cleanup paths that do not return dirty connections to a pool

Learning Journey

1

Build a Visible Database-State Demonstrator

2 lessons

Create the standalone service and database fixture that makes session state observable before introducing pooling failure behavior.

2

Reproduce the Poisoned Pool

4 lessons

Use a deliberately small pool to show how session-level read-only state survives one request and breaks a later write.

3

Reset and Prevent the Leak

3 lessons

Turn the reproduction into a recovery tool, verify both reset strategies, and leave the project with a practical prevention runbook.

Public lesson

Create the minimal diagnostic service

A small health check that really touches Postgres

A service can return HTTP 200 while its database connection is unusable. That makes a plain “server is running” route a weak diagnostic: it checks Express, but not the dependency that later connection-pool experiments will exercise.

You will add one small route that asks Postgres a deliberately simple question. The route should return success only when both pieces are alive:

HTTP request → Express → node-postgres pool → Postgres

I could not retrieve the supplied article URL in this environment, so this lesson stays within the packet’s stated scope rather than attributing additional details to the article.

Tasks

Find the existing server entry point

In the ready project, inspect the existing server file and the project’s documented start command. Do not create a second server if one already exists. You are looking for the place where Express is created and where the service begins listening.

Also find the project’s existing database configuration, if present. Keep its connection settings rather than inventing a new environment-variable name.

If the project already has a Pool, use it. If it has only an Express app, add the pool beside the app using the project’s existing database configuration.

The important shape is:

import express from 'express';
import { Pool } from 'pg';

const app = express();

// Keep the project's existing connection configuration here.
// If the project already creates a pool, do not create another one.
const pool = new Pool(/* existing database configuration */);

The pool is intentionally created once, outside the route handler. A pool represents reusable database connections; creating one for every request would make this diagnostic service obscure the very connection behavior we want to observe later.

Tasks

Add the database-backed health route

In the same server file, add a route skeleton like this and complete the missing query:

app.get('/health', async (_request, response) => {
  try {
    const result = await pool.query(/* write a tiny read-only SQL query */);

    response.status(200).json({
      status: 'ok',
      database: result.rows[0],
    });
  } catch (error) {
    console.error('Health check failed:', error);

    response.status(503).json({
      status: 'error',
      database: 'unavailable',
    });
  }
});

For the query, use a read-only statement that does not depend on application tables. A useful choice is:

SELECT 1 AS database_ok

This is enough for the first diagnostic. It proves that the request reached Postgres through node-postgres; it does not require migrations or business data.

The 503 branch matters. Without it, an exception could become an unhelpful generic server error, and a monitoring check would have less clear evidence that the database—not Express—is unavailable.

Keep the project’s existing listen code below the route. If the file does not yet have one, use the existing project convention for starting the service rather than adding a second startup path.

Tasks

Run the check

Start the service with the project’s documented command. Then request the health route using the project’s documented way to make HTTP requests, or open the route in a browser if that is how the project is normally checked.

The successful response should be shaped like:

{
  "status": "ok",
  "database": {
    "database_ok": 1
  }
}

The exact formatting may differ, but notice two facts:

  • the response is JSON rather than plain text;
  • the JSON contains evidence returned by Postgres.

That second point distinguishes this route from an endpoint that merely says “the process is alive.”

Tasks

Make the check fail on purpose

Now stop or otherwise make the project’s database unavailable using the project’s normal local workflow, then request /health again.

You should see:

  • an HTTP 503 response;
  • "status": "error" in the JSON;
  • a server-side log containing the database error.

Restore the database and run the request once more. The route should return to the successful response.

This small failure-and-recovery experiment is worth doing now. It shows what the route actually measures: the same request path can succeed or fail depending on whether the pool can obtain a usable database connection. The query is tiny, so any failure is easier to attribute to connectivity or pool state rather than application logic.

Tasks

Leave the SELECT 1 AS database_ok query and the single shared pool in place. Later demonstrations can use this stable probe to tell the difference between a healthy service, a service whose database access is failing, and a service that has recovered.

Course Outline

3 modules · 9 lessons

Build a Visible Database-State Demonstrator

Reproduce the Poisoned Pool

Reset and Prevent the Leak

Learn by building your own version.

Remix this public project to open the workspace, follow the guided build, and let the AI mentor teach you through the work instead of doing it for you.