feat: add support for nested transaction rollbacks via savepoints in sql#21678
feat: add support for nested transaction rollbacks via savepoints in sql#21678jacek-prisma merged 28 commits intoprisma:mainfrom
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
packages/client/tests/functional/interactive-transactions/tests.ts
Outdated
Show resolved
Hide resolved
155d16c to
5d80814
Compare
|
@Jolg42 Is it possible to verify that prisma/prisma-engines#4375 fixes the issue and gets the test to pass? |
|
Update: That surfaced that the PR in prisma-engines is not up do date with Update 2: |
|
Ummm, seems it is not working @LucianBuzzo. Or I messed up royally. Asking some colleagues for advice. |
|
@janpio I'll look into it on my end - any idea how to run just the |
|
@janpio I'm replicating the issue on my end - I think I might need to update the client code to maintain the same transaction ID in nested transactions 🤔 . I'm going to dig through and see what I can come up wiht. |
69291d4 to
5ffdd3d
Compare
|
Can be updated to use version from #22161 now |
Summary: - Added per-transaction operation serialization in TransactionManager via operationQueue and lock helpers, so nested start/commit/rollback and timeout/cancel paths cannot interleave state mutations. - Routed nested start (newTxId) through the transaction lock and revalidated state under lock before mutating depth/savepoint stacks. - Updated nested rollback to keep state consistent even when rollback-to-savepoint or release-savepoint queries fail. - Added a regression test for concurrent nested starts where the first savepoint creation fails, verifying the second nested start remains consistent and rollback targets the correct savepoint. - Removed leftover RequestHandler interactiveTransaction payload mutation that injected new_tx_id. - Updated AGENTS.md touched line to use official Wasm capitalization. Validation: - pnpm --filter @prisma/client-engine-runtime test transaction-manager.test.ts - pnpm --filter @prisma/client-engine-runtime build
Changes: - Added getItxScopeContext() helper to read TX_ID/TX_SCOPE_ID/TX_SCOPE_STATE with a typed context object, removing repeated local any-casts. - Updated _transactionWithCallback() to use typed itx context values for nested transaction scope handling. - Pulled scopeStack.push(scopeId) out of the nested/non-nested if-else without changing behavior. - Replaced transaction start info type from any to Transaction.InteractiveTransactionInfo<unknown>. - Renamed rollbackAttempts to rollbackScopeCount for clearer intent in rollback loop. - Replaced scopeStack.splice(0, scopeStack.length) with scopeStack.length = 0 for direct stack reset. - Switched Mongo nested-transaction runtime guard to use typed itx context (txId presence) instead of direct symbol access.
Changes: - Standardized provider checks in client-generator-ts to use context.isSqlProvider() for interactive transaction deny-list generation. - Refined getPrismaClient transaction scope typing into coherent top-level vs nested states to avoid per-field optional handling. - Added helpers for scope context validation and nested scope id generation to simplify _transactionWithCallback flow. - Simplified nested-vs-top-level close order checks for readability while preserving existing runtime behavior and error messages. - Added debug logging for suppressed rollback failures in multi-rollback cleanup loop. - Updated Mongo nested transaction runtime check to use typed scope context kind.
Added rationale comments above #withActiveTransactionLock and #runSerialized explaining that transaction operations are serialized per tx id to avoid savepoint/depth/status races across await boundaries.
- Add optional Transaction.savepoint(action, name) API in @prisma/driver-adapter-utils and re-export SavepointAction. - Update binder to forward wrapped savepoint methods for error-capturing adapters. - Refactor TransactionManager nested start/commit/rollback to prefer adapter-provided savepoint SQL, with provider-based fallback retained behind a TODO. - Implement savepoint methods for pg, neon, ppg, planetscale, mariadb, libsql, better-sqlite3, and mssql transaction implementations. - Extend transaction-manager tests to verify adapter-provided savepoint SQL and optional release behavior. - Validation: transaction-manager test suite passes; touched packages type-check with tsconfig.build.json using --emitDeclarationOnly false --noEmit.
packages/client-engine-runtime/src/transaction-manager/transaction-manager.ts
Outdated
Show resolved
Hide resolved
packages/client-engine-runtime/src/transaction-manager/transaction-manager.ts
Outdated
Show resolved
Hide resolved
packages/client-engine-runtime/src/transaction-manager/transaction-manager.ts
Outdated
Show resolved
Hide resolved
- Replace Transaction savepoint(action, name) with async createSavepoint/rollbackToSavepoint/releaseSavepoint in driver-adapter-utils and binder. - Refactor TransactionManager nested transaction flow to call adapter savepoint methods and remove provider fallback SQL synthesis. - Migrate adapter savepoint implementations (pg, neon, ppg, planetscale, mariadb, libsql, better-sqlite3, mssql, d1-http, d1-worker) to execute async savepoint operations. - Update transaction-manager tests for method-based savepoint behavior and missing-method error cases. - Simplify nested ITX scope symbol storage in getPrismaClient to TX_SCOPE_CONTEXT. - Document the new Transaction savepoint API shape in AGENTS.md.
|
@jacek-prisma let's go for the hatrick? 😅 🤞 |
|
Congratulations @LucianBuzzo! |
|
Haha thanks @janpio we got there in the end! 🎉 |
This adds a high level test that should pass if/when prisma/prisma-engines#4375 is merged.Transactions are now handled inside this repository, so the update to prisma-engines is no longer required.
This change adds support for handling rollbacks in nested transactions
in SQL databases. Specifically, the inner transaction should be rolled
back if the outer transaction fails.
To do this we keep track of the transaction ID and transaction depth so we can
re-use an existing open transaction in the underlying engine. This change also
allows the use of the
$transactionmethod on an interactive transaction client.Summary by CodeRabbit
New Features
Tests
Documentation