From commits-return-120613-archive-asf-public=cust-asf.ponee.io@ignite.apache.org Thu Sep 27 18:50:01 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 4CE0A180675 for ; Thu, 27 Sep 2018 18:49:59 +0200 (CEST) Received: (qmail 16622 invoked by uid 500); 27 Sep 2018 16:49:58 -0000 Mailing-List: contact commits-help@ignite.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ignite.apache.org Delivered-To: mailing list commits@ignite.apache.org Received: (qmail 16613 invoked by uid 99); 27 Sep 2018 16:49:58 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 27 Sep 2018 16:49:58 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 75C06DFB1A; Thu, 27 Sep 2018 16:49:56 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: ptupitsyn@apache.org To: commits@ignite.apache.org Date: Thu, 27 Sep 2018 16:49:57 -0000 Message-Id: <40fac7265c804909b87d213aa9a392be@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [2/2] ignite git commit: IGNITE-7282 .NET: Thin client: Implement Automatic Reconnect and Failover IGNITE-7282 .NET: Thin client: Implement Automatic Reconnect and Failover This closes #3467 Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/45abb9c7 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/45abb9c7 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/45abb9c7 Branch: refs/heads/master Commit: 45abb9c7069291d1bafa4a26edf23a36cc527716 Parents: 4527fe6 Author: Pavel Tupitsyn Authored: Thu Sep 27 19:49:38 2018 +0300 Committer: Pavel Tupitsyn Committed: Thu Sep 27 19:49:38 2018 +0300 ---------------------------------------------------------------------- .../Interop/PlatformBenchmarkBase.cs | 2 +- .../Apache.Ignite.Core.Tests.DotNetCore.csproj | 1 + .../Apache.Ignite.Core.Tests.csproj | 1 + .../Client/Cache/CacheTestNoMeta.cs | 4 +- .../Client/Cache/CacheTestSsl.cs | 5 +- .../Client/ClientConnectionTest.cs | 222 ++++++++++++++--- .../Client/ClientTestBase.cs | 4 +- .../Client/EndpointTest.cs | 96 +++++++ .../Client/IgniteClientConfigurationTest.cs | 12 +- .../Client/RawSecureSocketTest.cs | 18 +- .../Config/Client/IgniteClientConfiguration.xml | Bin 3144 -> 3384 bytes .../Dataload/DataStreamerTest.cs | 23 +- .../Apache.Ignite.Core.Tests/ExceptionsTest.cs | 3 +- .../Apache.Ignite.Core.Tests/custom_app.config | 6 +- .../Apache.Ignite.Core.csproj | 5 +- .../Apache.Ignite.Core/Client/IIgniteClient.cs | 15 ++ .../Client/IgniteClientConfiguration.cs | 44 ++++ .../IgniteClientConfigurationSection.xsd | 19 +- .../Apache.Ignite.Core/IgniteConfiguration.cs | 1 + .../dotnet/Apache.Ignite.Core/Ignition.cs | 1 - .../Impl/Binary/BinaryProcessorClient.cs | 4 +- .../Impl/Client/Cache/CacheClient.cs | 10 +- .../Impl/Client/ClientFailoverSocket.cs | 247 +++++++++++++++++++ .../Impl/Client/ClientSocket.cs | 166 +++++++------ .../Apache.Ignite.Core/Impl/Client/Endpoint.cs | 148 +++++++++++ .../Impl/Client/IClientSocket.cs | 58 +++++ .../Impl/Client/IgniteClient.cs | 28 ++- .../Impl/Common/IgniteArgumentCheck.cs | 10 +- .../dotnet/Apache.Ignite.sln.DotSettings | 1 + .../ThinClient/ThinClientPutGetExample.cs | 5 +- .../ThinClient/ThinClientQueryExample.cs | 5 +- .../ThinClient/ThinClientSqlExample.cs | 21 +- 32 files changed, 999 insertions(+), 186 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/PlatformBenchmarkBase.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/PlatformBenchmarkBase.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/PlatformBenchmarkBase.cs index ca9fb0c..a234e9a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/PlatformBenchmarkBase.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/PlatformBenchmarkBase.cs @@ -135,7 +135,7 @@ namespace Apache.Ignite.Benchmarks.Interop { return new IgniteClientConfiguration { - Host = IPAddress.Loopback.ToString() + Endpoints = new[] {IPAddress.Loopback.ToString()} }; } } http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj index 69170c2..e27f8b7 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Apache.Ignite.Core.Tests.DotNetCore.csproj @@ -107,6 +107,7 @@ + http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj index 3a4ef03..aa58afc 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj @@ -145,6 +145,7 @@ + http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs index 0a8b146..1978243 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs @@ -46,7 +46,7 @@ namespace Apache.Ignite.Core.Tests.Client.Cache { CompactFooter = false }, - Host = IPAddress.Loopback.ToString() + Endpoints = new[] {IPAddress.Loopback.ToString()} }; using (var client = Ignition.StartClient(cfg)) @@ -126,4 +126,4 @@ namespace Apache.Ignite.Core.Tests.Client.Cache } } } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestSsl.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestSsl.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestSsl.cs index 95d45af..0f55ce5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestSsl.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestSsl.cs @@ -18,6 +18,7 @@ namespace Apache.Ignite.Core.Tests.Client.Cache { using System.IO; + using System.Net; using System.Security.Authentication; using Apache.Ignite.Core.Client; using NUnit.Framework; @@ -46,7 +47,7 @@ namespace Apache.Ignite.Core.Tests.Client.Cache { return new IgniteClientConfiguration(base.GetClientConfiguration()) { - Port = 11110, + Endpoints = new[] {IPAddress.Loopback + ":11110"}, SslStreamFactory = new SslStreamFactory { CertificatePath = Path.Combine("Config", "Client", "thin-client-cert.pfx"), @@ -57,7 +58,7 @@ namespace Apache.Ignite.Core.Tests.Client.Cache SslProtocols = SslProtocols.Tls #else SslProtocols = SslProtocols.Tls12 -#endif +#endif } }; } http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs index 14d1abf..0c202bc 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs @@ -18,7 +18,6 @@ namespace Apache.Ignite.Core.Tests.Client { using System; - using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; @@ -53,7 +52,7 @@ namespace Apache.Ignite.Core.Tests.Client } /// - /// Fixture tear down. + /// Test tear down. /// [TearDown] public void TearDown() @@ -87,7 +86,7 @@ namespace Apache.Ignite.Core.Tests.Client { using (Ignition.Start(SecureServerConfig())) { - var cliCfg = SecureClientConfig(); + var cliCfg = GetSecureClientConfig(); cliCfg.Password = null; var ex = Assert.Throws(() => { Ignition.StartClient(cliCfg); }); @@ -117,7 +116,7 @@ namespace Apache.Ignite.Core.Tests.Client { using (Ignition.Start(SecureServerConfig())) { - var cliCfg = SecureClientConfig(); + var cliCfg = GetSecureClientConfig(); cliCfg.UserName = "invalid"; @@ -142,9 +141,9 @@ namespace Apache.Ignite.Core.Tests.Client { srv.GetCluster().SetActive(true); - using (var cli = Ignition.StartClient(SecureClientConfig())) + using (var cli = Ignition.StartClient(GetSecureClientConfig())) { - CacheClientConfiguration ccfg = new CacheClientConfiguration() + CacheClientConfiguration ccfg = new CacheClientConfiguration { Name = "TestCache", QueryEntities = new[] @@ -164,7 +163,7 @@ namespace Apache.Ignite.Core.Tests.Client cache.Query(new SqlFieldsQuery("CREATE USER \"my_User\" WITH PASSWORD 'my_Password'")).GetAll(); } - var cliCfg = SecureClientConfig(); + var cliCfg = GetSecureClientConfig(); cliCfg.UserName = "my_User"; cliCfg.Password = "my_Password"; @@ -219,8 +218,7 @@ namespace Apache.Ignite.Core.Tests.Client var clientCfg = new IgniteClientConfiguration { - Host = "localhost", - Port = 2000 + Endpoints = new[] {"localhost:2000"} }; using (Ignition.Start(servCfg)) @@ -233,12 +231,48 @@ namespace Apache.Ignite.Core.Tests.Client } /// + /// Tests client config with EndPoints property. + /// + [Test] + public void TestEndPoints() + { + using (var ignite = Ignition.Start(TestUtils.GetTestConfiguration())) + { + ignite.CreateCache("foo"); + + const int port = IgniteClientConfiguration.DefaultPort; + + // DnsEndPoint. + var cfg = new IgniteClientConfiguration + { + Endpoints = new[] { "localhost" } + }; + + using (var client = Ignition.StartClient(cfg)) + { + Assert.AreEqual("foo", client.GetCacheNames().Single()); + } + + // IPEndPoint. + cfg = new IgniteClientConfiguration + { + Endpoints = new[] { "127.0.0.1:" + port } + }; + + using (var client = Ignition.StartClient(cfg)) + { + Assert.AreEqual("foo", client.GetCacheNames().Single()); + } + } + } + + /// /// Tests that default configuration throws. /// [Test] public void TestDefaultConfigThrows() { - Assert.Throws(() => Ignition.StartClient(new IgniteClientConfiguration())); + Assert.Throws(() => Ignition.StartClient(new IgniteClientConfiguration())); } /// @@ -253,7 +287,13 @@ namespace Apache.Ignite.Core.Tests.Client // ReSharper disable once ObjectCreationAsStatement var ex = Assert.Throws(() => new ClientSocket(GetClientConfiguration(), - new ClientProtocolVersion(-1, -1, -1))); + new DnsEndPoint( + "localhost", + ClientConnectorConfiguration.DefaultPort, + AddressFamily.InterNetwork), + null, + null, + new ClientProtocolVersion(-1, -1, -1))); Assert.AreEqual(ClientStatusCode.Fail, ex.StatusCode); @@ -275,7 +315,7 @@ namespace Apache.Ignite.Core.Tests.Client var clientCfg = new IgniteClientConfiguration { - Host = "localhost" + Endpoints = new[] {"localhost"} }; using (Ignition.Start(servCfg)) @@ -298,7 +338,7 @@ namespace Apache.Ignite.Core.Tests.Client { var ex = Assert.Throws(() => Ignition.StartClient(clientCfg)); Assert.AreEqual("Client handshake failed: 'Thin client connection is not allowed, " + - "see ClientConnectorConfiguration.thinClientEnabled.'.", + "see ClientConnectorConfiguration.thinClientEnabled.'.", ex.Message.Substring(0, 118)); } } @@ -385,16 +425,17 @@ namespace Apache.Ignite.Core.Tests.Client { Ignition.Start(TestUtils.GetTestConfiguration()); - var ops = new List(); + const int count = 100000; + var ops = new Task[count]; using (var client = StartClient()) { var cache = client.GetOrCreateCache("foo"); - for (var i = 0; i < 100000; i++) - { - ops.Add(cache.PutAsync(i, i)); - } - ops.First().Wait(); + Parallel.For(0, count, new ParallelOptions {MaxDegreeOfParallelism = 16}, + i => + { + ops[i] = cache.PutAsync(i, i); + }); } var completed = ops.Count(x => x.Status == TaskStatus.RanToCompletion); @@ -408,7 +449,7 @@ namespace Apache.Ignite.Core.Tests.Client var ex = task.Exception; Assert.IsNotNull(ex); var baseEx = ex.GetBaseException(); - Assert.IsNotNull((object) (baseEx as SocketException) ?? baseEx as ObjectDisposedException, + Assert.IsNotNull((object) (baseEx as SocketException) ?? baseEx as ObjectDisposedException, ex.ToString()); } } @@ -436,10 +477,10 @@ namespace Apache.Ignite.Core.Tests.Client var cache = client.GetOrCreateCache("foo"); cache[1] = 1; Assert.AreEqual(1, cache[1]); - + Thread.Sleep(90); Assert.AreEqual(1, cache[1]); - + // Idle check frequency is 2 seconds. Thread.Sleep(4000); var ex = Assert.Catch(() => cache.Get(1)); @@ -456,13 +497,133 @@ namespace Apache.Ignite.Core.Tests.Client using (Ignition.Start(TestUtils.GetTestConfiguration())) { // Connect to Ignite REST endpoint. - var cfg = new IgniteClientConfiguration {Host = "127.0.0.1", Port = 11211 }; - var ex = Assert.Throws(() => Ignition.StartClient(cfg)); + var cfg = new IgniteClientConfiguration("127.0.0.1:11211"); + var ex = GetSocketException(Assert.Catch(() => Ignition.StartClient(cfg))); Assert.AreEqual(SocketError.ConnectionAborted, ex.SocketErrorCode); } } /// + /// Tests reconnect logic with single server. + /// + [Test] + public void TestReconnect() + { + // Connect client and check. + Ignition.Start(TestUtils.GetTestConfiguration()); + var client = Ignition.StartClient(new IgniteClientConfiguration("127.0.0.1")); + Assert.AreEqual(0, client.GetCacheNames().Count); + + var ep = client.RemoteEndPoint as IPEndPoint; + Assert.IsNotNull(ep); + Assert.AreEqual(IgniteClientConfiguration.DefaultPort, ep.Port); + Assert.AreEqual("127.0.0.1", ep.Address.ToString()); + + ep = client.LocalEndPoint as IPEndPoint; + Assert.IsNotNull(ep); + Assert.AreNotEqual(IgniteClientConfiguration.DefaultPort, ep.Port); + Assert.AreEqual("127.0.0.1", ep.Address.ToString()); + + // Stop server. + Ignition.StopAll(true); + + // First request fails, error is detected. + var ex = Assert.Catch(() => client.GetCacheNames()); + Assert.IsNotNull(GetSocketException(ex)); + + // Second request causes reconnect attempt which fails (server is stopped). + var aex = Assert.Throws(() => client.GetCacheNames()); + Assert.AreEqual("Failed to establish Ignite thin client connection, " + + "examine inner exceptions for details.", aex.Message.Substring(0, 88)); + + // Start server, next operation succeeds. + Ignition.Start(TestUtils.GetTestConfiguration()); + Assert.AreEqual(0, client.GetCacheNames().Count); + } + + /// + /// Tests disabled reconnect behavior. + /// + [Test] + public void TestReconnectDisabled() + { + // Connect client and check. + Ignition.Start(TestUtils.GetTestConfiguration()); + using (var client = Ignition.StartClient(new IgniteClientConfiguration("127.0.0.1") + { + ReconnectDisabled = true + })) + { + Assert.AreEqual(0, client.GetCacheNames().Count); + + // Stop server. + Ignition.StopAll(true); + + // Request fails, error is detected. + var ex = Assert.Catch(() => client.GetCacheNames()); + Assert.IsNotNull(GetSocketException(ex)); + + // Restart server, client does not reconnect. + Ignition.Start(TestUtils.GetTestConfiguration()); + ex = Assert.Catch(() => client.GetCacheNames()); + Assert.IsNotNull(GetSocketException(ex)); + } + } + + /// + /// Tests reconnect logic with multiple servers. + /// + [Test] + public void TestFailover() + { + // Start 3 nodes. + Ignition.Start(TestUtils.GetTestConfiguration(name: "0")); + Ignition.Start(TestUtils.GetTestConfiguration(name: "1")); + Ignition.Start(TestUtils.GetTestConfiguration(name: "2")); + + // Connect client. + var port = IgniteClientConfiguration.DefaultPort; + var cfg = new IgniteClientConfiguration + { + Endpoints = new[] + { + "localhost", + string.Format("127.0.0.1:{0}..{1}", port + 1, port + 2) + } + }; + + using (var client = Ignition.StartClient(cfg)) + { + Assert.AreEqual(0, client.GetCacheNames().Count); + + // Stop target node. + var nodeId = ((IPEndPoint) client.RemoteEndPoint).Port - port; + Ignition.Stop(nodeId.ToString(), true); + + // Check failure. + Assert.IsNotNull(GetSocketException(Assert.Catch(() => client.GetCacheNames()))); + + // Check reconnect. + Assert.AreEqual(0, client.GetCacheNames().Count); + + // Stop target node. + nodeId = ((IPEndPoint) client.RemoteEndPoint).Port - port; + Ignition.Stop(nodeId.ToString(), true); + + // Check failure. + Assert.IsNotNull(GetSocketException(Assert.Catch(() => client.GetCacheNames()))); + + // Check reconnect. + Assert.AreEqual(0, client.GetCacheNames().Count); + + // Stop all nodes. + Ignition.StopAll(true); + Assert.IsNotNull(GetSocketException(Assert.Catch(() => client.GetCacheNames()))); + Assert.IsNotNull(GetSocketException(Assert.Catch(() => client.GetCacheNames()))); + } + } + + /// /// Starts the client. /// private static IIgniteClient StartClient() @@ -475,7 +636,7 @@ namespace Apache.Ignite.Core.Tests.Client /// private static IgniteClientConfiguration GetClientConfiguration() { - return new IgniteClientConfiguration { Host = IPAddress.Loopback.ToString() }; + return new IgniteClientConfiguration(IPAddress.Loopback.ToString()); } /// @@ -497,7 +658,7 @@ namespace Apache.Ignite.Core.Tests.Client ex = ex.InnerException; } - + throw new Exception("SocketException not found.", origEx); } @@ -510,12 +671,12 @@ namespace Apache.Ignite.Core.Tests.Client return new IgniteConfiguration(TestUtils.GetTestConfiguration()) { AuthenticationEnabled = true, - DataStorageConfiguration = new DataStorageConfiguration() + DataStorageConfiguration = new DataStorageConfiguration { StoragePath = Path.Combine(_tempDir, "Store"), WalPath = Path.Combine(_tempDir, "WalStore"), WalArchivePath = Path.Combine(_tempDir, "WalArchive"), - DefaultDataRegionConfiguration = new DataRegionConfiguration() + DefaultDataRegionConfiguration = new DataRegionConfiguration { Name = "default", PersistenceEnabled = true @@ -528,11 +689,10 @@ namespace Apache.Ignite.Core.Tests.Client /// Create client configuration with enabled authentication. /// /// Client configuration. - private static IgniteClientConfiguration SecureClientConfig() + private static IgniteClientConfiguration GetSecureClientConfig() { - return new IgniteClientConfiguration() + return new IgniteClientConfiguration("localhost") { - Host = "localhost", UserName = "ignite", Password = "ignite" }; http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs index db5e621..c6aee32 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientTestBase.cs @@ -91,7 +91,7 @@ namespace Apache.Ignite.Core.Tests.Client var cache = GetCache(); cache.RemoveAll(); cache.Clear(); - + Assert.AreEqual(0, cache.GetSize(CachePeekMode.All)); Assert.AreEqual(0, GetClientCache().GetSize(CachePeekMode.All)); } @@ -140,7 +140,7 @@ namespace Apache.Ignite.Core.Tests.Client { return new IgniteClientConfiguration { - Host = IPAddress.Loopback.ToString() + Endpoints = new[] {IPAddress.Loopback.ToString()} }; } http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/EndpointTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/EndpointTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/EndpointTest.cs new file mode 100644 index 0000000..05366ed --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/EndpointTest.cs @@ -0,0 +1,96 @@ +namespace Apache.Ignite.Core.Tests.Client +{ + using System.Linq; + using Apache.Ignite.Core.Client; + using Apache.Ignite.Core.Impl.Client; + using NUnit.Framework; + + /// + /// Tests for class. + /// + public class EndpointTest + { + [Test] + public void GetEndpoints_InvalidConfigFormat_ThrowsIgniteClientException() + { + var ex = AssertThrowsClientException(""); + Assert.AreEqual("IgniteClientConfiguration.Endpoints[...] can't be null or whitespace.", ex.Message); + + ex = AssertThrowsClientException("host:"); + Assert.AreEqual( + "Unrecognized format of IgniteClientConfiguration.Endpoint, failed to parse port: host:", + ex.Message); + + ex = AssertThrowsClientException("host:port"); + Assert.AreEqual( + "Unrecognized format of IgniteClientConfiguration.Endpoint, failed to parse port: host:port", + ex.Message); + + ex = AssertThrowsClientException("host:1.."); + Assert.AreEqual( + "Unrecognized format of IgniteClientConfiguration.Endpoint, failed to parse port: host:1..", + ex.Message); + + ex = AssertThrowsClientException("host:1..2..3"); + Assert.AreEqual( + "Unrecognized format of IgniteClientConfiguration.Endpoint: host:1..2..3", + ex.Message); + } + + [Test] + public void GetEndpoints_ParsesPortsAndRanges() + { + const string ip = "1.2.3.4"; + const string host = "example.com"; + const int port = 678; + const int port2 = 680; + + var ipWithDefaultPort = Endpoint.GetEndpoints(new IgniteClientConfiguration(ip)).Single(); + Assert.AreEqual(ip, ipWithDefaultPort.Host); + Assert.AreEqual(IgniteClientConfiguration.DefaultPort, ipWithDefaultPort.Port); + Assert.AreEqual(0, ipWithDefaultPort.PortRange); + + var ipWithCustomPort = Endpoint + .GetEndpoints(new IgniteClientConfiguration(string.Format("{0}:{1}", ip, port))) + .Single(); + Assert.AreEqual(ip, ipWithCustomPort.Host); + Assert.AreEqual(port, ipWithCustomPort.Port); + Assert.AreEqual(0, ipWithCustomPort.PortRange); + + var ipWithPortRange = Endpoint + .GetEndpoints(new IgniteClientConfiguration(string.Format("{0}:{1}..{2}", ip, port, port2))) + .Single(); + Assert.AreEqual(ip, ipWithPortRange.Host); + Assert.AreEqual(port, ipWithPortRange.Port); + Assert.AreEqual(port2 - port, ipWithPortRange.PortRange); + + var hostWithDefaultPort = Endpoint.GetEndpoints(new IgniteClientConfiguration(host)).Single(); + Assert.AreEqual(host, hostWithDefaultPort.Host); + Assert.AreEqual(IgniteClientConfiguration.DefaultPort, hostWithDefaultPort.Port); + Assert.AreEqual(0, hostWithDefaultPort.PortRange); + + var hostWithCustomPort = Endpoint + .GetEndpoints(new IgniteClientConfiguration(string.Format("{0}:{1}", host, port))) + .Single(); + Assert.AreEqual(host, hostWithCustomPort.Host); + Assert.AreEqual(port, hostWithCustomPort.Port); + Assert.AreEqual(0, hostWithCustomPort.PortRange); + + var hostWithPortRange = Endpoint + .GetEndpoints(new IgniteClientConfiguration(string.Format("{0}:{1}..{2}", host, port, port2))) + .Single(); + Assert.AreEqual(host, hostWithPortRange.Host); + Assert.AreEqual(port, hostWithPortRange.Port); + Assert.AreEqual(port2 - port, hostWithPortRange.PortRange); + + } + + private static IgniteClientException AssertThrowsClientException(string endpoint) + { + var endpoints = Endpoint.GetEndpoints(new IgniteClientConfiguration(endpoint)); + + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed + return Assert.Throws(() => endpoints.ToList()); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs index 09decc6..3d55f4c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs @@ -15,6 +15,7 @@ * limitations under the License. */ +#pragma warning disable 618 namespace Apache.Ignite.Core.Tests.Client { using System; @@ -84,6 +85,12 @@ namespace Apache.Ignite.Core.Tests.Client CheckCertificateRevocation = true, SkipServerCertificateValidation = true, SslProtocols = SslProtocols.None + }, + Endpoints = new [] + { + "foo", + "bar:123", + "baz:100..103" } }; @@ -177,7 +184,7 @@ namespace Apache.Ignite.Core.Tests.Client } // Missing section content. - ex = Assert.Throws(() => + ex = Assert.Throws(() => Ignition.StartClient("igniteClientConfiguration3")); Assert.AreEqual("IgniteClientConfigurationSection with name 'igniteClientConfiguration3' is " + "defined in , but not present in configuration.", ex.Message); @@ -210,7 +217,8 @@ namespace Apache.Ignite.Core.Tests.Client public void TestAllPropertiesArePresentInSchema() { IgniteConfigurationSerializerTest.CheckAllPropertiesArePresentInSchema( - "IgniteClientConfigurationSection.xsd", "igniteClientConfiguration", typeof(IgniteClientConfiguration)); + "IgniteClientConfigurationSection.xsd", "igniteClientConfiguration", + typeof(IgniteClientConfiguration)); } #endif } http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSecureSocketTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSecureSocketTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSecureSocketTest.cs index 11b4e86..379e7e3 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSecureSocketTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/RawSecureSocketTest.cs @@ -23,7 +23,6 @@ namespace Apache.Ignite.Core.Tests.Client using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; - using Apache.Ignite.Core.Client; using Apache.Ignite.Core.Impl.Binary.IO; using NUnit.Framework; @@ -38,25 +37,22 @@ namespace Apache.Ignite.Core.Tests.Client [Test] public void TestHandshake() { - var icfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) + var igniteConfiguration = new IgniteConfiguration(TestUtils.GetTestConfiguration()) { SpringConfigUrl = Path.Combine("Config", "Client", "server-with-ssl.xml") }; - using (Ignition.Start(icfg)) + using (Ignition.Start(igniteConfiguration)) { - var cfg = new IgniteClientConfiguration - { - Host = "127.0.0.1", - Port = 11110 - }; + const string host = "127.0.0.1"; + const int port = 11110; - using (var client = new TcpClient(cfg.Host, cfg.Port)) + using (var client = new TcpClient(host, port)) using (var sslStream = new SslStream(client.GetStream(), false, ValidateServerCertificate, null)) { var certsCollection = new X509CertificateCollection(new X509Certificate[] {LoadCertificateFile()}); - sslStream.AuthenticateAsClient(cfg.Host, certsCollection, SslProtocols.Tls, false); + sslStream.AuthenticateAsClient(host, certsCollection, SslProtocols.Tls, false); Assert.IsTrue(sslStream.IsAuthenticated); Assert.IsTrue(sslStream.IsMutuallyAuthenticated); @@ -87,7 +83,7 @@ namespace Apache.Ignite.Core.Tests.Client /// private static X509Certificate2 LoadCertificateFile() { - // Conveting from JKS to PFX: + // Converting from JKS to PFX: // keytool -importkeystore -srckeystore thekeystore.jks -srcstoretype JKS // -destkeystore thekeystore.pfx -deststoretype PKCS12 return new X509Certificate2(Path.Combine("Config", "Client", "thin-client-cert.pfx"), "123456"); http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Client/IgniteClientConfiguration.xml ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Client/IgniteClientConfiguration.xml b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Client/IgniteClientConfiguration.xml index 8d8d9bf..89f9a69 100644 Binary files a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Client/IgniteClientConfiguration.xml and b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/Client/IgniteClientConfiguration.xml differ http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Dataload/DataStreamerTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Dataload/DataStreamerTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Dataload/DataStreamerTest.cs index f1a25c6..3e69d20 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Dataload/DataStreamerTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Dataload/DataStreamerTest.cs @@ -115,7 +115,7 @@ namespace Apache.Ignite.Core.Tests.Dataload Assert.AreEqual(1, ldr.PerNodeBufferSize); ldr.PerNodeBufferSize = 2; Assert.AreEqual(2, ldr.PerNodeBufferSize); - + Assert.AreEqual(DataStreamerDefaults.DefaultPerThreadBufferSize, ldr.PerThreadBufferSize); ldr.PerThreadBufferSize = 1; Assert.AreEqual(1, ldr.PerThreadBufferSize); @@ -141,7 +141,7 @@ namespace Apache.Ignite.Core.Tests.Dataload /// /// Test data add/remove. /// - [Test] + [Test] public void TestAddRemove() { IDataStreamer ldr; @@ -154,7 +154,7 @@ namespace Apache.Ignite.Core.Tests.Dataload // Additions. var task = ldr.AddData(1, 1); - ldr.Flush(); + ldr.Flush(); Assert.AreEqual(1, _cache.Get(1)); Assert.IsTrue(task.IsCompleted); Assert.IsFalse(ldr.Task.IsCompleted); @@ -177,7 +177,7 @@ namespace Apache.Ignite.Core.Tests.Dataload Assert.IsTrue(task.IsCompleted); // Mixed. - ldr.AddData(5, 5); + ldr.AddData(5, 5); ldr.RemoveData(2); ldr.AddData(new KeyValuePair(7, 7)); ldr.AddData(6, 6); @@ -234,7 +234,7 @@ namespace Apache.Ignite.Core.Tests.Dataload Assert.IsNotNull(cache[2].Inner.Inner); Assert.IsNotNull(cache[3].Inner); Assert.IsNotNull(cache[3].Inner.Inner); - + Assert.IsNotNull(cache[4].Inner); Assert.IsNull(cache[4].Inner.Inner); } @@ -310,7 +310,7 @@ namespace Apache.Ignite.Core.Tests.Dataload private static int[] GetPrimaryPartitionKeys(IIgnite ignite, int count) { var affinity = ignite.GetAffinity(CacheName); - + var localNode = ignite.GetCluster().GetLocalNode(); var part = affinity.GetPrimaryPartitions(localNode).First(); @@ -361,10 +361,9 @@ namespace Apache.Ignite.Core.Tests.Dataload /// Tests that streamer gets collected when there are no references to it. /// [Test] + [Ignore("IGNITE-8731")] public void TestFinalizer() { - Assert.Fail("https://issues.apache.org/jira/browse/IGNITE-8731"); - var streamer = _grid.GetDataStreamer(CacheName); var streamerRef = new WeakReference(streamer); @@ -391,7 +390,7 @@ namespace Apache.Ignite.Core.Tests.Dataload var fut = ldr.AddData(1, 1); Thread.Sleep(100); Assert.IsFalse(fut.IsCompleted); - ldr.AutoFlushFrequency = 1000; + ldr.AutoFlushFrequency = 1000; fut.Wait(); // Test forced flush after frequency change. @@ -423,7 +422,7 @@ namespace Apache.Ignite.Core.Tests.Dataload } /// - /// Test multithreaded behavior. + /// Test multithreaded behavior. /// [Test] [Category(TestUtils.CategoryIntensive)] @@ -619,7 +618,7 @@ namespace Apache.Ignite.Core.Tests.Dataload public int Process(IMutableCacheEntry entry, int arg) { entry.Value = entry.Key + 1; - + return 0; } } @@ -633,7 +632,7 @@ namespace Apache.Ignite.Core.Tests.Dataload public int Process(IMutableCacheEntry entry, int arg) { entry.Value = entry.Key + 1; - + return 0; } http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExceptionsTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExceptionsTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExceptionsTest.cs index 0b06ea3..5e84823 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExceptionsTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExceptionsTest.cs @@ -16,14 +16,13 @@ */ #pragma warning disable 618 -namespace Apache.Ignite.Core.Tests +namespace Apache.Ignite.Core.Tests { using System; using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using System.Threading; - using System.Threading.Tasks; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Cache; using Apache.Ignite.Core.Cluster; http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core.Tests/custom_app.config ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/custom_app.config b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/custom_app.config index b79279ae..9fa96f5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/custom_app.config +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/custom_app.config @@ -36,5 +36,9 @@ - + + + 127.0.0.1 + + http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj index 273e088..57357da 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -78,6 +78,7 @@ + @@ -103,6 +104,8 @@ + + @@ -597,4 +600,4 @@ --> - + \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Client/IIgniteClient.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Client/IIgniteClient.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IIgniteClient.cs index 2b24aa4..813634b 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Client/IIgniteClient.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IIgniteClient.cs @@ -20,6 +20,7 @@ namespace Apache.Ignite.Core.Client using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using System.Net; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Client.Cache; @@ -108,5 +109,19 @@ namespace Apache.Ignite.Core.Client /// [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Semantics.")] IgniteClientConfiguration GetConfiguration(); + + /// + /// Gets the current remote EndPoint. + /// + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", + Justification = "Consistency with EndPoint class name.")] + EndPoint RemoteEndPoint { get; } + + /// + /// Gets the current local EndPoint. + /// + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", + Justification = "Consistency with EndPoint class name.")] + EndPoint LocalEndPoint { get; } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs index 80f26cf..311dfbe 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs @@ -18,7 +18,10 @@ namespace Apache.Ignite.Core.Client { using System; + using System.Collections.Generic; using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + using System.Linq; using System.Xml; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Impl.Binary; @@ -58,7 +61,9 @@ namespace Apache.Ignite.Core.Client /// public IgniteClientConfiguration() { +#pragma warning disable 618 Port = DefaultPort; +#pragma warning restore 618 SocketSendBufferSize = DefaultSocketBufferSize; SocketReceiveBufferSize = DefaultSocketBufferSize; TcpNoDelay = DefaultTcpNoDelay; @@ -66,6 +71,17 @@ namespace Apache.Ignite.Core.Client } /// + /// Initializes a new instance of the class. + /// + /// The host to connect to. + public IgniteClientConfiguration(string host) : this() + { + IgniteArgumentCheck.NotNull(host, "host"); + + Endpoints = new List {host}; + } + + /// /// Initializes a new instance of the class. /// /// The configuration to copy. @@ -76,8 +92,10 @@ namespace Apache.Ignite.Core.Client return; } +#pragma warning disable 618 Host = cfg.Host; Port = cfg.Port; +#pragma warning restore 618 SocketSendBufferSize = cfg.SocketSendBufferSize; SocketReceiveBufferSize = cfg.SocketReceiveBufferSize; TcpNoDelay = cfg.TcpNoDelay; @@ -93,20 +111,46 @@ namespace Apache.Ignite.Core.Client UserName = cfg.UserName; Password = cfg.Password; + Endpoints = cfg.Endpoints == null ? null : cfg.Endpoints.ToList(); + ReconnectDisabled = cfg.ReconnectDisabled; } /// /// Gets or sets the host. Should not be null. /// + [Obsolete("Use Endpoints instead")] public string Host { get; set; } /// /// Gets or sets the port. /// [DefaultValue(DefaultPort)] + [Obsolete("Use Endpoints instead")] public int Port { get; set; } /// + /// Gets or sets endpoints to connect to. + /// Examples of supported formats: + /// * 192.168.1.25 (default port is used, see ). + /// * 192.168.1.25:780 (custom port) + /// * 192.168.1.25:780-787 (custom port range) + /// * my-host.com (default port is used, see ). + /// * my-host.com:780 (custom port) + /// * my-host.com:780-787 (custom port range) + /// + /// When multiple endpoints are specified, failover and load-balancing mechanism is enabled: + /// * Ignite picks random endpoint and connects to it. + /// * On disconnect, next endpoint is picked from the list (. + /// + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public ICollection Endpoints { get; set; } + + /// + /// Gets or sets a value indicating whether automatic reconnect is disabled. + /// + public bool ReconnectDisabled { get; set; } + + /// /// Gets or sets the size of the socket send buffer. When set to 0, operating system default is used. /// [DefaultValue(DefaultSocketBufferSize)] http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd index b9a04b8..d7ebc12 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd @@ -30,7 +30,7 @@ - + Binary configuration. @@ -172,6 +172,16 @@ + + + Endpoints to connect to. + + + + + + + @@ -237,6 +247,11 @@ Socket operation timeout. Zero or negative for infinite timeout. + + + Disables automatic reconnect on network or server failure. + + Username to be used to connect to secured cluster. @@ -249,4 +264,4 @@ - \ No newline at end of file + http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs index 55d358a..bd53118 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs @@ -1568,6 +1568,7 @@ namespace Apache.Ignite.Core /// /// By default schema names are case-insensitive. Use quotes to enforce case sensitivity. /// + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public ICollection SqlSchemas { get; set; } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs index fe80ba1..25ce4f5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs @@ -774,7 +774,6 @@ namespace Apache.Ignite.Core public static IIgniteClient StartClient(IgniteClientConfiguration clientConfiguration) { IgniteArgumentCheck.NotNull(clientConfiguration, "clientConfiguration"); - IgniteArgumentCheck.NotNull(clientConfiguration.Host, "clientConfiguration.Host"); return new IgniteClient(clientConfiguration); } http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs index febecd4..d68f66e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessorClient.cs @@ -32,7 +32,7 @@ namespace Apache.Ignite.Core.Impl.Binary private const byte DotNetPlatformId = 1; /** Socket. */ - private readonly ClientSocket _socket; + private readonly IClientSocket _socket; /** Marshaller. */ private readonly Marshaller _marsh = BinaryUtils.Marshaller; @@ -41,7 +41,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// Initializes a new instance of the class. /// /// The socket. - public BinaryProcessorClient(ClientSocket socket) + public BinaryProcessorClient(IClientSocket socket) { Debug.Assert(socket != null); http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs index a5a9246..8cc2741 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Cache/CacheClient.cs @@ -231,7 +231,7 @@ namespace Apache.Ignite.Core.Impl.Client.Cache /** */ public IQueryCursor Query(SqlFieldsQuery sqlFieldsQuery, Func readerFunc) { - return DoOutInOp(ClientOp.QuerySqlFields, + return DoOutInOp(ClientOp.QuerySqlFields, w => WriteSqlFieldsQuery(w, sqlFieldsQuery, false), s => GetFieldsCursorNoColumnNames(s, readerFunc)); } @@ -277,7 +277,7 @@ namespace Apache.Ignite.Core.Impl.Client.Cache { IgniteArgumentCheck.NotNull(key, "key"); - return DoOutInOp(ClientOp.CacheGetAndRemove, w => w.WriteObjectDetached(key), + return DoOutInOp(ClientOp.CacheGetAndRemove, w => w.WriteObjectDetached(key), UnmarshalCacheResult); } @@ -514,7 +514,7 @@ namespace Apache.Ignite.Core.Impl.Client.Cache public CacheClientConfiguration GetConfiguration() { return DoOutInOp(ClientOp.CacheGetConfiguration, null, - s => new CacheClientConfiguration(s, _ignite.ServerVersion())); + s => new CacheClientConfiguration(s, _ignite.ServerVersion)); } /** */ @@ -574,7 +574,7 @@ namespace Apache.Ignite.Core.Impl.Client.Cache private T DoOutInOp(ClientOp opId, Action writeAction, Func readFunc) { - return _ignite.Socket.DoOutInOp(opId, stream => WriteRequest(writeAction, stream), + return _ignite.Socket.DoOutInOp(opId, stream => WriteRequest(writeAction, stream), readFunc, HandleError); } @@ -584,7 +584,7 @@ namespace Apache.Ignite.Core.Impl.Client.Cache private Task DoOutInOpAsync(ClientOp opId, Action writeAction, Func readFunc) { - return _ignite.Socket.DoOutInOpAsync(opId, stream => WriteRequest(writeAction, stream), + return _ignite.Socket.DoOutInOpAsync(opId, stream => WriteRequest(writeAction, stream), readFunc, HandleError); } http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientFailoverSocket.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientFailoverSocket.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientFailoverSocket.cs new file mode 100644 index 0000000..8a08368 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientFailoverSocket.cs @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Client +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Net; + using System.Net.Sockets; + using System.Threading; + using System.Threading.Tasks; + using Apache.Ignite.Core.Client; + using Apache.Ignite.Core.Impl.Binary.IO; + + /// + /// Socket wrapper with reconnect/failover functionality: reconnects on failure. + /// + internal class ClientFailoverSocket : IClientSocket + { + /** Underlying socket. */ + private ClientSocket _socket; + + /** Current global endpoint index for Round-robin. */ + private static long _endPointIndex; + + /** Config. */ + private readonly IgniteClientConfiguration _config; + + /** Endpoints with corresponding hosts. */ + private readonly List> _endPoints; + + /** Locker. */ + private readonly object _syncRoot = new object(); + + /** Disposed flag. */ + private bool _disposed; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + public ClientFailoverSocket(IgniteClientConfiguration config) + { + Debug.Assert(config != null); + + _config = config; + +#pragma warning disable 618 // Type or member is obsolete + if (config.Host == null && (config.Endpoints == null || config.Endpoints.Count == 0)) + { + throw new IgniteClientException("Invalid IgniteClientConfiguration: Host is null, " + + "Endpoints is null or empty. Nowhere to connect."); + } +#pragma warning restore 618 + + _endPoints = GetIpEndPoints(config).ToList(); + + if (_endPoints.Count == 0) + { + throw new IgniteClientException("Failed to resolve all specified hosts."); + } + + Connect(); + } + + /** */ + public T DoOutInOp(ClientOp opId, Action writeAction, Func readFunc, + Func errorFunc = null) + { + return GetSocket().DoOutInOp(opId, writeAction, readFunc, errorFunc); + } + + /** */ + public Task DoOutInOpAsync(ClientOp opId, Action writeAction, Func readFunc, Func errorFunc = null) + { + return GetSocket().DoOutInOpAsync(opId, writeAction, readFunc, errorFunc); + } + + /** */ + public ClientProtocolVersion ServerVersion + { + get { return GetSocket().ServerVersion; } + } + + /** */ + public EndPoint RemoteEndPoint + { + get + { + lock (_syncRoot) + { + return _socket != null ? _socket.RemoteEndPoint : null; + } + } + } + + /** */ + public EndPoint LocalEndPoint + { + get + { + lock (_syncRoot) + { + return _socket != null ? _socket.LocalEndPoint : null; + } + } + } + + /// + /// Checks the disposed state. + /// + private ClientSocket GetSocket() + { + lock (_syncRoot) + { + if (_disposed) + { + throw new ObjectDisposedException("ClientFailoverSocket"); + } + + if (_socket == null) + { + Connect(); + } + + return _socket; + } + } + + /** */ + [SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly", + Justification = "There is no finalizer.")] + public void Dispose() + { + lock (_syncRoot) + { + _disposed = true; + + if (_socket != null) + { + _socket.Dispose(); + _socket = null; + } + } + } + + /// + /// Connects the socket. + /// + private void Connect() + { + List errors = null; + var startIdx = (int) Interlocked.Increment(ref _endPointIndex); + + for (var i = 0; i < _endPoints.Count; i++) + { + var idx = (startIdx + i) % _endPoints.Count; + var endPoint = _endPoints[idx]; + + try + { + _socket = new ClientSocket(_config, endPoint.Key, endPoint.Value, OnSocketError); + return; + } + catch (SocketException e) + { + if (errors == null) + { + errors = new List(); + } + + errors.Add(e); + } + } + + throw new AggregateException("Failed to establish Ignite thin client connection, " + + "examine inner exceptions for details.", errors); + } + + /// + /// Called when socket error occurs. + /// + private void OnSocketError() + { + if (_config.ReconnectDisabled) + { + return; + } + + // Reconnect on next operation. + lock (_syncRoot) + { + _socket = null; + } + } + + /// + /// Gets the endpoints: all combinations of IP addresses and ports according to configuration. + /// + private static IEnumerable> GetIpEndPoints(IgniteClientConfiguration cfg) + { + foreach (var e in Endpoint.GetEndpoints(cfg)) + { + var host = e.Host; + Debug.Assert(host != null); // Checked by GetEndpoints. + + // GetHostEntry accepts IPs, but TryParse is a more efficient shortcut. + IPAddress ip; + + if (IPAddress.TryParse(host, out ip)) + { + for (var i = 0; i <= e.PortRange; i++) + { + yield return new KeyValuePair(new IPEndPoint(ip, e.Port + i), host); + } + } + else + { + for (var i = 0; i <= e.PortRange; i++) + { + foreach (var x in Dns.GetHostEntry(host).AddressList) + { + yield return new KeyValuePair(new IPEndPoint(x, e.Port + i), host); + } + } + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs index 8a8b53b..b9eee99 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs @@ -19,7 +19,6 @@ namespace Apache.Ignite.Core.Impl.Client { using System; using System.Collections.Concurrent; - using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -29,7 +28,6 @@ namespace Apache.Ignite.Core.Impl.Client using System.Threading; using System.Threading.Tasks; using Apache.Ignite.Core.Client; - using Apache.Ignite.Core.Common; using Apache.Ignite.Core.Impl.Binary; using Apache.Ignite.Core.Impl.Binary.IO; using Apache.Ignite.Core.Impl.Common; @@ -37,7 +35,7 @@ namespace Apache.Ignite.Core.Impl.Client /// /// Wrapper over framework socket for Ignite thin client operations. /// - internal sealed class ClientSocket : IDisposable + internal sealed class ClientSocket : IClientSocket { /** Version 1.0.0. */ private static readonly ClientProtocolVersion Ver100 = new ClientProtocolVersion(1, 0, 0); @@ -97,19 +95,27 @@ namespace Apache.Ignite.Core.Impl.Client /** Disposed flag. */ private bool _isDisposed; + /** Error callback. */ + private readonly Action _onError; + /// /// Initializes a new instance of the class. /// /// The client configuration. + /// The end point to connect to. + /// The host name (required for SSL). + /// Error callback. /// Protocol version. - public ClientSocket(IgniteClientConfiguration clientConfiguration, ClientProtocolVersion? version = null) + public ClientSocket(IgniteClientConfiguration clientConfiguration, EndPoint endPoint, string host, + Action onError = null, ClientProtocolVersion? version = null) { Debug.Assert(clientConfiguration != null); + _onError = onError; _timeout = clientConfiguration.SocketTimeout; - _socket = Connect(clientConfiguration); - _stream = GetSocketStream(_socket, clientConfiguration); + _socket = Connect(clientConfiguration, endPoint); + _stream = GetSocketStream(_socket, clientConfiguration, host); ServerVersion = version ?? CurrentProtocolVersion; @@ -161,7 +167,7 @@ namespace Apache.Ignite.Core.Impl.Client { // Encode. var reqMsg = WriteMessage(writeAction, opId); - + // Send. var response = SendRequest(ref reqMsg); @@ -186,6 +192,16 @@ namespace Apache.Ignite.Core.Impl.Client } /// + /// Gets the current remote EndPoint. + /// + public EndPoint RemoteEndPoint { get { return _socket.RemoteEndPoint; } } + + /// + /// Gets the current local EndPoint. + /// + public EndPoint LocalEndPoint { get { return _socket.LocalEndPoint; } } + + /// /// Starts waiting for the new message. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] @@ -245,7 +261,7 @@ namespace Apache.Ignite.Core.Impl.Client /// /// Decodes the response that we got from . /// - private static T DecodeResponse(BinaryHeapStream stream, Func readFunc, + private static T DecodeResponse(BinaryHeapStream stream, Func readFunc, Func errorFunc) { var statusCode = (ClientStatusCode)stream.ReadInt(); @@ -298,8 +314,8 @@ namespace Apache.Ignite.Core.Impl.Client BinaryUtils.Marshaller.FinishMarshal(writer); } }, 12, out messageLen); - - _stream.Write(buf, 0, messageLen); + + SocketWrite(buf, messageLen); // Decode response. var res = ReceiveMessage(); @@ -370,16 +386,20 @@ namespace Apache.Ignite.Core.Impl.Client // Socket.Receive can return any number of bytes, even 1. // We should repeat Receive calls until required amount of data has been received. var buf = new byte[size]; - var received = _stream.Read(buf,0, size); + var received = SocketRead(buf, 0, size); while (received < size) { - var res = _stream.Read(buf, received, size - received); + var res = SocketRead(buf, received, size - received); if (res == 0) { // Disconnected. _exception = _exception ?? new SocketException((int) SocketError.ConnectionAborted); + if (_onError != null) + { + _onError(); + } Dispose(); CheckException(); } @@ -410,7 +430,7 @@ namespace Apache.Ignite.Core.Impl.Client if (_requests.IsEmpty) { - _stream.Write(reqMsg.Buffer, 0, reqMsg.Length); + SocketWrite(reqMsg.Buffer, reqMsg.Length); var respMsg = ReceiveMessage(); var response = new BinaryHeapStream(respMsg); @@ -451,7 +471,7 @@ namespace Apache.Ignite.Core.Impl.Client Debug.Assert(added); // Send. - _stream.Write(reqMsg.Buffer, 0, reqMsg.Length); + SocketWrite(reqMsg.Buffer, reqMsg.Length); _listenerEvent.Set(); return req.CompletionSource.Task; } @@ -490,100 +510,90 @@ namespace Apache.Ignite.Core.Impl.Client } /// - /// Connects the socket. + /// Writes to the socket. All socket writes should go through this method. /// - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", - Justification = "Socket is returned from this method.")] - private static Socket Connect(IgniteClientConfiguration cfg) + private void SocketWrite(byte[] buf, int len) { - List errors = null; - - foreach (var ipEndPoint in GetEndPoints(cfg)) + try { - try - { - var socket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) - { - NoDelay = cfg.TcpNoDelay, - Blocking = true, - SendTimeout = (int) cfg.SocketTimeout.TotalMilliseconds, - ReceiveTimeout = (int) cfg.SocketTimeout.TotalMilliseconds - }; - - if (cfg.SocketSendBufferSize != IgniteClientConfiguration.DefaultSocketBufferSize) - { - socket.SendBufferSize = cfg.SocketSendBufferSize; - } - - if (cfg.SocketReceiveBufferSize != IgniteClientConfiguration.DefaultSocketBufferSize) - { - socket.ReceiveBufferSize = cfg.SocketReceiveBufferSize; - } - - socket.Connect(ipEndPoint); - - return socket; - } - catch (SocketException e) + _stream.Write(buf, 0, len); + } + catch (Exception) + { + if (_onError != null) { - if (errors == null) - { - errors = new List(); - } - - errors.Add(e); + _onError(); } + throw; } + } - if (errors == null) + /// + /// Reads from the socket. All socket reads should go through this method. + /// + private int SocketRead(byte[] buf, int pos, int len) + { + try { - throw new IgniteException("Failed to resolve client host: " + cfg.Host); + return _stream.Read(buf, pos, len); + } + catch (Exception) + { + if (_onError != null) + { + _onError(); + } + throw; } - - throw new AggregateException("Failed to establish Ignite thin client connection, " + - "examine inner exceptions for details.", errors); } /// - /// Gets the socket stream. + /// Connects the socket. /// - private static Stream GetSocketStream(Socket socket, IgniteClientConfiguration cfg) + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", + Justification = "Socket is returned from this method.")] + private static Socket Connect(IgniteClientConfiguration cfg, EndPoint endPoint) { - var stream = new NetworkStream(socket) + var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) { - ReadTimeout = (int) cfg.SocketTimeout.TotalMilliseconds, - WriteTimeout = (int) cfg.SocketTimeout.TotalMilliseconds + NoDelay = cfg.TcpNoDelay, + Blocking = true, + SendTimeout = (int) cfg.SocketTimeout.TotalMilliseconds, + ReceiveTimeout = (int) cfg.SocketTimeout.TotalMilliseconds }; - if (cfg.SslStreamFactory == null) + if (cfg.SocketSendBufferSize != IgniteClientConfiguration.DefaultSocketBufferSize) { - return stream; + socket.SendBufferSize = cfg.SocketSendBufferSize; + } + + if (cfg.SocketReceiveBufferSize != IgniteClientConfiguration.DefaultSocketBufferSize) + { + socket.ReceiveBufferSize = cfg.SocketReceiveBufferSize; } - return cfg.SslStreamFactory.Create(stream, cfg.Host); + socket.Connect(endPoint); + + return socket; } /// - /// Gets the endpoints: all combinations of IP addresses and ports according to configuration. + /// Gets the socket stream. /// - private static IEnumerable GetEndPoints(IgniteClientConfiguration cfg) + private static Stream GetSocketStream(Socket socket, IgniteClientConfiguration cfg, string host) { - var host = cfg.Host; - - if (host == null) + var stream = new NetworkStream(socket) { - throw new IgniteException("IgniteClientConfiguration.Host cannot be null."); - } - - // GetHostEntry accepts IPs, but TryParse is a more efficient shortcut. - IPAddress ip; + ReadTimeout = (int) cfg.SocketTimeout.TotalMilliseconds, + WriteTimeout = (int) cfg.SocketTimeout.TotalMilliseconds + }; - if (IPAddress.TryParse(host, out ip)) + if (cfg.SslStreamFactory == null) { - return new[] {new IPEndPoint(ip, cfg.Port)}; + return stream; } - return Dns.GetHostEntry(host).AddressList.Select(x => new IPEndPoint(x, cfg.Port)); + return cfg.SslStreamFactory.Create(stream, host); } /// http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Endpoint.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Endpoint.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Endpoint.cs new file mode 100644 index 0000000..a42d6cd --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/Endpoint.cs @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Client +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using Apache.Ignite.Core.Client; + using Apache.Ignite.Core.Impl.Common; + + /// + /// Internal representation of client endpoint. + /// + internal class Endpoint + { + /** */ + private static readonly string[] HostSeparators = {":"}; + + /** */ + private static readonly string[] PortsSeparators = {".."}; + + /// + /// Initializes a new instance of the class. + /// + private Endpoint(string host, int port = IgniteClientConfiguration.DefaultPort, int portRange = 0) + { + Host = IgniteArgumentCheck.NotNullOrEmpty(host, "host"); + Port = port; + PortRange = portRange; + } + + /// + /// Gets or sets the host. + /// + public string Host { get; private set; } + + /// + /// Gets or sets the port. + /// + [DefaultValue(IgniteClientConfiguration.DefaultPort)] + public int Port { get; private set; } + + /// + /// Size of the port range. Default is 0, meaning only one port is used, defined by . + /// + public int PortRange { get; private set; } + + /// + /// Gets the client endpoints from given configuration. + /// + public static IEnumerable GetEndpoints(IgniteClientConfiguration cfg) + { +#pragma warning disable 618 // Type or member is obsolete + if (cfg.Host != null) + { + yield return new Endpoint(cfg.Host, cfg.Port); + } +#pragma warning restore 618 + + if (cfg.Endpoints != null) + { + foreach (var endpoint in cfg.Endpoints) + { + yield return ParseEndpoint(endpoint); + } + } + } + + /// + /// Parses the endpoint string. + /// + private static Endpoint ParseEndpoint(string endpoint) + { + if (string.IsNullOrWhiteSpace(endpoint)) + { + throw new IgniteClientException( + "IgniteClientConfiguration.Endpoints[...] can't be null or whitespace."); + } + + var parts = endpoint.Split(HostSeparators, StringSplitOptions.None); + + if (parts.Length == 1) + { + return new Endpoint(endpoint); + } + + if (parts.Length == 2) + { + var host = parts[0]; + var port = parts[1]; + + var ports = port.Split(PortsSeparators, StringSplitOptions.None); + + if (ports.Length == 1) + { + return new Endpoint(host, ParsePort(endpoint, port)); + } + + if (ports.Length == 2) + { + var minPort = ParsePort(endpoint, ports[0]); + var maxPort = ParsePort(endpoint, ports[1]); + + if (maxPort < minPort) + { + throw new IgniteClientException( + "Invalid format of IgniteClientConfiguration.Endpoint, port range is empty: " + endpoint); + } + + return new Endpoint(host, minPort, maxPort - minPort); + } + } + + throw new IgniteClientException("Unrecognized format of IgniteClientConfiguration.Endpoint: " + endpoint); + } + + /// + /// Parses the port string. + /// + private static int ParsePort(string endpoint, string portString) + { + int port; + + if (int.TryParse(portString, out port)) + { + return port; + } + + throw new IgniteClientException( + "Unrecognized format of IgniteClientConfiguration.Endpoint, failed to parse port: " + endpoint); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/45abb9c7/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/IClientSocket.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/IClientSocket.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/IClientSocket.cs new file mode 100644 index 0000000..c81f45f --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/IClientSocket.cs @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Client +{ + using System; + using System.Net; + using System.Threading.Tasks; + using Apache.Ignite.Core.Client; + using Apache.Ignite.Core.Impl.Binary.IO; + + /// + /// Wrapper over framework socket for Ignite thin client operations. + /// + internal interface IClientSocket : IDisposable + { + /// + /// Performs a send-receive operation. + /// + T DoOutInOp(ClientOp opId, Action writeAction, + Func readFunc, Func errorFunc = null); + + /// + /// Performs a send-receive operation asynchronously. + /// + Task DoOutInOpAsync(ClientOp opId, Action writeAction, + Func readFunc, Func errorFunc = null); + + /// + /// Gets the server version. + /// + ClientProtocolVersion ServerVersion { get; } + + /// + /// Gets the current remote EndPoint. + /// + EndPoint RemoteEndPoint { get; } + + /// + /// Gets the current local EndPoint. + /// + EndPoint LocalEndPoint { get; } + } +}