Skip to content

Distributed Transactions (MSDTC) code blocking on disposal of Transaction Scope #76010

@nathangreaves

Description

@nathangreaves

Description

When disposing of a TransactionScope object in .NET 7 code - where the transaction has been promoted to a distributed transaction and work has taken place in another process - the code blocks indefinitely.

Reproduction Steps

I encountered this when converting one of our .NET 4.8 ASP.NET web api applications to .NET 7, but I have written a simple console app that demonstrates this blocking behaviour - https://github.com/nathangreaves/NET7RC1-MSDTC-TestApp.
It contains 2 projects that have virtually the same code. One is targeting .NET 7 RC1, the other is .NET Framework 4.8.

The code starts a new TransactionScope, inserts a value into a database table, and writes the transmitter propagation token to a file on disk. When running the application a second time as a new instance, it will read this file, create a new TransactionScope (using the Transaction from the transmitter propogation token), and insert a value into a database table. It deletes the file following completion of the transaction. The first instance will then complete the outer transaction, and then repeat the whole process a second time.

The code in .NET Framework 4.8 works correctly, committing both transactions and continuing execution.
However the .NET 7 code blocks on disposing the outer transaction. Also the Inner Transaction throws the following exception which may be relevant:

System.Threading.SynchronizationLockException: Object synchronization method was called from an unsynchronized block of code.
   at System.Threading.Monitor.Exit(Object obj)
   at System.Transactions.DependentTransaction.Complete()
   at System.Transactions.TransactionScope.InternalDispose()
   at System.Transactions.TransactionScope.Dispose()

However, if you run the applications in the following order:

  • Outer transaction using .NET 7 project
  • Inner transaction using .NET Framework 4.8 project
    The inner transaction completes successfully with the above exception, and the outer transaction (.NET 7) still blocks.

I attached the debugger to the .NET 7 project whilst it was blocked:
Debugger - Waiting on lock
This screenshot shows the code has blocked waiting for a lock in transactionScope.Dispose();

The following screenshots show the output of the console applications for all of the scenarios in terms of which version of .NET is running for the 2 instances of the console application:

Outer Transaction .NET 7, Inner Transaction .NET 7 - outer transaction blocks on dispose, inner transaction throws SynchronizationLockException exception:
Net7 Outer - Net7 Inner

Outer Transaction .NET 7, Inner Transaction .NET 4.8 - outer transaction blocks on dispose, inner transaction successful:
Net7 Outer - Net48 Inner

Outer Transaction .NET 4.8, Inner Transaction .NET 7 - outer transactions complete, inner transactions throw SynchronizationLockException on dispose (but still complete):
Net48 Outer - Net7 Inner

Outer Transaction .NET 4.8, Inner Transaction .NET 4.8 - outer and inner transactions complete and execution continues
Net48 Outer - Net48 Inner

Expected behavior

Execution does not block on transactionScope.Dispose() when completing a distributed transaction.
Execution does not throw exception System.Threading.SynchronizationLockException when completing a distributed transaction.

Actual behavior

When disposing of a TransactionScope object in .NET 7 code - where the transaction has been promoted to a distributed transaction and work has taken place in another process - the code blocks indefinitely.

Regression?

This code works correctly in .NET Framework 4.8

Known Workarounds

No response

Configuration

.NET 7 RC1
Windows Server 2019 Standard - 1809 (17763.2565)
x64

Other information

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions