Improved sql connection pool resiliency

This commit is contained in:
2026-01-07 01:52:12 +01:00
parent e83ce61877
commit e49a7c83ba
3 changed files with 38 additions and 8 deletions

View File

@@ -1,3 +1,4 @@
using System.Data;
using System.Data.Common; using System.Data.Common;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
@@ -6,6 +7,7 @@ namespace Server.Helper;
public class SQLHelper:IDisposable public class SQLHelper:IDisposable
{ {
public MySqlConnection connection; public MySqlConnection connection;
public DbDataReader? dbDataReader;
public string connectionString; public string connectionString;
public SQLHelper(MySqlConnection connection, string connectionString) public SQLHelper(MySqlConnection connection, string connectionString)
{ {
@@ -30,13 +32,15 @@ public class SQLHelper:IDisposable
lock (connection) lock (connection)
{ {
EnsureConnected(); EnsureConnected();
EnsureDbReaderIsClosed();
using MySqlCommand command = connection.CreateCommand(); using MySqlCommand command = connection.CreateCommand();
command.CommandText = query; command.CommandText = query;
foreach (KeyValuePair<string, dynamic> parameter in parameters) foreach (KeyValuePair<string, dynamic> parameter in parameters)
{ {
command.Parameters.AddWithValue($"@{parameter.Key}", parameter.Value); command.Parameters.AddWithValue($"@{parameter.Key}", parameter.Value);
} }
return command.ExecuteReader(); dbDataReader = command.ExecuteReader();
return dbDataReader;
} }
} }
@@ -45,6 +49,7 @@ public class SQLHelper:IDisposable
lock (connection) lock (connection)
{ {
EnsureConnected(); EnsureConnected();
EnsureDbReaderIsClosed();
using MySqlCommand command = connection.CreateCommand(); using MySqlCommand command = connection.CreateCommand();
command.CommandText = query; command.CommandText = query;
@@ -61,6 +66,7 @@ public class SQLHelper:IDisposable
lock (connection) lock (connection)
{ {
EnsureConnected(); EnsureConnected();
EnsureDbReaderIsClosed();
using MySqlCommand command = connection.CreateCommand(); using MySqlCommand command = connection.CreateCommand();
command.CommandText = query; command.CommandText = query;
@@ -83,11 +89,29 @@ public class SQLHelper:IDisposable
connection.Close(); connection.Close();
connection.Open(); connection.Open();
} }
catch (Exception) catch (Exception ex)
{ {
throw; // TODO add logging here ElmahCore.ElmahExtensions.RaiseError(ex);
throw;
} }
} }
return true; return true;
} }
public void EnsureDbReaderIsClosed()
{
int counter = 0;
int sleepTime = 10;
int timeout = 5000;
while (!(dbDataReader?.IsClosed ?? true))
{
if (counter > timeout / sleepTime)
{
TimeoutException ex = new("Unable to ensure dbDataReader is closed");
ElmahCore.ElmahExtensions.RaiseError(ex);
throw ex;
}
Thread.Sleep(sleepTime);
}
}
} }

View File

@@ -88,7 +88,7 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
public Entity? EntityFromJSON(SearchdomainManager searchdomainManager, ILogger logger, JSONEntity jsonEntity) //string json) public Entity? EntityFromJSON(SearchdomainManager searchdomainManager, ILogger logger, JSONEntity jsonEntity) //string json)
{ {
SQLHelper helper = searchdomainManager.helper.DuplicateConnection(); using SQLHelper helper = searchdomainManager.helper.DuplicateConnection();
Searchdomain searchdomain = searchdomainManager.GetSearchdomain(jsonEntity.Searchdomain); Searchdomain searchdomain = searchdomainManager.GetSearchdomain(jsonEntity.Searchdomain);
List<Entity> entityCache = searchdomain.entityCache; List<Entity> entityCache = searchdomain.entityCache;
AIProvider aIProvider = searchdomain.aIProvider; AIProvider aIProvider = searchdomain.aIProvider;

View File

@@ -82,13 +82,19 @@ public class SearchdomainManager
{ {
DbDataReader reader = helper.ExecuteSQLCommand("SELECT name FROM searchdomain", []); DbDataReader reader = helper.ExecuteSQLCommand("SELECT name FROM searchdomain", []);
List<string> results = []; List<string> results = [];
try
{
while (reader.Read()) while (reader.Read())
{ {
results.Add(reader.GetString(0)); results.Add(reader.GetString(0));
} }
reader.Close();
return results; return results;
} }
finally
{
reader.Close();
}
}
} }
public int CreateSearchdomain(string searchdomain, SearchdomainSettings settings) public int CreateSearchdomain(string searchdomain, SearchdomainSettings settings)