Skip to content

5.1.2 + System.Transactions with volatile resource manager gives "The operation is not valid for the current state of the enlistment" exceptions under concurrent load #2262

Closed
@coleman-c

Description

Describe the bug

After upgrading to EFCore 8 from EFCore 6 we started seeing "System.InvalidOperationException: The operation is not valid for the current state of the enlistment." errors in our logfiles.

A bit of research has isolated the issue to the increased version of the SqlClient transitive dependency, specifically the update from 5.0.1 to 5.0.2. The issue is present on the latest 5.1.2 version also. Included below is a sample application that reproduces the issue.

In low volume testing the issue didn't appear, but under moderate load appeared reliably.

The full exception we are seeing is below:

System.InvalidOperationException: The operation is not valid for the current state of the enlistment.
   at System.Transactions.EnlistmentState.Committed(InternalEnlistment enlistment)
   at System.Transactions.SinglePhaseEnlistment.Committed()
   at Microsoft.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment enlistment)
   at System.Transactions.DurableEnlistmentCommitting.EnterState(InternalEnlistment enlistment)
   at System.Transactions.PreparingEnlistment.Prepared()
   at MyEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment) in E:\Src\_scratch\ConsoleApp3\Program.cs:line 36
   at System.Transactions.VolatileEnlistmentPreparing.EnterState(InternalEnlistment enlistment)
   at System.Transactions.TransactionStateVolatilePhase1.EnterState(InternalTransaction tx)
   at System.Transactions.CommittableTransaction.Commit()
   at System.Transactions.TransactionScope.InternalDispose()
   at System.Transactions.TransactionScope.Dispose()
   at Program.<>c.<<<Main>$>b__0_0>d.MoveNext() in E:\Src\_scratch\ConsoleApp3\Program.cs:line 18
--- End of stack trace from previous location ---
   at Program.<Main>$(String[] args) in E:\Src\_scratch\ConsoleApp3\Program.cs:line 24
   at Program.<Main>(String[] args)

To reproduce

A complete console app is shown below that reproduces the issue reliably. Swapping between the two referenced versions of Microsoft.Data.SqlClient should demonstrate it working or failing in 5.0.1 and 5.0.2 repectively.

using System.Transactions;
using Microsoft.Data.SqlClient;

List<Task> tasks = [];
for (int i = 0; i < 1000; ++i)
{
    var task = Task.Run(async () =>
    {
        using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            SqlConnection conn = new("...");
            await conn.OpenAsync();
            conn.Close();

            Transaction.Current.EnlistVolatile(new MyEnlistmentNotification(), EnlistmentOptions.None);

            tx.Complete();
        }
    });

    tasks.Add(task);
}
await Task.WhenAll(tasks);

internal class MyEnlistmentNotification : IEnlistmentNotification
{
    public void Commit(Enlistment enlistment)
    {
        Thread.Sleep(500);
        enlistment.Done();
    }

    public void InDoubt(Enlistment enlistment) => enlistment.Done();
    public void Prepare(PreparingEnlistment preparingEnlistment) => preparingEnlistment.Prepared();
    public void Rollback(Enlistment enlistment) => enlistment.Done();
}
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <!--Broke-->
    <PackageReference Include="Microsoft.Data.SqlClient" Version="5.0.2" />
    <!--Works-->
    <!--<PackageReference Include="Microsoft.Data.SqlClient" Version="5.0.1" />-->
  </ItemGroup>
</Project>

Expected behavior

The same behaviour in 5.0.2 and greater as in 5.0.1.

Further technical details

Microsoft.Data.SqlClient version: 5.0.2 / 5.1.2
.NET target: net8
SQL Server version: SQL Server 2016/2022
Operating system: net8 debian docker image & Windows 11

Additional context
Currently this prevents us migrating to any EFCore versions > 7.0.5 since they all require a SqlClient dependency > 5.0.1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions