Skip to content

fix(adapter-mssql): prevent connection pool exhaustion and EREQINPROG errors by serializing commit/rollback#28825

Open
devanshuprakash wants to merge 5 commits intoprisma:mainfrom
devanshuprakash:fix/mssql-connection-pool-exhaustion
Open

fix(adapter-mssql): prevent connection pool exhaustion and EREQINPROG errors by serializing commit/rollback#28825
devanshuprakash wants to merge 5 commits intoprisma:mainfrom
devanshuprakash:fix/mssql-connection-pool-exhaustion

Conversation

@devanshuprakash
Copy link
Copy Markdown

@devanshuprakash devanshuprakash commented Dec 3, 2025

This PR fixes a critical issue in @prisma/adapter-mssql where EREQINPROG errors and connection pool exhaustion could occur under load.

Root Cause

The issue was caused by a race condition inside the MssqlTransaction class:

  • The commit and rollback methods did not acquire the mutex that serializes access to the underlying MSSQL connection.
  • As a result, commit or rollback could execute while a query was still in progress (e.g., a slow query).
  • The MSSQL driver then threw:

EREQINPROG: Can't rollback transaction. There is a request in progress.

  • In some cases, this left the connection in a bad state, eventually exhausting the connection pool.

Fix

This PR ensures:

  • commit acquires the mutex before executing.
  • rollback acquires the mutex before executing.

This guarantees that these methods wait for any active queries to finish, removing the race condition.

Changes

packages/adapter-mssql/src/mssql.ts

  • Updated MssqlTransaction.commit to acquire the mutex.
  • Updated MssqlTransaction.rollback to acquire the mutex.
  • Exported PrismaMssqlAdapter to enable unit testing.

packages/adapter-mssql/src/transaction.test.ts

  • Added a regression test that reproduces the issue by:
    • Simulating a slow query,
    • Triggering a rollback while the query is active.
  • Verified that the fix prevents the EREQINPROG error and allows the rollback to complete successfully after the query finishes.

Verification

Tested via:

pnpm test packages/adapter-mssql/src/transaction.test.ts

Output:

✓ src/transaction.test.ts (1 test) 103ms
✓ PrismaMssqlAdapter reproduction > should reproduce EREQINPROG when rollback is called during active query 103ms

Fixes #28812

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved MSSQL transaction handling with enhanced commit and rollback operations for better reliability.
  • Tests

    • Added transaction scenario testing to verify rollback behavior during concurrent operations.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 3, 2025

Walkthrough

Addresses a race condition in MSSQL adapter transaction handling by protecting commit and rollback operations with mutex locks to prevent concurrent access, and adds a test validating rollback waits for in-progress queries to complete.

Changes

Cohort / File(s) Change Summary
Transaction synchronization
packages/adapter-mssql/src/mssql.ts
Wrapped commit and rollback operations with mutex acquisition/release to prevent concurrent access; exported PrismaMssqlAdapter class for public use
Transaction tests
packages/adapter-mssql/src/transaction.test.ts
New test file validating rollback behavior during in-flight queries and EREQINPROG error handling; verifies rollback waits for in-progress queries to complete

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the primary fix: serializing commit/rollback operations to prevent EREQINPROG errors and connection pool exhaustion.
Linked Issues check ✅ Passed The PR addresses all key objectives from issue #28812: implements mutex serialization for commit/rollback, exports PrismaMssqlAdapter for testing, and adds regression test verifying proper handling of in-progress queries.
Out of Scope Changes check ✅ Passed All changes directly support the fix for issue #28812: mutex wrapping of commit/rollback, exporting adapter class for testing, and transaction rollback regression test. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 708f39f and 5c110e8.

📒 Files selected for processing (2)
  • packages/adapter-mssql/src/mssql.ts (2 hunks)
  • packages/adapter-mssql/src/transaction.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript for new code in the Prisma monorepo

Files:

  • packages/adapter-mssql/src/transaction.test.ts
  • packages/adapter-mssql/src/mssql.ts
**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.ts: Place test files alongside source files with .test.ts naming convention, which are excluded from build output via esbuild config
Inline snapshots should be kept concise unless the exact message matters

Files:

  • packages/adapter-mssql/src/transaction.test.ts
🧠 Learnings (4)
📚 Learning: 2025-12-03T09:46:36.091Z
Learnt from: CR
Repo: prisma/prisma PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T09:46:36.091Z
Learning: Applies to packages/migrate/src/__tests__/**/*.test.ts : Use `ctx.setDatasource()` test helper to override config.datasource for connection-specific test scenarios

Applied to files:

  • packages/adapter-mssql/src/transaction.test.ts
📚 Learning: 2025-12-03T09:46:36.091Z
Learnt from: CR
Repo: prisma/prisma PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T09:46:36.091Z
Learning: Applies to packages/client-generator-ts/src/TSClient/file-generators/PrismaNamespaceFile.ts : In PrismaClient constructor options, update generated types in `packages/client-generator-ts/src/TSClient/file-generators/PrismaNamespaceFile.ts` (`buildClientOptions` function)

