Skip to content

fix(adapter-pg): serialize queries on single-connection clients - #29979

Open
gadcam wants to merge 2 commits into
prisma:v7from
gadcam:fix/adapter-pg-serialize-single-connection-queries
Open

fix(adapter-pg): serialize queries on single-connection clients#29979
gadcam wants to merge 2 commits into
prisma:v7from
gadcam:fix/adapter-pg-serialize-single-connection-queries

Conversation

@gadcam

@gadcam gadcam commented Aug 11, 2026

Copy link
Copy Markdown

Problem

@prisma/adapter-pg emits DeprecationWarning: Calling client.query() when the client is already executing a query whenever a query with 2+ sibling relations runs inside a transaction. The query interpreter's join node loads relations concurrently; a pg.Pool tolerates this, but a transaction's single pg.PoolClient does not — and pg@9 will make it a hard error.

Fixes #29407.

Fix

Per @aqrln's review on #29468, serialization is done in the base PgQueryable.performIO rather than in PgTransaction — the constraint is the single connection, not transactions per se. PrismaPgAdapter opts out via a serializeQueries flag: the pool manages its own concurrency, and serializing it would cap the whole application at one query at a time. A small promise-chain lock replaces #29468's async-mutex dependency.

The interpreter is deliberately untouched: join fan-out stays parallel (a regression test now pins this) and adapters own the serialization.

Tests

  • transaction connection never sees overlapping client.query() calls (fails without the fix)
  • pool queries stay parallel — guards against reintroducing a global bottleneck
  • a failed query releases the lock, so an error can't deadlock the rest of the transaction
  • interpreter: join children still load in parallel

Credit

This supersedes #29468 by @matingathani, who identified the issue and proposed the original mutex approach — picking it up as the review feedback has been open since 2026-07-28. Thanks also to @tensordreams for their contributions to that branch.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved transaction query handling so queries execute sequentially and continue correctly after a query failure.
    • Preserved concurrent query execution for connection pools and join-related child queries.
  • Tests

    • Added coverage for transaction serialization, pool concurrency, failure recovery, and concurrent join queries.

@CLAassistant

CLAassistant commented Aug 11, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b3ada4f8-cf96-47cd-8b43-6240edba9fc9

📥 Commits

Reviewing files that changed from the base of the PR and between 7139956 and 9adde52.

📒 Files selected for processing (3)
  • packages/adapter-pg/src/__tests__/pg.test.ts
  • packages/adapter-pg/src/pg.ts
  • packages/client-engine-runtime/src/interpreter/query-interpreter.test.ts

📝 Walkthrough

Walkthrough

The PostgreSQL adapter now serializes queries for single-client transactions, preserves serialization after failures, and allows pool adapters to remain concurrent. Tests cover transaction ordering, pool concurrency, failure recovery, and concurrent join child queries.

Changes

PostgreSQL query serialization

Layer / File(s) Summary
Serialization policy and failure-safe execution
packages/adapter-pg/src/pg.ts, packages/adapter-pg/src/__tests__/pg.test.ts
PgQueryable queues single-client queries and continues the queue after failures. PrismaPgAdapter disables serialization for pools. Tests cover serialized transactions and concurrent pool queries.
Join child-query concurrency validation
packages/client-engine-runtime/src/interpreter/query-interpreter.test.ts
A regression test verifies that three join child queries execute concurrently.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: ⚪ Minimal · up to 9adde

The change is localized to query serialization and its associated tests; no actionable merge-blocking risk remains beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant QueryInterpreter
  participant PgQueryable
  participant pgClient
  QueryInterpreter->>PgQueryable: performIO(query)
  PgQueryable->>pgClient: execute one queued query
  pgClient-->>PgQueryable: return result or failure
  PgQueryable-->>QueryInterpreter: return result
Loading

Possibly related PRs

  • prisma/prisma#29839: Both changes address PostgreSQL query serialization, failure-safe locking, and pool concurrency.
  • prisma/prisma#29954: Both changes modify PgQueryable.performIO and PostgreSQL transaction lifecycle handling.

Suggested labels: lgtm

Suggested reviewers: jacek-prisma, aqrln

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: serializing queries for single-connection PostgreSQL clients.
Linked Issues check ✅ Passed The changes prevent overlapping queries on single clients, preserve pool concurrency, and test failure recovery and parallel relation queries for [#29407].
Out of Scope Changes check ✅ Passed All code and tests support query serialization, pool concurrency, failure recovery, or parallel relation-query behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread packages/client-engine-runtime/src/interpreter/query-interpreter.test.ts Outdated
Comment thread packages/client-engine-runtime/src/interpreter/query-interpreter.test.ts Outdated
Comment thread packages/adapter-pg/src/pg.ts Outdated
return this.#performIO(query)
}
const previous = this.#queryLock
const current = previous.then(() => this.#performIO(query))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will reject all pending queries with the exact same error if a previous query in the chain fails, which sounds undesirable

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The previous version did isolate errors (the stored chain was always current.catch(() => {}), so a failure only rejected its own caller; the "keeps serializing after a failed query" test covers exactly this scenario).
That said the code did not express well this intent : the lock is now a resolve-only Promise<void> that just signals completion of the previous query, so an error structurally can't enter the chain.

gadcam and others added 2 commits August 13, 2026 13:18
A pg.PoolClient is a single connection and does not support concurrent
queries (deprecated in pg@8, an error in pg@9), but the query interpreter
loads sibling relations concurrently, so any query with 2+ relations
inside a transaction triggered the deprecation warning. Serialize
performIO in PgQueryable for single-connection clients, leaving the
pool path parallel.

Supersedes prisma#29468, fixes prisma#29407.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Camille Barneaud <1693643+gadcam@users.noreply.github.com>
Restructure the serialization lock so it can never carry a query's
error: the lock promise only signals completion, and a failed query
rejects its own caller only. Use timers.setImmediate() in tests and
derive the join-child type from the query plan instead of asserting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Camille Barneaud <1693643+gadcam@users.noreply.github.com>
@gadcam

gadcam commented Aug 21, 2026

Copy link
Copy Markdown
Author

@aqrln could you approve the CI run when you get a chance please?
All three review comments are addressed and tests pass locally, but workflows haven't run on the PR yet.

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