Description:
When setting a very low DefaultCommandTimeout and connection pooling is enabled (the default), the finalizer thread may be unable to properly dispose the connection. Note that I'm using the standard "using" statement pattern to create/dispose of the connection.
MySql.Data.MySqlClient.MySqlException was unhandled
HResult=-2147467259
Message=Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
Source=MySql.Data
ErrorCode=-2147467259
Number=0
StackTrace:
at MySql.Data.MySqlClient.ExceptionInterceptor.Throw(Exception exception)
at MySql.Data.MySqlClient.MySqlConnection.Throw(Exception ex)
at MySql.Data.MySqlClient.MySqlConnection.HandleTimeoutOrThreadAbort(Exception ex)
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.MySqlConnection.CloseFully()
at MySql.Data.MySqlClient.MySqlConnection.Close()
at MySql.Data.MySqlClient.MySqlConnection.Dispose(Boolean disposing)
at System.ComponentModel.Component.Finalize()
InnerException: System.TimeoutException
HResult=-2146233083
Message=Timeout in IO operation
Source=MySql.Data
StackTrace:
at MySql.Data.MySqlClient.TimedStream.StopTimer()
at MySql.Data.MySqlClient.TimedStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at MySql.Data.MySqlClient.CompressedStream.CompressAndSendCache()
at MySql.Data.MySqlClient.CompressedStream.Flush()
at MySql.Data.MySqlClient.MySqlStream.SendPacket(MySqlPacket packet)
at MySql.Data.MySqlClient.NativeDriver.ExecutePacket(MySqlPacket packetToExecute)
at MySql.Data.MySqlClient.NativeDriver.SendQuery(MySqlPacket queryPacket)
at MySql.Data.MySqlClient.Driver.SendQuery(MySqlPacket p)
at MySql.Data.MySqlClient.Statement.ExecuteNext()
at MySql.Data.MySqlClient.PreparableStatement.ExecuteNext()
at MySql.Data.MySqlClient.Statement.Execute()
at MySql.Data.MySqlClient.PreparableStatement.Execute()
at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
InnerException:
How to repeat:
In my scenario, I'm running a connection within a "using" statement to ensure that dispose is properly called. When the finalizer decides it's time to dispose an unused connection, it called Dispose() on the connection. But because of the short command timeout, the dispose fails.
Suggested fix:
Normally, I would say just suppress the exception. But in this scenario, we can't trust the state of the connection anymore because a rollback cannot be performed. In this case, the *safest* thing to do is to truly close the connection with the database instead of releasing it back to the pool.
The work-around is to either increase the default command timeout, or to disable connection pooling.