blob: 1a4c30aba3bf388b50575ca3230e53069d3081b1 [file] [log] [blame]
/*
* 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));
}
}
}
}