using System; using System.IO; using System.Net.Sockets; using MySqlConnector; namespace SyncEngine.MySql { public static class MySqlConnectionFailures { public static bool IsReconnectable(Exception ex) { if (ex == null) { return false; } var mysql = FindException(ex); if (mysql != null) { return IsReconnectableMySql(mysql); } if (ex is ObjectDisposedException) { return true; } if (ex is InvalidOperationException invalidOp && invalidOp.Message.IndexOf("connection", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (FindException(ex) != null || FindException(ex) != null) { return true; } return false; } /// Classifies a MySQL exception for reconnect retry (testable without a live server). public static bool IsReconnectableMySql(MySqlException mysql) { if (mysql == null) { return false; } return IsReconnectableMySql(mysql.ErrorCode, mysql.IsTransient); } /// Classifies MySQL error codes for reconnect retry. public static bool IsReconnectableMySql(MySqlErrorCode errorCode, bool isTransient) { if (errorCode == MySqlErrorCode.AccessDenied || errorCode == MySqlErrorCode.UnknownDatabase || errorCode == MySqlErrorCode.NoSuchTable || errorCode == MySqlErrorCode.TableAccessDenied) { return false; } return isTransient || IsConnectionLostErrorCode(errorCode); } private static bool IsConnectionLostErrorCode(MySqlErrorCode code) { // MySQL server/client disconnect codes (incl. ServerShutdown during restart) if (code == MySqlErrorCode.UnableToConnectToHost || code == MySqlErrorCode.ServerShutdown || code == MySqlErrorCode.ShutdownComplete || code == MySqlErrorCode.ConnectionCountError) { return true; } // 2006 = server has gone away, 2013 = lost connection during query var numeric = (int)code; return numeric == 0 || numeric == 2006 || numeric == 2013; } private static T FindException(Exception ex) where T : Exception { var current = ex; while (current != null) { var typed = current as T; if (typed != null) { return typed; } current = current.InnerException; } return null; } } }