PageRenderTime 90ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/Raven.Database/Counters/Replication/CountersReplicationTopologyDiscoverer.cs

http://github.com/ayende/ravendb
C# | 271 lines | 219 code | 49 blank | 3 comment | 22 complexity | ca554cb71afc5fe2cabdbe60d3881acc MD5 | raw file
Possible License(s): GPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Apache-2.0, BSD-3-Clause, CC-BY-SA-3.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Raven.Abstractions.Connection;
  5. using Raven.Abstractions.Counters;
  6. using Raven.Abstractions.Data;
  7. using Raven.Abstractions.Extensions;
  8. using Raven.Abstractions.Logging;
  9. using Raven.Abstractions.Util;
  10. using Raven.Bundles.Replication.Data;
  11. using Raven.Client.Connection;
  12. using Raven.Database.Bundles.Replication.Data;
  13. using Raven.Json.Linq;
  14. namespace Raven.Database.Counters.Replication
  15. {
  16. internal class CountersReplicationTopologyDiscoverer
  17. {
  18. private readonly CounterStorage counterStorage;
  19. private readonly int ttl;
  20. private readonly ILog log;
  21. private readonly RavenJArray @from;
  22. private readonly HttpRavenRequestFactory requestFactory;
  23. public CountersReplicationTopologyDiscoverer(CounterStorage counterStorage, RavenJArray @from, int ttl, ILog log)
  24. {
  25. this.counterStorage = counterStorage;
  26. this.ttl = ttl;
  27. this.log = log;
  28. this.@from = @from;
  29. requestFactory = new HttpRavenRequestFactory();
  30. }
  31. public CountersReplicationTopologyRootNode Discover()
  32. {
  33. var root = new CountersReplicationTopologyRootNode(counterStorage.CounterStorageUrl, counterStorage.ServerId);
  34. if (ttl <= 0)
  35. return root;
  36. CountersReplicationDocument replicationData;
  37. IEnumerable<CounterStorage.ReplicationSourceInfo> serverSources;
  38. using (var reader = counterStorage.CreateReader())
  39. {
  40. replicationData = reader.GetReplicationData();
  41. serverSources = reader.GetReplicationSources().ToList();
  42. }
  43. if (@from.Contains(counterStorage.CounterStorageUrl) == false)
  44. {
  45. @from.Add(counterStorage.CounterStorageUrl);
  46. }
  47. if (replicationData != null)
  48. root.Destinations = HandleDestinations(replicationData.Destinations);
  49. root.Sources = HandleSources(serverSources, root);
  50. return root;
  51. }
  52. private List<CountersReplicationTopologySourceNode> HandleSources(IEnumerable<CounterStorage.ReplicationSourceInfo> serverSources, CountersReplicationTopologyRootNode root)
  53. {
  54. return serverSources
  55. .Select(HandleSource)
  56. .ToList();
  57. }
  58. private CountersReplicationTopologySourceNode HandleSource(CounterStorage.ReplicationSourceInfo source)
  59. {
  60. if (from.Contains(source.SourceName))
  61. {
  62. var state = CheckSourceConnectionState(source.SourceName);
  63. switch (state)
  64. {
  65. case ReplicatonNodeState.Online:
  66. return CountersReplicationTopologySourceNode.Online(source.SourceName, source.ServerId, source.Etag);
  67. case ReplicatonNodeState.Offline:
  68. return CountersReplicationTopologySourceNode.Offline(source.SourceName, source.ServerId, source.Etag);
  69. default:
  70. throw new NotSupportedException(state.ToString());
  71. }
  72. }
  73. string error;
  74. CountersReplicationTopologyRootNode rootNode;
  75. if (TryGetSchema(source.SourceName, new RavenConnectionStringOptions(), out rootNode, out error))
  76. {
  77. var node = CountersReplicationTopologySourceNode.Online(source.SourceName, source.ServerId, source.Etag);
  78. node.Destinations = rootNode.Destinations;
  79. node.Sources = rootNode.Sources;
  80. node.Errors = rootNode.Errors;
  81. return node;
  82. }
  83. var offline = CountersReplicationTopologySourceNode.Online(source.SourceName, source.ServerId, source.Etag);
  84. if (string.IsNullOrEmpty(error) == false)
  85. offline.Errors.Add(error);
  86. return offline;
  87. }
  88. private List<CountersReplicationTopologyDestinationNode> HandleDestinations(List<CounterReplicationDestination> destinations)
  89. {
  90. return destinations
  91. .Select(HandleDestination)
  92. .ToList();
  93. }
  94. private CountersReplicationTopologyDestinationNode HandleDestination(CounterReplicationDestination replicationDestination)
  95. {
  96. RavenConnectionStringOptions connectionStringOptions = new RavenConnectionStringOptions
  97. {
  98. Credentials = replicationDestination.Credentials,
  99. ApiKey = replicationDestination.ApiKey,
  100. Url = replicationDestination.ServerUrl
  101. };
  102. string error;
  103. string targetServerUrl;
  104. // since each server can be addresses using both dns and ips we normalize connection string url by fetching target server url
  105. // it should give us consistent urls
  106. if (FetchTargetServerUrl(replicationDestination.ServerUrl, connectionStringOptions, out targetServerUrl, out error) == false)
  107. {
  108. var offlineNode = CountersReplicationTopologyDestinationNode.Offline(replicationDestination.CounterStorageUrl, counterStorage.ServerId);
  109. if (string.IsNullOrEmpty(error) == false)
  110. offlineNode.Errors.Add(error);
  111. return offlineNode;
  112. }
  113. targetServerUrl = targetServerUrl.ForCounter(replicationDestination.CounterStorageName);
  114. if (replicationDestination.Disabled)
  115. return CountersReplicationTopologyDestinationNode.Disabled(targetServerUrl, counterStorage.ServerId);
  116. if (from.Contains(targetServerUrl))
  117. {
  118. var state = CheckConnectionState(replicationDestination.CounterStorageUrl, connectionStringOptions);
  119. switch (state)
  120. {
  121. case ReplicatonNodeState.Online:
  122. return CountersReplicationTopologyDestinationNode.Online(targetServerUrl, counterStorage.ServerId);
  123. case ReplicatonNodeState.Offline:
  124. return CountersReplicationTopologyDestinationNode.Offline(targetServerUrl, counterStorage.ServerId);
  125. default:
  126. throw new NotSupportedException(state.ToString());
  127. }
  128. }
  129. CountersReplicationTopologyRootNode rootNode;
  130. if (TryGetSchema(targetServerUrl, connectionStringOptions, out rootNode, out error))
  131. {
  132. var node = CountersReplicationTopologyDestinationNode.Online(targetServerUrl, counterStorage.ServerId);
  133. node.Destinations = rootNode.Destinations;
  134. node.Sources = rootNode.Sources;
  135. node.Errors = rootNode.Errors;
  136. return node;
  137. }
  138. var offline = CountersReplicationTopologyDestinationNode.Offline(targetServerUrl, counterStorage.ServerId);
  139. if (string.IsNullOrEmpty(error) == false)
  140. offline.Errors.Add(error);
  141. return offline;
  142. }
  143. private bool FetchTargetServerUrl(string serverUrl, RavenConnectionStringOptions connectionStringOptions, out string targetServerUrl, out string error)
  144. {
  145. var url = string.Format("{0}/debug/config", serverUrl);
  146. try
  147. {
  148. var request = requestFactory.Create(url, HttpMethods.Get, connectionStringOptions);
  149. error = null;
  150. var ravenConfig = request.ExecuteRequest<RavenJObject>();
  151. var serverUrlFromTargetConfig = ravenConfig.Value<string>("ServerUrl");
  152. // replace host name with target hostname
  153. targetServerUrl = new UriBuilder(serverUrl) {Host = new Uri(serverUrlFromTargetConfig).Host}.Uri.ToString();
  154. return true;
  155. }
  156. catch (Exception e)
  157. {
  158. error = e.Message;
  159. targetServerUrl = null;
  160. return false;
  161. }
  162. }
  163. private bool TryGetSchema(string serverUrl, RavenConnectionStringOptions connectionStringOptions, out CountersReplicationTopologyRootNode rootNode, out string error)
  164. {
  165. var url = string.Format("{0}/admin/replication/topology/discover?&ttl={1}", serverUrl, ttl - 1);
  166. try
  167. {
  168. var request = requestFactory.Create(url, HttpMethods.Post, connectionStringOptions);
  169. request.Write(from);
  170. error = null;
  171. rootNode = request.ExecuteRequest<CountersReplicationTopologyRootNode>();
  172. var visitedNodes = new HashSet<string>();
  173. FindVisitedNodes(rootNode, visitedNodes);
  174. foreach (var visitedNode in visitedNodes)
  175. {
  176. if (@from.Contains(visitedNode) == false)
  177. {
  178. @from.Add(visitedNode);
  179. }
  180. }
  181. return true;
  182. }
  183. catch (Exception e)
  184. {
  185. error = e.Message;
  186. rootNode = null;
  187. return false;
  188. }
  189. }
  190. private void FindVisitedNodes(CountersReplicationTopologyNodeBase rootNode, HashSet<string> visitedNodes)
  191. {
  192. visitedNodes.Add(rootNode.ServerUrl);
  193. foreach (var source in rootNode.Sources)
  194. {
  195. FindVisitedNodes(source, visitedNodes);
  196. }
  197. foreach (var destinationNode in rootNode.Destinations)
  198. {
  199. FindVisitedNodes(destinationNode, visitedNodes);
  200. }
  201. }
  202. private ReplicatonNodeState CheckSourceConnectionState(string sourceUrl)
  203. {
  204. return CheckConnectionState(sourceUrl, new RavenConnectionStringOptions());
  205. }
  206. private ReplicatonNodeState CheckConnectionState(string serverUrl, RavenConnectionStringOptions connectionStringOptions)
  207. {
  208. try
  209. {
  210. var url = string.Format("{0}/replication/heartbeat?&sourceServer={1}", serverUrl, Uri.EscapeDataString(counterStorage.CounterStorageUrl));
  211. var request = requestFactory.Create(url, HttpMethods.Post, connectionStringOptions);
  212. request.ExecuteRequest();
  213. }
  214. catch (Exception e)
  215. {
  216. log.ErrorException(string.Format("Could not connect to '{0}'.", serverUrl), e);
  217. return ReplicatonNodeState.Offline;
  218. }
  219. return ReplicatonNodeState.Online;
  220. }
  221. }
  222. }