.env.example and the README still told people to use pgCat on :6432, the
exact setup that hangs on every parameterised query. With no prod and one
backend instance there is nothing for a pooler to multiplex, and
postgres-js already pools in-process, so DATABASE_URL now points at
Postgres on :5432 in .env.example, dev-env.sh and drizzle.config.ts.
prepare:false is kept in src/db/index.ts on purpose, so adding
PgBouncer later (tested working) is a config change rather than a code
change. The long measurement narrative in that file's comment is cut to
the decision and the pointer to PLAN.md, where the numbers now live.
The previous commit named PgBouncer as the pooler this client suits, from
background knowledge. It is now tested: PgBouncer 1.22 in transaction
mode (pool of 2, max_prepared_statements 0) passes every query that
hangs through pgCat 1.2.0, multiplexes 40 concurrent parameterised
queries over 2 server connections, and runs scripts/e2e-authz.sh to a
full PASS with 68 checks.
A real pooled database became available, so the deployment assumption in
src/db/index.ts - "points at pgCat/PgBouncer, so keep prepared statements
disabled" - was finally testable. It does not hold.
Through pgCat 1.2.0 an unparameterised `select 1` succeeds, and so does
`select count(*) from organisations`, but any query carrying a bind
parameter hangs forever. That is true with prepare:false, with
prepare:true, with fetch_types:false, and inside an explicit
transaction. The same queries straight at Postgres all pass, and psql's
\bind through the same pool returns its row, so the fault is specific to
how postgres-js frames those messages rather than to extended protocol.
Every Drizzle query binds parameters, so no subset of this app works
there. drizzle-kit hung at commit for the same reason - it builds its
own client and drizzle.config.ts cannot pass prepare:false - having
executed all 14 statements of 0000, which then rolled back.
Transaction-mode pooling itself was confirmed, not assumed: in one
client session consecutive statements hopped between server backends
while statements inside BEGIN..COMMIT stayed pinned to one.
So the comment now records what was measured, names PgBouncer as the
pooler this configuration actually suits, and says migrations go
straight at Postgres. dev-env.sh's note gets the same correction.
Two real bugs found on the way there:
- e2e-authz.sh and e2e-edge.sh hardcoded `psql -h localhost -U continuum
-d continuum` while both read DATABASE_URL for the backend under test.
Point DATABASE_URL elsewhere and the suite seeds fixtures into one
database while exercising another - and its own failure message says
"no database at $DATABASE_URL", naming a URL nothing ever tried. Both
now use `psql "$DATABASE_URL"`.
- drizzle.config.ts fell back to port 6432, where nothing listens here,
so a bare db:migrate failed confusingly. It now matches dev-env.sh's
DEV_DATABASE_URL.
bun test 128 pass / 0 fail; e2e-authz.sh PASS with 68 checks against
local Postgres after the psql_q change. e2e-edge.sh was not re-run
because it drives the real printers, and its change is the same line.