Applied to files:

  • packages/adapter-mssql/src/mssql.ts
📚 Learning: 2025-12-03T09:46:36.091Z
Learnt from: CR
Repo: prisma/prisma PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T09:46:36.091Z
Learning: Applies to packages/client-generator-js/src/TSClient/PrismaClient.ts : In PrismaClient constructor options, update generated types in `packages/client-generator-js/src/TSClient/PrismaClient.ts` (`buildClientOptions` method)

Applied to files:

  • packages/adapter-mssql/src/mssql.ts
📚 Learning: 2025-12-03T09:46:36.091Z
Learnt from: CR
Repo: prisma/prisma PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T09:46:36.091Z
Learning: Applies to packages/client/src/runtime/core/engines/common/Engine.ts : In PrismaClient constructor options, update engine config in `EngineConfig` interface in `packages/client/src/runtime/core/engines/common/Engine.ts`

Applied to files:

  • packages/adapter-mssql/src/mssql.ts
🧬 Code graph analysis (1)
packages/adapter-mssql/src/transaction.test.ts (2)
packages/adapter-mssql/src/mssql.ts (1)
  • PrismaMssqlAdapter (128-177)
packages/client-engine-runtime/src/transaction-manager/transaction-manager.ts (1)
  • tx (239-295)
🔇 Additional comments (5)
packages/adapter-mssql/src/mssql.ts (3)

95-100: LGTM: Mutex correctly serializes commit with queries.

The mutex acquisition ensures commit() cannot run concurrently with an in-progress query, preventing the race condition. The finally block guarantees mutex release even if commit throws.


106-118: LGTM: Mutex correctly serializes rollback while preserving EABORT handling.

The mutex acquisition prevents rollback() from running during an active query, addressing the EREQINPROG error. The preserved EABORT handling correctly ignores errors when the transaction was already aborted, and the finally block ensures mutex release in all cases.


128-128: LGTM: Export enables testing.

Exporting PrismaMssqlAdapter expands the public API surface but is necessary for the regression test added in this PR.

packages/adapter-mssql/src/transaction.test.ts (2)

1-27: LGTM: Mock setup provides minimal viable implementation.

The mocked mssql module provides the necessary structure for the test. The empty object returned by transaction() is appropriately overridden in the test with a complete mock implementation.


29-96: LGTM: Test comprehensively validates the race condition fix.

The test accurately simulates the original issue:

  1. Starts a slow query that sets requestActive = true
  2. Attempts rollback while the query is active
  3. The mutex (from the fix) causes rollback to wait for the query to complete
  4. When rollback executes, requestActive = false, so EREQINPROG is not thrown

The test name, type assertions, and mock verifications properly address all previous review feedback.


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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d81b4b6 and 4926bf0.

📒 Files selected for processing (3)
  • packages/adapter-mssql/src/mssql.ts (2 hunks)
  • packages/adapter-mssql/src/transaction.test.ts (1 hunks)
  • packages/cli/src/Init.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/adapter-mssql/src/transaction.test.ts (2)
packages/adapter-mssql/src/mssql.ts (1)
  • PrismaMssqlAdapter (128-177)
packages/client-engine-runtime/src/transaction-manager/transaction-manager.ts (1)
  • tx (239-295)
packages/adapter-mssql/src/mssql.ts (3)
packages/client/tests/e2e/_utils/run.ts (1)
  • release (248-254)
packages/driver-adapter-utils/src/types.ts (1)
  • SqlDriverAdapter (248-268)
packages/driver-adapter-utils/src/index.ts (1)
  • SqlDriverAdapter (28-28)
🔇 Additional comments (3)
packages/cli/src/Init.ts (1)

116-139: Lazy-loading dev server modules only when needed looks correct

Guarding the dynamic import('@prisma/dev') / import('@prisma/dev/internal/state') behind url === undefined preserves existing behavior for all code paths while avoiding unnecessary module load when a URL is already provided. The defaultEnv callers always pass a defined URL except for the “local Prisma Postgres” default case, so the change is behaviorally equivalent and lower overhead.

packages/adapter-mssql/src/mssql.ts (2)

92-119: Serializing commit/rollback with the same mutex as performIO aligns with the intended fix

Using this.#mutex.acquire() in both commit and rollback, with release() in a finally block, correctly ensures that commits/rollbacks cannot run while a transactional request is in progress, and that queries cannot start while a close operation is running. This should prevent the EREQINPROG “request in progress” race while still preserving the existing EABORT handling in rollback.


128-210: Exporting PrismaMssqlAdapter is consistent with its factory and useful for tests

