using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Transactions; using MySqlConnector; using ServerCore; using ServerBase; namespace ServerBase; public partial class MySqlDbConnector : IDisposable { // MySql 접속 재시도 간격 시간 밀리초) private Int32 MYSQL_CONNECT_RETRY_INTERVAL_MSEC = 3000; // MySql 접속 재시도 간격 활성화 조건 실패 횟수 private Int32 MYSQL_CONNECT_RETRY_INTERVAL_ENABLE_CONDITION_FAILED_COUNT = 3; // private Int16 m_curr_retry_connect_count = 0; private string m_connection_string = string.Empty; private MySqlConnection? m_connection; private ConnectionState m_last_connection_state = ConnectionState.Closed; private delegate int MyDelegate(int a, int b); public MySqlDbConnector() { } public async Task initMySql(string connectionString) { var err_msg = string.Empty; var error_code = createConnection(connectionString); if (error_code.isFail()) { return error_code; } error_code = await openMySql(); if (error_code.isFail()) { Log.getLogger().error($"Failed to initMySql() !!! : errorCode:{error_code} - connectString:{m_connection_string}"); return error_code; } return ServerErrorCode.Success; } public ServerErrorCode createConnection(string connectionString) { try { if(null != m_connection) { Dispose(); } m_connection_string = connectionString; m_connection = new MySqlConnection(connectionString); // 연결 상태 변경에 대한 이벤트를 받기 위해 등록 한다. - kangms m_connection.StateChange += new StateChangeEventHandler(onStateChange); } catch (Exception e) { Log.getLogger().error($"Exception !!!, new MySqlConnection() !!! : message:{e}, connectString:{m_connection_string}"); return ServerErrorCode.MySqlConnectionCreateFailed; } return ServerErrorCode.Success; } public async Task openMySql() { if (m_connection == null) { return ServerErrorCode.MySqlConnectionCreateFailed; } try { await m_connection.OpenAsync(); } catch (Exception e) { Log.getLogger().error($"Exception to open MySqlConnection !!! : errDesc:{e.Message} - connectString:{m_connection_string}"); return ServerErrorCode.MySqlConnectionOpenFailed; } return ServerErrorCode.Success; } public void Dispose() { // 객체 소멸시점 MySqlConnection 이 ConnectionPool 에 반환될 수 있도록 // MySqlConnection.Dispose() 를 반드시 호출해 주어야 한다. - kangms if (m_connection != null) { m_connection.Dispose(); m_connection = null; } } public void disposeMySql() { Dispose(); } //========================================================================================= // MySqlDbConnector.onStateChange() 함수가 호출되는 시점은 // 1. Self Disconnect : MySqlConnection.Dispose() 이 호출되는 시점 (Using 으로 생성후 Using 범위를 벗어나는 순간, 즉 GC 호출 시점) // 2. Other Disconnect : 실제로 MySql 서버와 끊어진 경우 // 3. 기타 : 비정상적인 상황 //========================================================================================= private void onStateChange(object sender, StateChangeEventArgs e) { if (m_connection == null) { Log.getLogger().error($"Failed to onStateChange(), Connection is null or CurrentState is null !!! - connectString:{m_connection_string}"); return; } switch (e.CurrentState) { case ConnectionState.Closed: Log.getLogger().debug($"Closed MySqlConnector !!! - connectString:{m_connection_string}"); break; case ConnectionState.Open: Log.getLogger().debug($"Opened MySqlConnector !!! - connectString:{m_connection_string}"); break; } m_last_connection_state = e.CurrentState; } public async Task retryOpenMySql() { Log.getLogger().info($"Retry db connect start !!! - connectString:{m_connection_string}"); var error_code = ServerErrorCode.Success; for (var i = 0; i < MYSQL_CONNECT_RETRY_INTERVAL_ENABLE_CONDITION_FAILED_COUNT; i++) { await Task.Delay(MYSQL_CONNECT_RETRY_INTERVAL_MSEC); error_code = createConnection(m_connection_string); if (error_code.isFail()) { Log.getLogger().error($"Failed to createConnection() when retryOpenMySql() !!! : errorCode:{error_code}, retryCount:{i} - connectString:{m_connection_string}"); continue; } error_code = await openMySql(); if (error_code.isFail()) { Log.getLogger().error($"Failed to openMySql() when retryOpenMySql() !!! : errorCode:{error_code}, retryCount:{i} - connectString:{m_connection_string}"); } else { Log.getLogger().info($"Success Retry db connect !!! : retryCount:{i} - connectString:{m_connection_string}"); return ServerErrorCode.Success; } } return error_code; } public async Task querySQL(string queryString, Func dataReader) { var error_code = ServerErrorCode.Success; try { using (var cmd = new MySqlCommand(queryString, m_connection)) using (var reader = await cmd.ExecuteReaderAsync()) { if (true == await reader.ReadAsync()) { if (null != dataReader) { error_code = dataReader.Invoke(reader); if (error_code.isFail()) { Log.getLogger().error($"Failed to dataReader.Invoke() when querySQL() !!! : errorCode:{error_code}, queryString:{queryString} - connectString:{m_connection_string}"); return error_code; } } } } } catch(Exception e) { error_code = ServerErrorCode.MySqlDbQueryException; var err_msg = $"Exception !!!, Failed to query for Read when querySQL() !!! : errorCode:{error_code}, exception:{e}, queryString:{queryString} - connectString:{m_connection_string}"; Log.getLogger().fatal(err_msg); return error_code; } return error_code; } }