Bug #102129 Hang in MySqlConnection.Close after TransactionScope times out

Submitted: 1 Jan 2021 23:00 Modified: 17 Jan 2022 19:21
Reporter: Bradley Grainger (OCA) Email Updates:
Status: Can't repeat Impact on me:
None 
Category:Connector / NET Severity:S2 (Serious)
Version:8.0.22 OS:Windows (10)
Assigned to: CPU Architecture:Other (x64)

[1 Jan 2021 23:00] Bradley Grainger
Description:
After a System.Transactions.Transaction (that a connection is enlisted in) times out, calling MySqlConnection.Close just hangs for DefaultCommandTimeout seconds. After the hang, the call to Close sometimes succeeds, but sometimes throws a NullReferenceException:

NullReferenceException: Object reference not set to an instance of an object. 
   at MySql.Data.MySqlClient.MySqlDataReader.Close()
   at MySql.Data.MySqlClient.MySqlConnection.Close()
   at UserQuery.Main(), line 24 

Meanwhile, the transaction throws an exception on a background thread:

MySqlException: Fatal error encountered during command execution.
   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.EnlistableStates.Timeout(InternalTransaction tx)
   at System.Transactions.Bucket.TimeoutTransactions()
   at System.Transactions.BucketSet.TimeoutTransactions()
   at System.Transactions.TransactionTable.ThreadTimer(Object state)
   at System.Threading.TimerQueueTimer.<>c.<.cctor>b__23_0(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.TimerQueueTimer.CallCallback(Boolean isThreadPool)
   at System.Threading.TimerQueueTimer.Fire(Boolean isThreadPool)
   at System.Threading.TimerQueue.FireNextTimers()
   at System.Threading.TimerQueue.AppDomainTimerCallback(Int32 id)

How to repeat:
Run the following C# code, which causes a System.Transactions transaction to time out before the SQL statement has finished executing:

var transactionScope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(1));
var connection = new MySqlConnection("...;AutoEnlist=true");
connection.Open();
Console.WriteLine("Executing command");
using (var command = new MySqlCommand("DO SLEEP(2);", connection))
	command.ExecuteNonQuery();
Console.WriteLine("Executed command");
connection.Close(); // delays for 30 seconds; then sometimes succeeds, sometimes throws NullReferenceException
connection.Dispose();

Console.WriteLine("Closed connection");
transactionScope.Complete();
[4 Jan 2021 6:30] MySQL Verification Team
Hello Bradley,

Thank you for the report and test case.
With the provided test case I'm getting below Unhandled Exception:

- C/NET 8.0.22, VS 2019, .NET framework 4.8

using MySql.Data.MySqlClient;
using System;
using System.Transactions;

namespace Bug102129
{
    class Program
    {
        static void Main(string[] args)
        {
            var transactionScope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(1));
            var connection = new MySqlConnection("server=xxx;port=3333;userid = ushastry; password = mysql123;AutoEnlist=true");

            connection.Open();

            Console.WriteLine("Executing command");
            using (var command = new MySqlCommand("DO SLEEP(2);", connection))
                command.ExecuteNonQuery();
            Console.WriteLine("Executed command");
            connection.Close(); // delays for 30 seconds; then sometimes succeeds, sometimes throws NullReferenceException
            connection.Dispose();

            Console.WriteLine("Closed connection");
            transactionScope.Complete();
        }
    }
}

Unhandled Exception: System.Transactions.TransactionAbortedException: The transaction has aborted.
   at MySql.Data.MySqlClient.Interceptors.ExceptionInterceptor.Throw(Exception exception)
   at MySql.Data.MySqlClient.MySqlConnection.Throw(Exception ex)
   at MySql.Data.MySqlClient.MySqlCommand.Throw(Exception ex)
   at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
   at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader()
   at MySql.Data.MySqlClient.Driver.LoadServerProperties(MySqlConnection connection)
   at MySql.Data.MySqlClient.Driver.Configure(MySqlConnection connection)
   at MySql.Data.MySqlClient.MySqlConnection.Open()
   at Bug102129.Program.Main(String[] args) in C:\Work\MySQLNet\Bug102129\Bug102129\Program.cs:line 14
Press any key to continue . . .

Am I missing anything here? Please let me know. Thank you!

regards,
Umesh
[13 Jan 2021 14:07] Bradley Grainger
I tested this again. I don't get the exception I reported when targeting "net48" (.NET Framework 4.8), but I do get it when targeting "net5.0" (.NET 5.0). So it appears this bug may be framework-specific.
[18 Jan 2021 8:24] MySQL Verification Team
Thank you for the feedback.
[17 Jan 2022 19:21] Daniel Valdez
Posted by developer:
 
Could not be reproduced using the latest release of Connector/NET, v8.0.27.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.042 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.