Making PrismaMssqlAdapter an exported class matches its use as the concrete return type of PrismaMssqlAdapterFactory.connect() and enables direct instantiation in tests and advanced consumers. Just be aware this effectively promotes it to part of the package’s public API surface going forward.

Copy link
Copy Markdown
Member

@aqrln aqrln left a comment

Choose a reason for hiding this comment

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

Thanks so much! The change in the logic itself looks good to me, but there are some issues in the test and unrelated changes that need to be moved out of this PR.

@aqrln aqrln added this to the 7.2.0 milestone Dec 3, 2025
@devanshuprakash
Copy link
Copy Markdown
Author

I've addressed your feedback:

  1. Renamed the test suite to PrismaMssqlAdapter transaction regression to better reflect its purpose.
  2. Removed the any usage in the test and replaced it with a proper type assertion (Error & { code: string }).
    Please review @aqrln

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
packages/adapter-mssql/src/transaction.test.ts (3)

29-29: Test suite name should be more specific.

The suite name "transaction regression" doesn't clearly indicate what race condition is being guarded against. Consider a more descriptive name like:

-describe('PrismaMssqlAdapter transaction regression', () => {
+describe('PrismaMssqlAdapter: rollback during active query (EREQINPROG prevention)', () => {

30-30: Test name contradicts the expected behavior.

The test name says "should reproduce EREQINPROG" but line 87 expects rollback to resolve without error. With the mutex fix in place, the test verifies that EREQINPROG does not occur. Consider renaming to reflect the guarded regression:

-  it('should reproduce EREQINPROG when rollback is called during active query', async () => {
+  it('does not throw EREQINPROG when rollback is called during an active query', async () => {

62-73: Avoid using as any type assertion.

The as any type assertion on line 73 bypasses type safety. Consider defining a proper type or interface for the mocked transaction object:

type MockTransaction = {
  on: () => void
  begin: () => Promise<void>
  commit: () => Promise<void>
  rollback: ReturnType<typeof vi.fn>
  request: () => {
    input: () => void
    query: ReturnType<typeof vi.fn>
    arrayRowMode: boolean
  }
}

pool.transaction = () =>
  ({
    on: () => {},
    begin: async () => {},
    commit: async () => {},
    rollback: rollbackMock,
    request: () => ({
      input: () => {},
      query: queryMock,
      arrayRowMode: false,
    }),
  }) as MockTransaction
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4926bf0 and 76d3a21.

📒 Files selected for processing (1)
  • packages/adapter-mssql/src/transaction.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript for new code in the Prisma monorepo

Files:

  • packages/adapter-mssql/src/transaction.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.ts: Place test files alongside source files with .test.ts naming convention, which are excluded from build output via esbuild config
Inline snapshots should be kept concise unless the exact message matters

Files:

  • packages/adapter-mssql/src/transaction.test.ts
🧠 Learnings (1)
📚 Learning: 2025-12-03T09:46:36.091Z
Learnt from: CR
Repo: prisma/prisma PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T09:46:36.091Z
Learning: Applies to packages/migrate/src/__tests__/**/*.test.ts : Use `ctx.setDatasource()` test helper to override config.datasource for connection-specific test scenarios

Applied to files:

  • packages/adapter-mssql/src/transaction.test.ts
🧬 Code graph analysis (1)
packages/adapter-mssql/src/transaction.test.ts (2)
packages/adapter-mssql/src/mssql.ts (1)
  • PrismaMssqlAdapter (128-177)
packages/client-engine-runtime/src/transaction-manager/transaction-manager.ts (1)
  • tx (239-295)
🔇 Additional comments (2)
packages/adapter-mssql/src/transaction.test.ts (2)

1-4: LGTM!

Imports are appropriate for the test setup. The newly exported PrismaMssqlAdapter class enables this unit test.


6-27: LGTM!

The mocking strategy appropriately isolates the adapter behavior for testing. The minimal implementations and override pattern are standard for unit tests.

@devanshuprakash
Copy link
Copy Markdown
Author

@aqrln
Done! I've updated the test names to be more descriptive, fixed the types, and added the requested assertions for mock calls and ordering.

@devanshuprakash devanshuprakash force-pushed the fix/mssql-connection-pool-exhaustion branch from 708f39f to 5c110e8 Compare December 3, 2025 18:45
@arthur-ver
Copy link
Copy Markdown

arthur-ver commented Dec 21, 2025

@aqrln What's the status of this fix? Currently experiencing lots of TransactionError: Can't rollback transaction. There is a request in progress. with the latest Prisma 7.2.0...

@trentonsnyder
Copy link
Copy Markdown

@devanshuprakash We patched our adapter with these changes about a week ago and haven't seen the Transaction Errorsince then. Thanks for putting this up 👍

using v16.16.3 "rust-free"

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.

Prisma with @prisma/adapter-mssql: Connection Pool Exhaustion and "EREQINPROG" Errors in Production

4 participants