/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* Licensed 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.
*/
package com.hazelcast.client.cluster;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.client.spi.properties.ClientProperty;
import com.hazelcast.client.test.TestHazelcastFactory;
import com.hazelcast.cluster.ClusterState;
import com.hazelcast.config.Config;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import com.hazelcast.core.OperationTimeoutException;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;
import com.hazelcast.spi.exception.TargetDisconnectedException;
import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.annotation.ParallelTest;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static com.hazelcast.internal.cluster.impl.AdvancedClusterStateTest.changeClusterStateEventually;
import static com.hazelcast.test.HazelcastTestSupport.assertClusterSizeEventually;
import static com.hazelcast.test.HazelcastTestSupport.randomMapName;
import static com.hazelcast.test.HazelcastTestSupport.waitAllForSafeState;
import static com.hazelcast.test.HazelcastTestSupport.warmUpPartitions;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(HazelcastParallelClassRunner.class)
@Category({QuickTest.class, ParallelTest.class})
public class ClientClusterStateTest {
private TestHazelcastFactory factory;
private HazelcastInstance[] instances;
private HazelcastInstance instance;
@Before
public void before() {
factory = new TestHazelcastFactory();
instances = factory.newInstances(new Config(), 4);
for (HazelcastInstance instance : instances) {
assertClusterSizeEventually(4, instance);
}
instance = instances[instances.length - 1];
}
@After
public void after() {
factory.terminateAll();
}
@Test
public void testClient_canConnect_whenClusterState_frozen() {
instance.getCluster().changeClusterState(ClusterState.FROZEN);
factory.newHazelcastClient();
}
@Test
public void testClient_canExecuteWriteOperations_whenClusterState_frozen() {
warmUpPartitions(instances);
changeClusterStateEventually(instance, ClusterState.FROZEN);
HazelcastInstance client = factory.newHazelcastClient();
IMap<Object, Object> map = client.getMap(randomMapName());
map.put(1, 1);
}
@Test
public void testClient_canExecuteReadOperations_whenClusterState_frozen() {
warmUpPartitions(instances);
changeClusterStateEventually(instance, ClusterState.FROZEN);
HazelcastInstance client = factory.newHazelcastClient();
IMap<Object, Object> map = client.getMap(randomMapName());
map.get(1);
}
@Test
public void testClient_canConnect_whenClusterState_passive() {
instance.getCluster().changeClusterState(ClusterState.PASSIVE);
factory.newHazelcastClient();
}
@Test(expected = OperationTimeoutException.class)
public void testClient_canNotExecuteWriteOperations_whenClusterState_passive() {
warmUpPartitions(instances);
ClientConfig clientConfig = new ClientConfig().setProperty(ClientProperty.INVOCATION_TIMEOUT_SECONDS.getName(), "3");
HazelcastInstance client = factory.newHazelcastClient(clientConfig);
IMap<Object, Object> map = client.getMap(randomMapName());
changeClusterStateEventually(instance, ClusterState.PASSIVE);
map.put(1, 1);
}
@Test
public void testClient_canExecuteReadOperations_whenClusterState_passive() {
warmUpPartitions(instances);
HazelcastInstance client = factory.newHazelcastClient();
IMap<Object, Object> map = client.getMap(randomMapName());
changeClusterStateEventually(instance, ClusterState.PASSIVE);
map.get(1);
}
@Test
public void testClient_canConnect_whenClusterState_goesBackToActive_fromPassive() {
instance.getCluster().changeClusterState(ClusterState.PASSIVE);
instance.getCluster().changeClusterState(ClusterState.ACTIVE);
factory.newHazelcastClient();
}
@Test
public void testClient_canExecuteOperations_whenClusterState_goesBackToActive_fromPassive() {
warmUpPartitions(instances);
changeClusterStateEventually(instance, ClusterState.PASSIVE);
instance.getCluster().changeClusterState(ClusterState.ACTIVE);
HazelcastInstance client = factory.newHazelcastClient();
IMap<Object, Object> map = client.getMap(randomMapName());
map.put(1, 1);
}
@Test
public void testClusterShutdownDuringMapPutAll() {
warmUpPartitions(instances);
waitAllForSafeState(instances);
HazelcastInstance client = factory.newHazelcastClient();
final IMap<Object, Object> map = client.getMap(randomMapName());
final HashMap values = new HashMap<Double, Double>();
for (int i = 0; i < 1000; i++) {
double value = Math.random();
values.put(value, value);
}
final int numThreads = 10;
final CountDownLatch threadsFinished = new CountDownLatch(numThreads);
final CountDownLatch threadsStarted = new CountDownLatch(numThreads);
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < numThreads; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
ILogger logger = Logger.getLogger(getClass());
threadsStarted.countDown();
logger.info("putAll thread started");
while (true) {
try {
map.putAll(values);
Thread.sleep(100);
} catch (IllegalStateException e) {
logger.warning("Expected exception for Map putAll during cluster shutdown:", e);
break;
} catch (TargetDisconnectedException e) {
logger.warning("Expected exception for Map putAll during cluster shutdown:", e);
break;
} catch (InterruptedException e) {
// do nothing
}
}
threadsFinished.countDown();
logger.info("putAll thread finishing. Current finished thread count is:" + (numThreads - threadsFinished
.getCount()));
}
});
}
try {
assertTrue("All threads could not be started", threadsStarted.await(1, TimeUnit.MINUTES));
} catch (InterruptedException e) {
fail("All threads could not be started due to InterruptedException. Could not start " + threadsStarted.getCount()
+ " threads out of " + numThreads);
}
instance.getCluster().shutdown();
executor.shutdown();
try {
assertTrue("All threads could not be finished", threadsFinished.await(2, TimeUnit.MINUTES));
} catch (InterruptedException e) {
fail("All threads could not be finished due to InterruptedException. Could not finish " + threadsFinished.getCount()
+ " threads out of " + numThreads);
} finally {
executor.shutdownNow();
}
}
}