Skip to content

MySqlConnection.DoCloseAsync(): KeyNotFoundException in s_transactionConnections #997

@Timovzl

Description

@Timovzl
  • Version: 1.3.9
  • Target: netcoreapp3.1

I am running into a case where MySqlConnection.DoCloseAsync throws on this line.

System.Collections.Generic.KeyNotFoundException: The given key 'System.Transactions.Transaction' was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at MySqlConnector.MySqlConnection.DoCloseAsync(Boolean changeState, IOBehavior ioBehavior) in /_/src/MySqlConnector/MySqlConnection.cs:line 983
   at MySqlConnector.MySqlConnection.CloseAsync(Boolean changeState, IOBehavior ioBehavior) in /_/src/MySqlConnector/MySqlConnection.cs:line 954
   at MySqlConnector.MySqlConnection.Dispose(Boolean disposing) in /_/src/MySqlConnector/MySqlConnection.cs:line 641
   at System.ComponentModel.Component.Dispose()

The MySqlConnection has an m_enlistedTransaction, but the corresponding Transaction does not exist in s_transactionConnections. 😕

I would offer a repro, but I can only reproduce this in Docker on Linux on an alpha environment. I have found no way of reproducing it locally, even when running from Docker, against that environment's database (and other infrastructure).

On the problematic environment, the behavior is quite consistent. The job is running every 5 minutes, ending in the same repository method, and it always throws this exception.

I have attempted to scrutinize MySqlConnection, looking for any way that an instance might obtain an m_enlistedTransaction whose Transaction is absent in s_transactionConnections. Perhaps it is of interest that DoCloseAsync calls DbReader.DisposeAsync, and the reader was created with CommandBehavior.CloseConnection. Could that end up removing the Transaction from s_transactionConnections somehow? Still, I believe that would also unset m_enlistedTransaction. (I'm not violating the lack of thread-safety, of course.)

The calling code is structured as follows:

// No outer TransactionScopes present

using var transactionScope = new TransactionScope(
	TransactionScopeOption.Required,
	new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromMinutes(10) },
	TransactionScopeAsyncFlowOption.Enabled);

{
	using var connection = new MySqlConnection(...);
	using var command = connection.CreateCommand();

	command.CommandText = ...;
	await connection.OpenAsync();

	// This line hits the exception, when the disposal of the reader causes disposal of the connection:
	await ReadData(command);
}

transactionScope.Complete();

// Local function that reads data from the command
async Task ReadData(DbCommand command)
{
	using var reader = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection);

	// Snip
}

Again, apologies for the lack of a repro. I hope that, with your expertise, we may be able to discern what might cause a missing key in s_transactionConnections.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions