Bug #33267 Pinging a possibly broken connection may do more harm than good?
Submitted: 16 Dec 2007 12:18 Modified: 29 Jan 2008 22:27
Reporter: Gianluca Colombo Email Updates:
Status: Duplicate Impact on me:
None 
Category:Connector / NET Severity:S3 (Non-critical)
Version:5.1.3 and 5.1.4 OS:Microsoft Windows
Assigned to: CPU Architecture:Any
Tags: connection;broken;ping

[16 Dec 2007 12:18] Gianluca Colombo
Description:
Hi,

In an attempt to add more robustness to an application, before using again a Connection object opened some time before, I hoped I could do the following:

If Not Connection.Ping() Then
   Connection.Close
   Connection.Open
EndIf
... then do some job ...

This will check whether the connection is still alive (e.g. not closed by server after 8 hours inactivity timeout or broken socket) before I use it.

This sequence will actually fail on Connection.Open with a NullReference exception, if the connection is actually broken.

Please note that if Ping is not called, the sequence will perform correctly (e.g. calling just Connection.Close+Open) and correct operation will be restored (but I expect performance to be worse).

How to repeat:
1. open a connection to a server, even on localhost
2. stop MySQL server service and restart it
3. try the 4 code lines above

Suggested fix:
Looking at the client source I tried to figure out why, and I did a quick-and-dirty fix, as I urgently needed it.

After ping has reported failure, Connection.Close will simply do nothing, as the connection state is already closed.
When re-opening the same connection instance, the null reference exception is given by the  following piece of code:

NativeDriver.cs:
 public override void Configure(MySqlConnection conn)
 {
 	base.Configure(conn);
        stream.MaxPacketSize = (ulong) maxPacketSize;
	stream.Encoding = Encoding;
 }
as 'stream' is null.

To improve the behaviour, I did the following:
1. I touched Connection.cs Ping function:

public bool Ping()
{
    bool result = driver.Ping();
    if (!result)
        //SetState(ConnectionState.Closed, true);
        Close(); //I call the public close method instead of just setting its state
    return result;
}

2. This won't work unless a second fix is performed in MySqlPool.cs

I added the try/catch block at the bottom, around poolGate.Release();

public void ReleaseConnection(Driver driver)
{
	lock (lockObject)
        {
		if (inUsePool.Contains(driver))
			inUsePool.Remove(driver);

				if (driver.IsTooOld())
				{
					driver.Close();
					Debug.Assert(!idlePool.Contains(driver));
				}
				else
					idlePool.Enqueue(driver);

                // we now either have a connection available or have room to make
                // one so we release one slot in our semaphore

		//added try-catch
                try
                {
                    poolGate.Release();
                }
                catch
                {} //might need something else here??
            }
        }
}
[29 Jan 2008 22:27] Reggie Burnett
This turns out to be a duplicate of 33909 which has now been fixed in 5.1.5 and 5.2+