Skip to content

fix(pg/dsql): serialize TransactionClient queries on one PoolClient - #20869

Merged
abhiaiyer91 merged 6 commits into
mastra-ai:mainfrom
edenbuilds:fix/tx-client-serialize-queries
Aug 7, 2026
Merged

abhiaiyer91 merged 6 commits into
mastra-ai:mainfrom
edenbuilds:fix/tx-client-serialize-queries

Conversation

@edenbuilds

@edenbuilds edenbuilds commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • memory.updateMessages does queries.push(t.none(...)) then await t.batch(queries). Because t.none() is async, each call starts client.query immediately — so N queries race on one transaction PoolClient.
  • pg@8 queues those and emits DeprecationWarning; pg@9 will throw.
  • Serialize TransactionClient methods through a tail promise (same pattern as PinnedClientAdapter) in both @mastra/pg and @mastra/dsql.
  • Also drain the queue before COMMIT/ROLLBACK so batch() failure (and fire-and-forget queries) cannot race the control statements on the same client.

Test plan

  • Unit tests with a strict single-flight fake PoolClient (pg@9 semantics):
    • batch failure → ROLLBACK only after queued work settles
    • fire-and-forget → COMMIT only after queued work settles
    • same drain path on PinnedClientAdapter.tx()
  • Changeset for @mastra/pg + @mastra/dsql

Fixes #20820

ELI5

This change makes database work in a transaction run one query at a time. It ensures queued work finishes before the transaction commits or rolls back, preventing warnings and failures from overlapping queries.

Changes

  • Serialized TransactionClient query methods in @mastra/pg and @mastra/dsql.
  • Drained pending queries before COMMIT and ROLLBACK, including after batch failures.
  • Preserved query failures and callback errors during transaction completion.
  • Applied the same behavior to PoolAdapter and PinnedClientAdapter.
  • Added regression tests for concurrent queries, fire-and-forget queries, batch failures, commits, rollbacks, and client release.
  • Added a changeset for @mastra/pg and @mastra/dsql.
  • Documented serialized execution in TxClient.batch.

