System.Transactions hangs when non-MSDTC PSPE promotion occurs during EnlistDuringPrepareRequired Prepare
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Description
A non-MSDTC promotion triggered from inside an EnlistDuringPrepareRequired phase-0 callback leaves the System.Transactions commit state machine in TransactionStateDelegatedNonMSDTC instead of restarting commit in the promoted transaction states.
### Reproduction Steps
The problem occurs when calling this
```
for (var index = 1; index <= 2; index++)
{
var connection = new SqlConnection(connectionString);
this._connections.Add(connection);
connection.Open();
connection.EnlistTransaction(this._transactionToUse);
}
```
inside a `IEnlistmentNotification.Prepare()` when the transaction is not yet promoted before that.
See reproduction in [EnlistDuringPrepared.zip](https://github.com/user-attachments/files/31177153/EnlistDuringPrepared.zip): run the application and wait 1 minute for the exception. (setting openDuringPrepare to false, will enlist the connection before complete is called, which works)
### Expected behavior
Disposing the transaction should not run into a timeout
### Actual behavior
After `IEnlistmentNotification.Prepare()` is called, the transaction hangs for 60 seconds and then throws
```
System.Transactions.TransactionAbortedException: The transaction has aborted.
---> System.TimeoutException: Transaction Timeout
--- End of inner exception stack trace ---
at System.Transactions.TransactionStateAborted.EndCommit(InternalTransaction tx)
at System.Transactions.CommittableTransaction.Commit()
at System.Transactions.TransactionScope.InternalDispose()
at System.Transactions.TransactionScope.Dispose()
```
### Regression?
_No response_
### Known Workarounds
Ensure that the transaction is already promoted before Complete() is called.
### Configuration
.NET 10 (can also be reproduced with .NET 472)
Windows 11 x64 (I currently don't have another machine available to test)
### Other information
I assume that the code used to work with a MsSql server and broke after switching to AzureSql (it appeared in an old unit test which was originally written for an MsSql server that hasn't run for several years).
Contributor guide
Research direction
Run the EnlistDuringPrepared.zip reproduction on .NET 10 and observe the timeout during TransactionScope disposal. Start with the System.Transactions commit path around IEnlistmentNotification.Prepare(), comparing promotion during the callback with the openDuringPrepare=false workaround. Done means a non-MSDTC promotion during phase-0 prepare completes without the 60-second Transaction Timeout.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, csharp, sql
- Domain
- backend, databases, distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100