You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
8.1 KiB

4 years ago
  1. #if MYSQL_6_10
  2. // Copyright ?2014, 2018, Oracle and/or its affiliates. All rights reserved.
  3. //
  4. // MySQL Connector/NET is licensed under the terms of the GPLv2
  5. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  6. // MySQL Connectors. There are special exceptions to the terms and
  7. // conditions of the GPLv2 as it is applied to this software, see the
  8. // FLOSS License Exception
  9. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  10. //
  11. // This program is free software; you can redistribute it and/or modify
  12. // it under the terms of the GNU General Public License as published
  13. // by the Free Software Foundation; version 2 of the License.
  14. //
  15. // This program is distributed in the hope that it will be useful, but
  16. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  17. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  18. // for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License along
  21. // with this program; if not, write to the Free Software Foundation, Inc.,
  22. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  23. using System;
  24. using System.Collections.Generic;
  25. using System.ComponentModel;
  26. using System.Text;
  27. using Externals.MySql.Data.MySqlClient;
  28. namespace Externals.MySql.Data.MySqlClient.Replication
  29. {
  30. /// <summary>
  31. /// Manager for Replication and Load Balancing features
  32. /// </summary>
  33. internal static class ReplicationManager
  34. {
  35. private static List<ReplicationServerGroup> groups = new List<ReplicationServerGroup>();
  36. private static Object thisLock = new Object();
  37. //private static Dictionary<string, ReplicationServerSelector> selectors = new Dictionary<string, ReplicationServerSelector>();
  38. static ReplicationManager()
  39. {
  40. Groups = groups;
  41. //#if !NETCORE10
  42. // load up our selectors
  43. if (MySqlConfiguration.Settings == null) return;
  44. foreach (var group in MySqlConfiguration.Settings.Replication.ServerGroups)
  45. {
  46. ReplicationServerGroup g = AddGroup(group.Name, group.GroupType, group.RetryTime);
  47. foreach (var server in group.Servers)
  48. g.AddServer(server.Name, server.IsMaster, server.ConnectionString);
  49. }
  50. //#endif
  51. }
  52. /// <summary>
  53. /// Returns Replication Server Group List
  54. /// </summary>
  55. internal static IList<ReplicationServerGroup> Groups { get; private set; }
  56. /// <summary>
  57. /// Adds a Default Server Group to the list
  58. /// </summary>
  59. /// <param name="name">Group name</param>
  60. /// <param name="retryTime">Time between reconnections for failed servers</param>
  61. /// <returns>Replication Server Group added</returns>
  62. internal static ReplicationServerGroup AddGroup(string name, int retryTime)
  63. {
  64. return AddGroup(name, null, retryTime);
  65. }
  66. /// <summary>
  67. /// Adds a Server Group to the list
  68. /// </summary>
  69. /// <param name="name">Group name</param>
  70. /// <param name="groupType">ServerGroup type reference</param>
  71. /// <param name="retryTime">Time between reconnections for failed servers</param>
  72. /// <returns>Server Group added</returns>
  73. internal static ReplicationServerGroup AddGroup(string name, string groupType, int retryTime)
  74. {
  75. if (string.IsNullOrEmpty(groupType))
  76. groupType = "Externals.MySql.Data.MySqlClient.Replication.ReplicationRoundRobinServerGroup";
  77. Type t = Type.GetType(groupType);
  78. ReplicationServerGroup g = (ReplicationServerGroup)Activator.CreateInstance(t, name, retryTime) as ReplicationServerGroup;
  79. groups.Add(g);
  80. return g;
  81. }
  82. /// <summary>
  83. /// Gets the next server from a replication group
  84. /// </summary>
  85. /// <param name="groupName">Group name</param>
  86. /// <param name="isMaster">True if the server to return must be a master</param>
  87. /// <returns>Replication Server defined by the Load Balancing plugin</returns>
  88. internal static ReplicationServer GetServer(string groupName, bool isMaster)
  89. {
  90. ReplicationServerGroup group = GetGroup(groupName);
  91. return group.GetServer(isMaster);
  92. }
  93. /// <summary>
  94. /// Gets a Server Group by name
  95. /// </summary>
  96. /// <param name="groupName">Group name</param>
  97. /// <returns>Server Group if found, otherwise throws an MySqlException</returns>
  98. internal static ReplicationServerGroup GetGroup(string groupName)
  99. {
  100. ReplicationServerGroup group = null;
  101. foreach (ReplicationServerGroup g in groups)
  102. {
  103. if (String.Compare(g.Name, groupName, StringComparison.OrdinalIgnoreCase) != 0) continue;
  104. group = g;
  105. break;
  106. }
  107. if (group == null)
  108. throw new MySqlException(String.Format(Resources.ReplicationGroupNotFound, groupName));
  109. return group;
  110. }
  111. /// <summary>
  112. /// Validates if the replication group name exists
  113. /// </summary>
  114. /// <param name="groupName">Group name to validate</param>
  115. /// <returns><c>true</c> if the replication group name is found; otherwise, <c>false</c></returns>
  116. internal static bool IsReplicationGroup(string groupName)
  117. {
  118. foreach (ReplicationServerGroup g in groups)
  119. if (String.Compare(g.Name, groupName, StringComparison.OrdinalIgnoreCase) == 0) return true;
  120. return false;
  121. }
  122. /// <summary>
  123. /// Assigns a new server driver to the connection object
  124. /// </summary>
  125. /// <param name="groupName">Group name</param>
  126. /// <param name="master">True if the server connection to assign must be a master</param>
  127. /// <param name="connection">MySqlConnection object where the new driver will be assigned</param>
  128. internal static void GetNewConnection(string groupName, bool master, MySqlConnection connection)
  129. {
  130. do
  131. {
  132. lock (thisLock)
  133. {
  134. if (!IsReplicationGroup(groupName)) return;
  135. ReplicationServerGroup group = GetGroup(groupName);
  136. ReplicationServer server = group.GetServer(master, connection.Settings);
  137. if (server == null)
  138. throw new MySqlException(Resources.Replication_NoAvailableServer);
  139. try
  140. {
  141. bool isNewServer = false;
  142. if (connection.driver == null || !connection.driver.IsOpen)
  143. {
  144. isNewServer = true;
  145. }
  146. else
  147. {
  148. MySqlConnectionStringBuilder msb = new MySqlConnectionStringBuilder(server.ConnectionString);
  149. if (!msb.Equals(connection.driver.Settings))
  150. {
  151. isNewServer = true;
  152. }
  153. }
  154. if (isNewServer)
  155. {
  156. Driver driver = Driver.Create(new MySqlConnectionStringBuilder(server.ConnectionString));
  157. connection.driver = driver;
  158. }
  159. return;
  160. }
  161. catch (MySqlException ex)
  162. {
  163. connection.driver = null;
  164. server.IsAvailable = false;
  165. MySqlTrace.LogError(ex.Number, ex.ToString());
  166. if (ex.Number == 1042)
  167. {
  168. // retry to open a failed connection and update its status
  169. group.HandleFailover(server, ex);
  170. }
  171. else
  172. throw;
  173. }
  174. }
  175. } while (true);
  176. }
  177. }
  178. }
  179. #endif