Software versions
MySqlConnector version: 2.4.0
Server type (MySQL, MariaDB, Aurora, etc.) and version: MariaDB 10.4
.NET version: net8.0
(Optional) ORM NuGet packages and versions:
Describe the bug
When the tcp connection is broken and MySqlConnection.Open() is called to reconnect to the database, the failed ServerSession is not returned to the ConnectionPool, which eventually leads to a ConnectTimeout.
Exception
MySqlConnector.MySqlException (0x80004005): Connect Timeout expired. All pooled connections are in use.
at MySqlConnector.MySqlConnection.CreateSessionAsync(ConnectionPool pool, Int64 startingTimestamp, Activity activity, Nullable`1 ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlConnection.cs:line 1109
at MySqlConnector.MySqlConnection.OpenAsync(Nullable`1 ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlConnection.cs:line 567
at MySqlConnector.MySqlConnection.Open() in /_/src/MySqlConnector/MySqlConnection.cs:line 522
at Program.<>c__DisplayClass0_1.<<Main>$>b__1() in Program.cs:line 34
Code sample
using MySqlConnector;
const string connString = "server=localhost;port=3306;user=root;password=pass;Character Set=utf8mb4;";
const string sql = @"SELECT 1;";
using var barrier = new Barrier(52);
for (var i = 0; i < 51; i++)
{
new Thread(() =>
{
using (var conn = new MySqlConnection(connString))
{
conn.Open();
barrier.SignalAndWait(); //Signal main thread that connection is open
barrier.SignalAndWait(); //Wait for main thread
try
{
using var cmd = new MySqlCommand(sql, conn);
cmd.ExecuteScalar();
}
catch (Exception)
{
//Reconnect
if (conn.State == System.Data.ConnectionState.Closed)
{
try
{
conn.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
else
{
//The bug is only reproducible if Open() is called, which requires the Closed state.
Console.WriteLine($"Connection is in state {conn.State}, expected Closed");
}
}
barrier.SignalAndWait(); //Signal main thread and wait for other threads
}
}).Start();
}
barrier.SignalAndWait(); //Wait for other threads to open connections
Console.Write("Reconnect database, then press Enter to continue...");
Console.ReadLine();
barrier.SignalAndWait(); //Signal other threads
barrier.SignalAndWait(); //Wait for other threads to finish
Expected behavior
The sample program exits without an exception.
Additional context
Restarting the database via docker restart is not enough, because that leaves the MySqlConnection in the Open state. To reproduce the bug, I had to use docker network disconnect followed by docker network connect.
My assumption of what happens here:
ServerSession calls SetFailed due to the broken TCP connection, setting the state of the owning MySqlConnection to Closed without returning the failed ServerSession back to the ConnectionPool.
MySqlConnection.Open() does not check if m_session is already set and simply overwrites it, causing the ServerSession to "leak", permanently blocking a slot in the ConnectionPool.
Software versions
MySqlConnector version: 2.4.0
Server type (MySQL, MariaDB, Aurora, etc.) and version: MariaDB 10.4
.NET version: net8.0
(Optional) ORM NuGet packages and versions:
Describe the bug
When the tcp connection is broken and
MySqlConnection.Open()is called to reconnect to the database, the failedServerSessionis not returned to theConnectionPool, which eventually leads to a ConnectTimeout.Exception
Code sample
Expected behavior
The sample program exits without an exception.
Additional context
Restarting the database via
docker restartis not enough, because that leaves theMySqlConnectionin theOpenstate. To reproduce the bug, I had to usedocker network disconnectfollowed bydocker network connect.My assumption of what happens here:
ServerSessioncallsSetFaileddue to the broken TCP connection, setting the state of the owningMySqlConnectiontoClosedwithout returning the failedServerSessionback to theConnectionPool.MySqlConnection.Open()does not check ifm_sessionis already set and simply overwrites it, causing theServerSessionto "leak", permanently blocking a slot in theConnectionPool.