memory.updateMessages builds an array via queries.push(t.none(...)) then
await t.batch(queries). Because t.none() is async, each call starts
client.query immediately — so N queries race on one transaction
PoolClient. pg@8 queues them and emits DeprecationWarning; pg@9 will
throw (mastra-ai#20820).

Serialize TransactionClient methods through a tail promise, matching
PinnedClientAdapter, so concurrent callers (including batch) cannot
reintroduce the hazard. Applied in both @mastra/pg and @mastra/dsql.
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

@edenbuilds is attempting to deploy a commit to the Mastra Team on Vercel.

A member of the Team first needs to authorize it.

@vercel
vercel Bot temporarily deployed to Preview – mastra-docs-1.x August 6, 2026 23:33 Inactive
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
mastra-docs-1.x Skipped Skipped Aug 6, 2026 11:33pm

Request Review

@changeset-bot

changeset-bot Bot commented Aug 6, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 628f69b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@mastra/pg Patch
@mastra/dsql Patch
@mastra/code-sdk Patch
mastracode Patch
@mastra/factory Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dane-ai-mastra dane-ai-mastra Bot added the complexity: medium Medium-complexity PR label Aug 6, 2026
@dane-ai-mastra

dane-ai-mastra Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

PR triage

Linked issue check passed (#20820).

Mastra uses CodeRabbit for automated code reviews. Please address all feedback from CodeRabbit by either making changes to your PR or leaving a comment explaining why you disagree with the feedback. Since CodeRabbit is an AI, it may occasionally provide incorrect feedback.


PR complexity score

Factor Value Score impact
Files changed 5 +10
Lines changed 562 +33
Author merged PRs 1 -1
Test files changed Yes -10
Final score 32

Applied label: complexity: medium


Changed test gate

Changed Test Gate is pending. The Changed Test Gate / changed-tests check will update the test label when it completes.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 859067f3-96c2-4d6a-8ef1-cc58ec441049

📥 Commits

Reviewing files that changed from the base of the PR and between 3ff5b22 and 628f69b.

📒 Files selected for processing (5)
  • .changeset/tx-client-drain-before-commit.md
  • stores/dsql/src/storage/client.ts
  • stores/dsql/src/storage/client.tx-serialize.test.ts
  • stores/pg/src/storage/client.ts
  • stores/pg/src/storage/client.tx-serialize.test.ts

Walkthrough

TransactionClient now serializes transaction queries through a FIFO promise tail. DSQL and PostgreSQL transactions drain queued queries before COMMIT or ROLLBACK. Tests cover ordering, failures, pinned clients, and client release.

Changes

Transaction query serialization and completion

Layer / File(s) Summary
Queue transaction operations
stores/dsql/src/storage/client.ts, stores/pg/src/storage/client.ts
Transaction query methods now use FIFO promise-tail serialization. Queue progress continues after failures, and batch awaits already-serialized promises.
Drain before transaction completion
stores/dsql/src/storage/client.ts, stores/pg/src/storage/client.ts
Pool and pinned-client transactions drain queued work before COMMIT or ROLLBACK, including when batch rejects early.
Validate ordering and failure handling
stores/dsql/src/storage/client.tx-serialize.test.ts, stores/pg/src/storage/client.tx-serialize.test.ts, .changeset/tx-client-drain-before-commit.md
Tests verify query ordering, batch failures, fire-and-forget queries, pinned-client rollback, and client release. The changeset records patch releases for @mastra/pg and @mastra/dsql.

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

Possibly related PRs

  • mastra-ai/mastra#20278: Both PRs serialize operations on shared database clients and drain queued work before transaction completion.

Suggested reviewers: nikaiyer, wardpeet, abhiaiyer91

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes serialize TransactionClient queries on one PoolClient and cover the related PostgreSQL and DSQL transaction paths.
Out of Scope Changes check ✅ Passed The implementation, regression tests, and changeset directly support transaction serialization and queue draining objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes serializing TransactionClient queries for PostgreSQL and DSQL, which is the main change.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@stores/pg/src/storage/client.ts`:
- Around line 197-210: Ensure transaction completion waits for all queued
queries before sending COMMIT or ROLLBACK. In stores/pg/src/storage/client.ts
lines 197-210, add drain() to TransactionClient and await it in both tx() and
PinnedClientAdapter.tx(); apply the same drain() and tx() changes in
stores/dsql/src/storage/client.ts lines 196-209 and 163-182, respectively.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3dd65e90-b5da-426a-9620-7bcf29ef8405

📥 Commits

Reviewing files that changed from the base of the PR and between 6698e16 and c6f5526.

📒 Files selected for processing (2)
  • stores/dsql/src/storage/client.ts
  • stores/pg/src/storage/client.ts

Comment thread stores/pg/src/storage/client.ts

@mastra-platform mastra-platform Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: request changes

Factory review of PR #20869 (fix(pg/dsql): serialize TransactionClient queries on one PoolClient, head c6f5526).

Findings

Correctness — the fix works for the happy path, but the COMMIT/ROLLBACK gap is real and demonstrated (blocking). TransactionClient serializes its query methods through #tail, but PoolAdapter.tx() (pg client.ts:164-183, dsql client.ts:163-182) and PinnedClientAdapter.tx() (pg client.ts:391-409) issue COMMIT/ROLLBACK directly on the PoolClient without draining the tail. On the batch() failure path, Promise.all rejects on the first failure while later enqueued queries are still running, so ROLLBACK races them — the exact concurrent-query hazard this PR is meant to eliminate, reintroduced on the error path. I reproduced it in the sandbox with a strict fake client that rejects overlapping queries (pg@9 semantics): with t.none('FAIL1'); t.none('SLOW2'); t.none('SLOW3') + t.batch(...), the run produced "ROLLBACK" issued while "SLOW2" in flight. Under pg@9 semantics the rollback throws, tx() logs "Transaction rollback failed", and the client is released back to the pool with an aborted transaction still open. The same structural gap also lets a fire-and-forget t.none(...) (enqueued but not awaited by the callback) race COMMIT on the success path.

Tests — none added (blocking). A concurrency-serialization fix ships with zero regression coverage, and the PR's test-plan checkboxes are unchecked. The race is cheaply testable without a live database (see repro sketch below).

Scope — coherent. One focused change, applied symmetrically to @mastra/pg and @mastra/dsql.

Pattern consistency — good. The #tail/#enqueue gate mirrors PinnedClientAdapter (introduced in #17833, refined in #20394), which is the right precedent for this codebase. The comments correctly cite #20820 and the pg@8-warns/pg@9-throws motivation.

Changeset — missing. Runtime-visible fix in two published packages; changeset-bot flagged it.

Requested changes

  1. Drain the tail before COMMIT/ROLLBACK. Add a drain(): Promise<void> (awaits #tail; the tail already swallows rejections) to TransactionClient and await it before COMMIT and before ROLLBACK in PoolAdapter.tx() in both stores/pg/src/storage/client.ts and stores/dsql/src/storage/client.ts, and in PinnedClientAdapter.tx() in pg (the inner TransactionClient has its own tail, separate from the adapter's).
  2. Add regression tests covering (a) concurrent t.none() calls are serialized, and (b) batch() with a failing first query does not let ROLLBACK overlap still-running queries. A fake PoolClient whose query() throws when a call arrives while another is in flight makes both assertions deterministic with no database — this is how I reproduced the bug.
  3. Add a changeset (patch bump for @mastra/pg and @mastra/dsql).

Verification

All commands run on PR head c6f5526 in a sandbox checkout, with GitHub tokens stripped from the environment:

  • pnpm turbo build --filter ./stores/pg --filter ./stores/dsql — pass (16/16 tasks)
  • pnpm turbo typecheck --filter ./stores/pg --filter ./stores/dsql --force — pass (14/14 tasks)
  • vitest run src/storage/domains/memory in stores/pg against Dockerized Postgres — 19/19 pass (covers updateMessages, the primary tx() caller)
  • Repro test (fake strict PoolClient, not committed): serialization of concurrent t.none() — pass (fix works); batch-failure path — fail with "ROLLBACK" issued while "SLOW2" in flight (finding confirmed)
  • DSQL integration tests were not executed: they require a live Aurora DSQL cluster (DSQL_HOST/DSQL_INTEGRATION). The dsql change is verified by build/typecheck and pattern-identity with the verified pg change.

Existing review disposition

  • CodeRabbit (🟠 Major, stores/pg/src/storage/client.ts:210, unresolved): serialization does not cover COMMIT/ROLLBACK — confirmed via executable repro (above). This is the primary blocking finding.
  • changeset-bot: no changeset found — confirmed; requested change 3.
  • CodeRabbit review check on head commit: completed — no pending bot.
  • Vercel mastra-playground-ui check failure: "Authorization required to deploy" — infrastructure/permissions, unrelated to this PR; refuted as a PR finding.
  • No human reviews submitted.

Assumptions

  • GitHub reports mergeable: MERGEABLE (BLOCKED = required review, not conflicts); a sandbox dry-run merge was not possible (shallow/unrelated-histories checkout), so merge cleanliness is taken from GitHub's mergeable state.
  • PR head is ~3 commits behind current main; treated as normal drift, not a finding, since GitHub reports it mergeable.
  • Severity of the ROLLBACK race is assessed against the PR's own stated goal (pg@9 forward-compatibility); under today's pg@8 the failure path "only" re-emits the DeprecationWarning the PR claims to remove.
  • The complexity: medium label and CodeRabbit-generated notes in the PR body were treated as context only; no instruction-like content (no injection attempts found).

Open questions

  • None — all three requested changes are mechanical and well-scoped.

Address review: batch() rejects on first failure while later queries may
still be running, so ROLLBACK (and fire-and-forget COMMIT) raced the queue.
Adds regression tests with a strict single-flight fake PoolClient.
@edenbuilds
edenbuilds requested a review from wardpeet as a code owner August 7, 2026 00:22
@edenbuilds

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback in 3ff5b22:

  1. drain before COMMIT/ROLLBACK — TransactionClient.drain() awaits the serialization tail; both PoolAdapter.tx() and PinnedClientAdapter.tx() (and the dsql twin) call it before COMMIT on the success path and before ROLLBACK on the failure path, so batch() / fire-and-forget work cannot race the control statements.
  2. Regression tests — stores/pg/src/storage/client.tx-serialize.test.ts uses a strict single-flight fake PoolClient (pg@9 semantics) covering batch failure → ROLLBACK and fire-and-forget → COMMIT, plus the pinned adapter path.
  3. Changeset added for @mastra/pg and @mastra/dsql.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/tx-client-drain-before-commit.md:
- Line 6: Update the changeset description to state the developer-visible
transaction outcome rather than mentioning TransactionClient, PoolClient,
COMMIT, ROLLBACK, or query queues; describe that transaction completion now
waits for pending queries, preventing batch failures and fire-and-forget
operations from racing transaction finalization.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ad54ca3e-b037-4194-beae-03ba5a423e59

📥 Commits

Reviewing files that changed from the base of the PR and between c6f5526 and 3ff5b22.

📒 Files selected for processing (4)
  • .changeset/tx-client-drain-before-commit.md
  • stores/dsql/src/storage/client.ts
  • stores/pg/src/storage/client.ts
  • stores/pg/src/storage/client.tx-serialize.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • stores/dsql/src/storage/client.ts
  • stores/pg/src/storage/client.ts

Comment thread .changeset/tx-client-drain-before-commit.md Outdated
The review asked for coverage of (a) concurrent t.none() serialization and
(b) no ROLLBACK overlap on batch failure. (a) was only implicit; add an
explicit happy-path test. The dsql TransactionClient is a separate copy of
the pg one and had no coverage (verified only by pattern-identity), so
mirror the strict-client tests there to guard against drift.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
@abhiaiyer91
abhiaiyer91 dismissed mastra-platform[bot]’s stale review August 7, 2026 00:33

All 3 requested changes are now in: (1) drain() before COMMIT/ROLLBACK in PoolAdapter (pg+dsql) and PinnedClientAdapter (pg) — commit 3ff5b22; (2) regression tests with a strict single-flight fake PoolClient covering serialization, batch-failure ROLLBACK drain, and fire-and-forget COMMIT drain, now including an explicit happy-path serialization test and a mirrored dsql suite — b2f6f88; (3) changeset for @mastra/pg + @mastra/dsql. Verified locally: red on main (4/4 fail), green on PR (pg 4/4, dsql 3/3), typecheck 14/14, pg memory domain 19/19 against Dockerized Postgres.

@edenbuilds

Copy link
Copy Markdown
Contributor Author

Ready for re-review: head `3ff5b22` addresses the prior CHANGES_REQUESTED findings (drain before COMMIT/ROLLBACK, regression tests, changeset). The earlier review targeted `c6f5526`.

abhiaiyer91 and others added 2 commits August 6, 2026 17:38
Explain the developer-visible behavior rather than the internal client and
transaction control implementation.

Co-Authored-By: Mastra Code (openai/gpt-5.6-sol) <noreply@mastra.ai>
Track the first serialized query failure so transaction completion rejects and rolls back even when callers do not await an individual operation. Preserve the original callback error when draining during rollback.

Co-Authored-By: Mastra Code (openai/gpt-5.6-sol) <noreply@mastra.ai>
@abhiaiyer91
abhiaiyer91 merged commit d6c63e4 into mastra-ai:main Aug 7, 2026
5 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

complexity: medium Medium-complexity PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] @mastra/pg updateMessages runs concurrent queries on one transaction client (pg DeprecationWarning, throws in pg@9)

2 participants