using System; using System.Data; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using MySql.Data.MySqlClient; using Uber.Core; namespace Uber.Storage { class DatabaseManager { public DatabaseServer Server; public Database Database; public DatabaseClient[] Clients; public Boolean[] AvailableClients; public int ClientStarvation; public Thread ClientMonitor; public string ConnectionString { get { MySqlConnectionStringBuilder ConnString = new MySqlConnectionStringBuilder(); ConnString.Server = Server.Hostname; ConnString.Port = Server.Port; ConnString.UserID = Server.Username; ConnString.Password = Server.Password; ConnString.Database = Database.DatabaseName; ConnString.MinimumPoolSize = Database.PoolMinSize; ConnString.MaximumPoolSize = Database.PoolMaxSize; return ConnString.ToString(); } } public DatabaseManager(DatabaseServer _Server, Database _Database) { Server = _Server; Database = _Database; Clients = new DatabaseClient[0]; AvailableClients = new Boolean[0]; ClientStarvation = 0; StartClientMonitor(); } public void DestroyClients() { lock (this.Clients) { for (int i = 0; i < Clients.Length; i++) { Clients[i].Destroy(); Clients[i] = null; } } } public void DestroyDatabaseManager() { StopClientMonitor(); lock (this.Clients) { for (int i = 0; i < Clients.Length; i++) { try { Clients[i].Destroy(); Clients[i] = null; } catch (NullReferenceException) { } } } Server = null; Database = null; Clients = null; AvailableClients = null; } public void StartClientMonitor() { if (ClientMonitor != null) { return; } ClientMonitor = new Thread(MonitorClients); ClientMonitor.Name = "DB Monitor"; ClientMonitor.Priority = ThreadPriority.Lowest; ClientMonitor.Start(); } public void StopClientMonitor() { if (ClientMonitor == null) { return; } try { ClientMonitor.Abort(); } catch (ThreadAbortException) { } ClientMonitor = null; } public void MonitorClients() { while (true) { try { lock (this.Clients) { DateTime DT = DateTime.Now; for (int i = 0; i < Clients.Length; i++) { if (Clients[i].State != ConnectionState.Closed) { if (Clients[i].InactiveTime >= 45) // Not used in the last %x% seconds { Clients[i].Disconnect(); } } } } Thread.Sleep(10000); // 10 Seconds } catch (ThreadAbortException) { } catch (Exception e) { UberEnvironment.GetLogging().WriteLine("An error occured in database manager client monitor: " + e.Message); } } } public DatabaseClient GetClient() { lock (this.Clients) { lock (this.AvailableClients) { for (uint i = 0; i < Clients.Length; i++) { if (AvailableClients[i] == true) { ClientStarvation = 0; if (Clients[i].State == ConnectionState.Closed) { try { Clients[i].Connect(); } catch (Exception e) { UberEnvironment.GetLogging().WriteLine("Could not get database client: " + e.Message, LogLevel.Error); } } if (Clients[i].State == ConnectionState.Open) { AvailableClients[i] = false; Clients[i].UpdateLastActivity(); return Clients[i]; } } } } ClientStarvation++; if (ClientStarvation >= ((Clients.Length + 1) / 2)) { ClientStarvation = 0; SetClientAmount((uint)(Clients.Length + 1 * 1.3f)); return GetClient(); } DatabaseClient Anonymous = new DatabaseClient(0, this); Anonymous.Connect(); return Anonymous; } } public void SetClientAmount(uint Amount) { lock (this.Clients) { lock (this.AvailableClients) { if (Clients.Length == Amount) { return; } if (Amount < Clients.Length) { for (uint i = Amount; i < Clients.Length; i++) { Clients[i].Destroy(); Clients[i] = null; } } DatabaseClient[] _Clients = new DatabaseClient[Amount]; bool[] _AvailableClients = new bool[Amount]; for (uint i = 0; i < Amount; i++) { if (i < Clients.Length) { _Clients[i] = Clients[i]; _AvailableClients[i] = AvailableClients[i]; } else { _Clients[i] = new DatabaseClient((i + 1), this); _AvailableClients[i] = true; } } Clients = _Clients; AvailableClients = _AvailableClients; } } } public void ReleaseClient(uint Handle) { lock (this.Clients) { lock (this.AvailableClients) { if (Clients.Length >= (Handle - 1)) // Ensure client exists { AvailableClients[Handle - 1] = true; } } } } } class Database { public string DatabaseName; public uint PoolMinSize; public uint PoolMaxSize; public Database(string _DatabaseName, uint _PoolMinSize, uint _PoolMaxSize) { DatabaseName = _DatabaseName; PoolMinSize = _PoolMinSize; PoolMaxSize = _PoolMaxSize; } } class DatabaseServer { public string Hostname; public uint Port; public string Username; public string Password; public DatabaseServer(string _Hostname, uint _Port, string _Username, string _Password) { Hostname = _Hostname; Port = _Port; Username = _Username; Password = _Password; } } class DatabaseClient : IDisposable { public uint Handle; public DateTime LastActivity; public DatabaseManager Manager; public MySqlConnection Connection; public MySqlCommand Command; public Boolean IsAnonymous { get { return (Handle == 0); } } public int InactiveTime { get { return (int)(DateTime.Now - LastActivity).TotalSeconds; } } public ConnectionState State { get { return (Connection != null) ? Connection.State : ConnectionState.Broken; } } public DatabaseClient(uint _Handle, DatabaseManager _Manager) { if (_Manager == null) { throw new ArgumentNullException("[DBClient.Connect]: Invalid database handle"); } Handle = _Handle; Manager = _Manager; Connection = new MySqlConnection(Manager.ConnectionString); Command = Connection.CreateCommand(); UpdateLastActivity(); } public void Connect() { if (Connection == null) throw new DatabaseException("[DBClient.Connect]: Connection instance of database client " + Handle + " holds no value."); if (Connection.State != ConnectionState.Closed) throw new DatabaseException("[DBClient.Connect]: Connection instance of database client " + Handle + " requires to be closed before it can open again."); try { Connection.Open(); } catch (MySqlException e) { throw new DatabaseException("[DBClient.Connect]: Could not open MySQL Connection - " + e.Message); } } public void Disconnect() { try { Connection.Close(); } catch { } } public void Dispose() { if (this.IsAnonymous) { Destroy(); return; } Command.CommandText = null; Command.Parameters.Clear(); Manager.ReleaseClient(Handle); } public void Destroy() { Disconnect(); Connection.Dispose(); Connection = null; Command.Dispose(); Command = null; Manager = null; } public void UpdateLastActivity() { LastActivity = DateTime.Now; } public void AddParamWithValue(string sParam, object val) { Command.Parameters.AddWithValue(sParam, val); } public void ExecuteQuery(string sQuery) { try { Command.CommandText = sQuery; Command.ExecuteScalar(); Command.CommandText = null; } catch (Exception e) { UberEnvironment.GetLogging().WriteLine(e + "\n (" + sQuery + ")"); } } public bool findsResult(string sQuery) { bool Found = false; try { Command.CommandText = sQuery; MySqlDataReader dReader = Command.ExecuteReader(); Found = dReader.HasRows; dReader.Close(); } catch (Exception e) { UberEnvironment.GetLogging().WriteLine(e + "\n (" + sQuery + ")"); } return Found; } public DataSet ReadDataSet(string Query) { DataSet DataSet = new DataSet(); Command.CommandText = Query; using (MySqlDataAdapter Adapter = new MySqlDataAdapter(Command)) { Adapter.Fill(DataSet); } Command.CommandText = null; return DataSet; } public DataTable ReadDataTable(string Query) { DataTable DataTable = new DataTable(); Command.CommandText = Query; using (MySqlDataAdapter Adapter = new MySqlDataAdapter(Command)) { Adapter.Fill(DataTable); } Command.CommandText = null; return DataTable; } public DataRow ReadDataRow(string Query) { DataTable DataTable = ReadDataTable(Query); if (DataTable != null && DataTable.Rows.Count > 0) { return DataTable.Rows[0]; } return null; } public string ReadString(string Query) { Command.CommandText = Query; string result = Command.ExecuteScalar().ToString(); Command.CommandText = null; return result; } public Int32 ReadInt32(string Query) { Command.CommandText = Query; Int32 result = Int32.Parse(Command.ExecuteScalar().ToString()); Command.CommandText = null; return result; } } public class DatabaseException : Exception { public DatabaseException(string sMessage) : base(sMessage) { } } }