Bug #109476 TransactionScope.Dispose throws "Connection must be valid and open to rollback"
Submitted: 23 Dec 2022 21:15 Modified: 24 Dec 2022 12:17
Reporter: Bradley Grainger (OCA) Email Updates:
Status: Verified Impact on me:
None 
Category:Connector / NET Severity:S3 (Non-critical)
Version:8.0.31 OS:Windows (10)
Assigned to: CPU Architecture:x86

[23 Dec 2022 21:15] Bradley Grainger
Description:
The test case from bug #107110 does not appear to be completely fixed, but throws a different exception now:

System.InvalidOperationException : Connection must be valid and open to rollback transaction
Stack Trace:
   at MySql.Data.MySqlClient.MySqlTransaction.Rollback()
   at MySql.Data.MySqlClient.MySqlConnection.CloseFully()
   at MySql.Data.MySqlClient.MySqlTransactionScope.Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
   at MySql.Data.MySqlClient.MySqlPromotableTransaction.System.Transactions.IPromotableSinglePhaseNotification.Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
   at System.Transactions.DurableEnlistmentAborting.EnterState(InternalEnlistment enlistment)
   at System.Transactions.DurableEnlistmentActive.InternalAborted(InternalEnlistment enlistment)
   at System.Transactions.TransactionStateAborted.EnterState(InternalTransaction tx)
   at System.Transactions.TransactionStateActive.Rollback(InternalTransaction tx, Exception e)
   at System.Transactions.Transaction.Rollback()
   at System.Transactions.TransactionScope.InternalDispose()
   at System.Transactions.TransactionScope.Dispose()

This exception is new in 8.0.31. In 8.0.30, it threw:

System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace:
   at MySql.Data.MySqlClient.NativeDriver.ExecutePacket(MySqlPacket packetToExecute)
   at MySql.Data.MySqlClient.NativeDriver.SendQuery(MySqlPacket queryPacket, Int32 paramsPosition)
   at MySql.Data.MySqlClient.Driver.SendQuery(MySqlPacket p, Int32 paramsPosition)
   at MySql.Data.MySqlClient.Statement.ExecuteNext()
   at MySql.Data.MySqlClient.PreparableStatement.ExecuteNext()
   at MySql.Data.MySqlClient.PreparableStatement.Execute()
   at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
   at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader()
   at MySql.Data.MySqlClient.MySqlCommand.ExecuteNonQuery()
   at MySql.Data.MySqlClient.MySqlTransaction.Rollback()
   at MySql.Data.MySqlClient.MySqlTransactionScope.Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
   at MySql.Data.MySqlClient.MySqlPromotableTransaction.System.Transactions.IPromotableSinglePhaseNotification.Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
   at System.Transactions.DurableEnlistmentAborting.EnterState(InternalEnlistment enlistment)
   at System.Transactions.DurableEnlistmentActive.InternalAborted(InternalEnlistment enlistment)
   at System.Transactions.TransactionStateAborted.EnterState(InternalTransaction tx)
   at System.Transactions.TransactionStateActive.Rollback(InternalTransaction tx, Exception e)
   at System.Transactions.Transaction.Rollback()
   at System.Transactions.TransactionScope.InternalDispose()
   at System.Transactions.TransactionScope.Dispose()

There appears to be a test case that verifies that this method doesn't throw: 
https://github.com/mysql/mysql-connector-net/blob/8.0.31/MySQL.Data/tests/MySql.Data.Tests....

I'm not sure how that test is passing, because this sample code is failing consistently for me (with mysql:8.0.31 running in a Docker container).

How to repeat:
Run the following C# code:

public void Test()
{
	using var scope = new TransactionScope();
	using var connection = new MySqlConnection("...");
	connection.Open();

	using var command = connection.CreateCommand();
	command.CommandText = "SELECT * from INFORMATION_SCHEMA.TABLES LIMIT 1; SELECT SLEEP(5);";
	command.CommandTimeout = 1;
	command.ExecuteNonQuery();
}

Suggested fix:
It should be possible to roll back a TransactionScope that outlives a MySqlConnection. Disposing the MySqlConnection wouldn't actually close the connection, but store it with the active Transaction to be committed or rolled back later.
[24 Dec 2022 12:17] MySQL Verification Team
Hello Bradley,

Thank you for the report and test case.

regards,
Umesh