# Postgres connection pooling rehearsal

Local rehearsal for **direct vs transaction-pooled** Postgres connections before
you scale application replicas. Uses disposable Postgres 16 + PgBouncer 1.23 on
loopback — never a customer database.

Source review (Bex contract pin): [`source-review.md`](./source-review.md).

Guide (after publish): [Postgres pooling before scaling](/docs/platform/postgres-pooling).

## Prerequisites

- Node.js ≥ 22.12
- Docker (pulls `postgres:16-alpine` and `edoburu/pgbouncer:v1.23.1-p0`)

## Quick start

```bash
npm ci
npm run setup          # labeled Postgres :55435 + PgBouncer :55436
npm run migrate        # schema via direct URL (session-safe path)
npm run load -- --path both --replicas 3 --requests 30
npm run inspect
npm test
npm run teardown -- --containers
```

Public download (no CMS checkout):

```bash
curl -fsSLO https://bex.co/examples/postgres-pooling-latest.zip
curl -fsSLO https://bex.co/examples/postgres-pooling-latest.zip.sha256
# verify: compare SHA-256 of the zip to the first field of the sidecar
unzip postgres-pooling-latest.zip && cd postgres-pooling
```

## What it proves

| Observation | Direct | Pooled (transaction) |
| --- | --- | --- |
| Many app clients | Backend count rises with clients | Backend count stays near `default_pool_size` |
| Successful SELECT alone | Not proof of pooling | Must also read pooler/backends |
| `SET` session state | Sticky on one connection | Not a migration/admin path |

Fixture numbers are explanatory inputs, not Bex plan capacity.

## Bex Blueprint sketch

See `render.yaml`: database `connectionPool: pgbouncer` and web service
`fromDatabase.connectionPoolString`. Hosted URLs and TLS come from your
installation's connection-info response — do not copy local loopback hosts.
