1

My application crashes due to this exception:

Exception Info: System.NotSupportedException
   at System.Net.Security._SslStream.ProcessRead(Byte[], Int32, Int32, System.Net.AsyncProtocolRequest)
   at System.Net.Security.SslStream.Read(Byte[], Int32, Int32)
   at MySql.Data.MySqlClient.TimedStream.Read(Byte[], Int32, Int32)
   at MySql.Data.MySqlClient.MySqlStream.ReadFully(System.IO.Stream, Byte[], Int32, Int32)
   at MySql.Data.MySqlClient.MySqlStream.LoadPacket()
   at MySql.Data.MySqlClient.MySqlStream.ReadPacket()
   at MySql.Data.MySqlClient.NativeDriver.GetResult(Int32 ByRef, Int64 ByRef)
   at MySql.Data.MySqlClient.Driver.GetResult(Int32, Int32 ByRef, Int64 ByRef)
   at MySql.Data.MySqlClient.Driver.NextResult(Int32, Boolean)
   at MySql.Data.MySqlClient.MySqlDataReader.NextResult()
   at MySql.Data.MySqlClient.MySqlDataReader.Close()
   at MySql.Data.MySqlClient.MySqlConnection.Close()
   at MySql.Data.MySqlClient.MySqlConnection.Dispose(Boolean)
   at MySql.Data.MySqlClient.MySqlConnection.Finalize()

I've tried to upgrade to the latest framework, and downloaded the newest version of MySql.Data file.

This is how one of my query methods looks like. The other query methods follows the same structure.

public static long InsertReturnInsertedAutoIncID(string queryStr, string[,] Parameters)
{
    int attempts = 0;
    do
    {
        try
        {
            attempts++;
            long autoIncID;
            using (MySqlConnection dbObj = new MySqlConnection(connectionToDBstring))
            {
                dbObj.Open();
                using (MySqlCommand query = new MySqlCommand(queryStr, dbObj))
                {
                    // Add parameters to query (as we don't want to concatinate)
                    for (int i = 0; i < Parameters.GetLength(0); i++)
                    {
                        query.Parameters.AddWithValue(Parameters[i, 0], Parameters[i, 1]);

                    }
                    query.ExecuteNonQuery();
                    autoIncID = query.LastInsertedId;
                }
            }
            return autoIncID;
        }
        catch (Exception ex)
        {
            Logger.Log.Error(ex.ToString());
            if (attempts >= maxAttempts)
            {
                return -1;
            }
            System.Threading.Thread.Sleep(200);
        }
    } while (true);
}

I have many threads(tasks) running in my application which queries the DB. There's no locks used since the threads should not be on hold for too long. This issue happends very rarely (Like every 7 day or so). I'm expecting this exception to not happend at all, but if I could just ignore the exception that would also be fine, I would just retry the query then. The issue is that this exception causes my application to stop working (Since the application is getting a "Application has stopped working" window.)

Chillychillx
  • 23
  • 1
  • 5
  • 1
    Try putting the exception handler inside the "using". The using has its own handler and then the code is returning and never seeing your while loop. – jdweng Sep 06 '19 at 15:46
  • 1
    Hi. I tried to produce exception in both of the "Using" blocks. When an exception occured, the next executed line was the catch block. This is correct behaviour. Therfore I'm not sure how it would help to add exception handler inside the "using" block. – Chillychillx Sep 06 '19 at 16:14
  • Please post TEXT results of A) SHOW GLOBAL STATUS LIKE '%thread%'; and B) SHOW GLOBAL VARIABLES LIKE '%thread%'; and C) SHOW GLOBAL STATUS LIKE 'com_stmt%'; – Wilson Hauck Sep 07 '19 at 19:44

1 Answers1

0

The exception is ultimately coming from MySql.Data.MySqlClient.MySqlConnection.Finalize(). This means that you have MySqlConnection objects that aren't being cleaned up properly (via using, Dispose, or Close) and are consequently being cleaned up by finalization run by the .NET garbage collector.

Note that MySql.Data violates the .NET Finalization best practices by cleaning up managed resources in its finalizer. This is explicitly forbidden because "the order in which the garbage collector destroys managed objects during finalization is not defined". It's quite possible that the NotSupportedException is being thrown because the SslStream's resources have already been finalized by the time MySqlConnection attempts to close the connection.

Unfortunately, an unhandled exception in a finalizer aborts the application (which is another reason that best practices call for as little work as possible to be done in a finalizer).

My recommendation would be to audit your code and ensure that every MySqlConnection is cleaned up properly. The safest way to do this is by using a using block, as shown in your example code. But it seems likely that there are some other instances that aren't using this pattern.

Secondly, you could switch to MySqlConnector. This is an alternative OSS MySQL ADO.NET library that follows all .NET best practices (and contains no finalizers). You should still clean up your connections when using this library (otherwise you'll have a connection pool leak), but it won't throw unhandled exceptions in the background.

Bradley Grainger
  • 27,458
  • 4
  • 91
  • 108
  • Hi. Thanks for the reply. I'm currently testing a try{} catch{} inside every using{} statement that I have. It was recommended in this post: https://stackoverflow.com/questions/10193674/catching-exception-inside-using-statement . jdweng also commented that this could solve the issue. Since the issue is very hard to replicate and rarely occurs I cannot tell if this solves the issue until atleast 2 weeks of testing. Do you also believe this would solve my issue, or do you recommend to switch to MySqlConnector already? – Chillychillx Sep 07 '19 at 09:39
  • @Chillychillx Adding `try`/`catch` blocks themselves won't help this issue, because that does nothing to close each `MySqlConnection`. Because the exception call stack clearly shows that a `MySqlConnection` is being finalized, it _must_ be the case that somewhere a `MySqlConnection` object is being created and opened without being closed or disposed. That's what I would look for to try to fix this issue. – Bradley Grainger Sep 07 '19 at 13:47
  • @Chillychillx FWIW, there are tools that can monitor your application and look for objects that aren't getting disposed; they can then show you the full call stack of its allocation, which can often help narrow it down very quickly. .NET Memory Profiler is one such tool; I believe JetBrains dotTrace can also do this. If you're able to run this application (under production load) on a development machine and diagnose it with one of those tools, that might provide the quickest way to solve this problem. – Bradley Grainger Sep 07 '19 at 13:48