docs: warn about postgres.js pipelining on Supavisor transaction mode (#50712)

Re-does #50082 against the redesigned `connecting-to-postgres.mdx` (see
[Slack
discussion](https://supabase.slack.com/archives/C04JR9DBNQL/p1789991651381399?thread_ts=1788980733.712609&cid=C04JR9DBNQL)).



What changed vs. #50082:
- `connecting-to-postgres.mdx`: the pipelining caution now lives in the
new **Transaction mode limitations** section (the old "Pooler
transaction mode" prose it was in got redesigned), and is a short
redirect to the Postgres.js guide rather than a full explanation.
- `postgres-js.mdx`: keeps the full warning, the `{ prepare: false }`
workaround, and now explains *why* pipelining can't just be turned off
(`max_pipeline: 0` breaks `sql.begin()`, tracked in
porsager/postgres#1189), plus a "contact support" prompt for anyone
still stuck, and a note that Supavisor v2.10 will add native pipelining
support.
- Drops the standalone troubleshooting page from #50082 — the team
wasn't confident enough yet to officially point everyone at the
`postgres.js` patch, so support is the escalation path for now instead.
- Re-adds the `Rule003Spelling.toml` allow-list entry for "pipelining"
(not present on `master`).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Clarified that query pipelining is unsupported in transaction mode and
may cause hangs or mismatched results.
* Documented Postgres.js pipeline behavior, recommended workarounds, and
planned native support in a future Supavisor release.
* Updated the Postgres.js connection example to disable prepared
statements for improved compatibility.
* Added a support contact link for assistance with transaction-mode
pipeline issues.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

## Preview

* pipelining mentioned in the Supavisor TX mode:
https://docs-git-docs-postgres-js-pipelining-warning-supabase.vercel.app/docs/guides/database/connecting-to-postgres#pooler-transaction-mode
* pipelining mentioned in more detail in TX mode limitations:
https://docs-git-docs-postgres-js-pipelining-warning-supabase.vercel.app/docs/guides/database/connecting-to-postgres#transaction-mode-limitations
* `postgres.js` pipelining warning with even more details:
https://docs-git-docs-postgres-js-pipelining-warning-supabase.vercel.app/docs/guides/database/postgres-js

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Miranda Limonczenko <miranda.limonczenko@supabase.io>
This commit is contained in:
authored and GitHub committed 2026-09-25 10:51:54 +02:00
1 parent cbd8889fc2
commit c3c741c20e
2 files changed
+25 -3

No files matched your search

@@ -84,6 +84,12 @@ Transaction mode does not support [prepared statements](https://postgresql.org/d
</Admonition>
<Admonition type="caution">
Transaction mode does not support [query pipelining](https://www.postgresql.org/docs/current/libpq-pipeline-mode.html). See [Transaction mode limitations](#transaction-mode-limitations) for mode details.
</Admonition>
```txt
postgresql://postgres.[PROJECT-REF]:[YOUR-PASSWORD]@[POOLER-HOST]:6543/postgres
```
@@ -168,7 +174,7 @@ For more on sizing an application-side pool, see the [Supavisor FAQ](/docs/guide
### Transaction mode limitations
[Transaction mode](#pooler-transaction-mode) returns your connection to the pool after each transaction, so anything that depends on session state doesn't survive between transactions. Three things are affected.
[Transaction mode](#pooler-transaction-mode) returns your connection to the pool after each transaction, so anything that depends on session state doesn't survive between transactions. Four things are affected.
**Prepared statements** aren't supported, so turn them off. Each driver does this differently:
@@ -185,7 +191,15 @@ For node-postgres, Psycopg, and Rust drivers, see [Disabling prepared statements
**Session-level state** is lost between transactions. This covers `set` and `reset`, session-level advisory locks, `listen` and `notify`, and temporary tables. Run them inside the transaction that needs them, or use session mode or a direct connection instead.
Direct connections and session mode support all three, so none of this applies to either.
**Query pipelining** isn't supported: transaction mode returns a connection to the pool as soon as it sees one reply finish, even if the client already queued more queries on it.
Direct connections and session mode support all four, so none of this applies to either.
<Admonition type='caution'>
`postgres.js` pipelines queries by default, so this combination can hang queries or return mismatched rows — see the [Postgres.js guide](/docs/guides/database/postgres-js) for how to avoid it.
</Admonition>
### SSL [#connecting-with-ssl]
@@ -38,6 +38,14 @@ hideToc: true
To get your connection details, go to the [**Connect** panel](/dashboard/project/_?showConnect=true). Choose [**Transaction pooler**](/dashboard/project/_?showConnect=true&method=transaction) if you're on a platform with transient connections, such as a serverless function, and [**Session pooler**](/dashboard/project/_?showConnect=true&method=session) if you have a long-lived connection. Copy the URI and save it as the environment variable `DATABASE_URL`.
<Admonition type="caution">
`postgres.js` pipelines queries by default. Combined with the [shared pooler transaction mode](/docs/guides/database/connecting-to-postgres#pooler-transaction-mode), this can hang queries or return mismatched rows. `postgres.js` has no working option to turn pipelining off directly: `max_pipeline: 0` breaks `sql.begin()` transactions instead, a known upstream [bug](https://github.com/porsager/postgres/issues/1189).
For more about pipelining with `postgres.js`, [contact support](/dashboard/support/new).
</Admonition>
</StepHikeCompact.Details>
<StepHikeCompact.Code>
@@ -47,7 +55,7 @@ hideToc: true
import postgres from 'postgres'
const connectionString = process.env.DATABASE_URL
const sql = postgres(connectionString)
const sql = postgres(connectionString, { prepare: false })
export default sql
```