Skip to content

feat(db): support transaction-pooling poolers (pgbouncer / RDS Proxy / Supabase) - #299

Merged
telivity-otaip merged 2 commits into
TelivityAI:mainfrom
modernitconsultants:feat/pooler-support
Aug 11, 2026
Merged

feat(db): support transaction-pooling poolers (pgbouncer / RDS Proxy / Supabase)#299
telivity-otaip merged 2 commits into
TelivityAI:mainfrom
modernitconsultants:feat/pooler-support

Conversation

@modernitconsultants

Copy link
Copy Markdown
Contributor

Running HAIP against Postgres through pgbouncer in transaction mode, the API dies on its second concurrent query. Two causes, both in how postgres.js is initialised.

1. Named prepared statements. postgres.js prepares by default. Under transaction pooling the statement is prepared on one backend connection and executed on another, which fails. The driver supports prepare: false for exactly this case, but there's no way to reach it from configuration today.

2. TLS. Poolers commonly terminate TLS with a private or self-signed certificate, and sslmode in the connection URL is not honoured consistently across postgres.js versions — so a URL parameter looks like it works and doesn't.

The change

Two environment switches, both defaulting to current behaviour so nothing changes for direct connections:

  • DATABASE_POOLER_MODE=transactionprepare: false
  • DATABASE_SSL=no-verifyssl: { rejectUnauthorized: false }

Applied at all three connection sites (apps/api database module, seed, push-schema), since the init containers reach the database through the same pooler. Documented in .env.example and .env.production.example.

Direct connections keep prepared statements, which are a real win when you own the connection — the switch only opts out where the pooler makes them impossible.

Testing

Verified against pgbouncer in transaction mode with a self-signed certificate: without these, push-schema exits on PostgresError: SSL required and the API fails on concurrent queries; with them, schema push and normal operation both succeed. pnpm build && pnpm -r test green.

Happy to split the TLS half into its own PR if you'd prefer them separate.

modernitconsultants and others added 2 commits August 11, 2026 19:46
postgres.js prepares statements by default. Under a transaction-pooling pooler
(pgbouncer, RDS Proxy, Supabase) the statement is prepared on one backend
connection and executed on another, so the API fails on its second concurrent
query with no way to opt out from configuration.

Poolers also commonly terminate TLS with a private or self-signed certificate,
and sslmode in the connection URL is not honoured consistently across
postgres.js versions.

Adds two environment switches, both defaulting to current behaviour so direct
connections are unchanged:

  DATABASE_POOLER_MODE=transaction  -> prepare: false
  DATABASE_SSL=no-verify            -> ssl: { rejectUnauthorized: false }

Applied at all three connection sites (api module, seed, push-schema) since the
init containers reach the database through the same pooler. Documented in both
env examples.
Maintainer polish on top of the pooler-support change: one
postgresOptionsFromEnv helper for api/seed/push-schema, plus a louder
MITM warning for DATABASE_SSL=no-verify in the env examples.

Co-authored-by: telivity-otaip <telivity-otaip@users.noreply.github.com>

@telivity-otaip telivity-otaip left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a solid fix — thank you, Charles. postgres.js named prepares under transaction pooling and unreliable URL sslmode were exactly the failure mode.

Merging with full credit to you. Small maintainer polish on top of your PR (not changing the design):

  • shared postgresOptionsFromEnv so api / seed / push-schema can’t drift
  • louder DATABASE_SSL=no-verify warning in .env.example and .env.production.example

(tools/haip-connect-gpt left alone — different package / TOOL_LOG_DATABASE_URL, no clean shared dep.)

Appreciate you running HAIP through a real pooler and sending this back.

@telivity-otaip
telivity-otaip merged commit a19c2d7 into TelivityAI:main Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants