| /* |
| * 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.Tests |
| { |
| using System; |
| using System.Collections.Generic; |
| using System.Linq; |
| using System.Threading; |
| using System.Threading.Tasks; |
| using Apache.Ignite.Core.Cache; |
| using Apache.Ignite.Core.Cache.Configuration; |
| using Apache.Ignite.Core.Cluster; |
| using Apache.Ignite.Core.Common; |
| using Apache.Ignite.Core.Lifecycle; |
| using Apache.Ignite.Core.Tests.Client.Cache; |
| using NUnit.Framework; |
| |
| /// <summary> |
| /// Client reconnect tests. |
| /// </summary> |
| [Category(TestUtils.CategoryIntensive)] |
| public class ReconnectTest |
| { |
| /** */ |
| private const string CacheName = "cache"; |
| |
| /// <summary> |
| /// Tests the cluster restart scenario, where client is alive, but all servers restart. |
| /// </summary> |
| [Test] |
| public void TestClusterRestart() |
| { |
| var serverCfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) |
| { |
| CacheConfiguration = new[] {new CacheConfiguration(CacheName)} |
| }; |
| |
| var clientCfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) |
| { |
| IgniteInstanceName = "client", |
| ClientMode = true |
| }; |
| |
| var server = Ignition.Start(serverCfg); |
| |
| Assert.AreEqual(1, server.GetCluster().GetNodes().Count); |
| |
| var client = Ignition.Start(clientCfg); |
| |
| Assert.AreEqual(2, client.GetCluster().GetNodes().Count); |
| |
| ClientReconnectEventArgs eventArgs = null; |
| |
| client.ClientReconnected += (sender, args) => { eventArgs = args; }; |
| |
| var cache = client.GetCache<int, Person>(CacheName); |
| |
| cache[1] = new Person(1); |
| |
| Ignition.Stop(server.Name, true); |
| |
| var cacheEx = Assert.Throws<CacheException>(() => cache.Get(1)); |
| var ex = cacheEx.InnerException as ClientDisconnectedException; |
| |
| Assert.IsNotNull(ex); |
| |
| // Wait a bit for cluster restart detection. |
| Thread.Sleep(1000); |
| |
| // Start the server and wait for reconnect. |
| Ignition.Start(serverCfg); |
| |
| // Check reconnect task. |
| Assert.IsTrue(ex.ClientReconnectTask.Result); |
| |
| // Wait a bit for notifications. |
| Thread.Sleep(100); |
| |
| // Check the event args. |
| Assert.IsNotNull(eventArgs); |
| Assert.IsTrue(eventArgs.HasClusterRestarted); |
| |
| // Refresh the cache instance and check that it works. |
| var cache1 = client.GetCache<int, Person>(CacheName); |
| Assert.AreEqual(0, cache1.GetSize()); |
| |
| cache1[1] = new Person(2); |
| Assert.AreEqual(2, cache1[1].Id); |
| |
| // Check that old cache instance still works. |
| Assert.AreEqual(2, cache.Get(1).Id); |
| } |
| |
| #if !NETCOREAPP |
| /// <summary> |
| /// Tests the failed connection scenario, where servers are alive, but can't be contacted. |
| /// </summary> |
| [Test] |
| public void TestFailedConnection() |
| { |
| var cfg = new IgniteConfiguration |
| { |
| SpringConfigUrl = "config\\reconnect-test.xml", |
| JvmClasspath = TestUtils.CreateTestClasspath(), |
| JvmOptions = TestUtils.TestJavaOptions() |
| }; |
| |
| var proc = new Process.IgniteProcess( |
| "-springConfigUrl=" + cfg.SpringConfigUrl, "-J-ea", "-J-Xcheck:jni", "-J-Xms512m", "-J-Xmx512m", |
| "-J-DIGNITE_QUIET=false"); |
| |
| Ignition.ClientMode = true; |
| |
| using (var ignite = Ignition.Start(cfg)) |
| { |
| var localNode = ignite.GetCluster().GetLocalNode(); |
| var remoteNode = ignite.GetCluster().ForRemotes().GetNode(); |
| |
| var reconnected = 0; |
| var disconnected = 0; |
| ignite.ClientDisconnected += (sender, args) => { disconnected++; }; |
| ignite.ClientReconnected += (sender, args) => { reconnected += args.HasClusterRestarted ? 10 : 1; }; |
| |
| Assert.IsTrue(ignite.GetCluster().ClientReconnectTask.IsCompleted); |
| |
| var cache = ignite.CreateCache<int, int>(CacheName); |
| |
| cache[1] = 1; |
| |
| // Suspend external process to cause disconnect |
| proc.Suspend(); |
| |
| var ex = Assert.Throws<CacheException>(() => cache.Get(1)); |
| |
| Assert.IsTrue(ex.ToString().Contains( |
| "javax.cache.CacheException: class org.apache.ignite.IgniteClientDisconnectedException: " + |
| "Operation has been cancelled (client node disconnected)")); |
| |
| var inner = (ClientDisconnectedException) ex.InnerException; |
| |
| Assert.IsNotNull(inner); |
| |
| var clientReconnectTask = inner.ClientReconnectTask; |
| |
| Assert.AreEqual(ignite.GetCluster().ClientReconnectTask, clientReconnectTask); |
| Assert.AreEqual(1, disconnected); |
| Assert.AreEqual(0, reconnected); |
| |
| // Resume process to reconnect |
| proc.Resume(); |
| |
| Assert.IsFalse(clientReconnectTask.Result); |
| |
| Assert.AreEqual(1, cache[1]); |
| Assert.AreEqual(1, disconnected); |
| |
| Thread.Sleep(100); // Wait for event handler |
| Assert.AreEqual(1, reconnected); |
| |
| var localNodeNew = ignite.GetCluster().GetLocalNode(); |
| Assert.AreNotSame(localNode, localNodeNew); |
| Assert.AreNotEqual(localNode.Id, localNodeNew.Id); |
| |
| var remoteNodeNew = ignite.GetCluster().ForRemotes().GetNode(); |
| Assert.AreEqual(remoteNode.Id, remoteNodeNew.Id); |
| } |
| } |
| #endif |
| |
| /// <summary> |
| /// Tests writer structure cleanup after client reconnect with full cluster restart. |
| /// </summary> |
| [Test] |
| public void TestClusterRestart_ResetsCachedMetadataAndWriterStructures() |
| { |
| var serverCfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) |
| { |
| CacheConfiguration = new[] {new CacheConfiguration(CacheName)} |
| }; |
| |
| var clientCfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) |
| { |
| IgniteInstanceName = "client", |
| ClientMode = true |
| }; |
| |
| var server = Ignition.Start(serverCfg); |
| var client = Ignition.Start(clientCfg); |
| |
| Assert.AreEqual(2, client.GetCluster().GetNodes().Count); |
| var localNode = client.GetCluster().GetLocalNode(); |
| var nodes = client.GetCluster().GetNodes(); |
| |
| var evt = new ManualResetEventSlim(false); |
| client.ClientReconnected += (sender, args) => evt.Set(); |
| |
| var cache = client.GetCache<int, Person>(CacheName); |
| cache[1] = new Person(1); |
| |
| Task.Factory.StartNew(() => |
| { |
| while (!evt.IsSet) |
| { |
| try |
| { |
| cache[1] = new Person(1); |
| } |
| catch (Exception) |
| { |
| // Ignore exceptions while disconnected, keep on trying to populate writer structure cache. |
| } |
| } |
| }); |
| |
| Ignition.Stop(server.Name, true); |
| var server2 = Ignition.Start(serverCfg); |
| evt.Wait(); |
| |
| // Verify that we can deserialize on server (meta is resent properly). |
| cache[2] = new Person(2); |
| |
| var serverCache = server2.GetCache<int, Person>(CacheName); |
| Assert.AreEqual(2, serverCache[2].Id); |
| |
| // Verify that cached node info is updated on the client. |
| CheckUpdatedNodes(client, localNode, nodes); |
| } |
| |
| /// <summary> |
| /// Test tear down. |
| /// </summary> |
| [TearDown] |
| public void TearDown() |
| { |
| Ignition.StopAll(true); |
| |
| #if !NETCOREAPP |
| Process.IgniteProcess.KillAll(); |
| #endif |
| |
| Ignition.ClientMode = false; |
| } |
| |
| /// <summary> |
| /// Checks that node info is up to date. |
| /// </summary> |
| // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local |
| private static void CheckUpdatedNodes(IIgnite client, IClusterNode localNode, ICollection<IClusterNode> nodes) |
| { |
| var localNodeNew = client.GetCluster().GetLocalNode(); |
| Assert.AreNotSame(localNode, localNodeNew); |
| Assert.AreNotEqual(localNode.Id, localNodeNew.Id); |
| |
| var nodesNew = client.GetCluster().GetNodes(); |
| Assert.AreEqual(2, nodesNew.Count); |
| |
| foreach (var node in nodesNew) |
| { |
| Assert.IsFalse(nodes.Any(n => n.Id == node.Id)); |
| } |
| } |
| } |
| } |