/* * 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.stress; import com.hazelcast.config.Config; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.test.HazelcastTestSupport; import org.junit.After; import org.junit.Before; import java.util.List; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import static com.hazelcast.core.Hazelcast.newHazelcastInstance; import static org.junit.Assert.assertNull; public abstract class StressTestSupport extends HazelcastTestSupport { //todo: should be system property public static final int RUNNING_TIME_SECONDS = 180; //todo: should be system property public static final int CLUSTER_SIZE = 6; //todo: should be system property public static final int KILL_DELAY_SECONDS = 10; private final List<HazelcastInstance> instances = new CopyOnWriteArrayList<HazelcastInstance>(); private CountDownLatch startLatch; private KillMemberThread killMemberThread; private volatile boolean stopOnError = true; private volatile boolean stopTest = false; private boolean clusterChangeEnabled = true; @Before public void setUp() { startLatch = new CountDownLatch(1); for (int k = 0; k < CLUSTER_SIZE; k++) { HazelcastInstance hz = newHazelcastInstance(createClusterConfig()); instances.add(hz); } } public void setClusterChangeEnabled(boolean membershutdownEnabled) { this.clusterChangeEnabled = membershutdownEnabled; } public Config createClusterConfig() { return new Config(); } @After public void tearDown() { stopTest = true; if (killMemberThread != null) { try { killMemberThread.join(TimeUnit.SECONDS.toMillis(KILL_DELAY_SECONDS * 4)); } catch (InterruptedException e) { e.printStackTrace(); } } for (HazelcastInstance hz : instances) { try { hz.getLifecycleService().terminate(); } catch (Exception e) { e.printStackTrace(); } } } public final boolean startAndWaitForTestCompletion() { System.out.println("Cluster change enabled:" + clusterChangeEnabled); if (clusterChangeEnabled) { killMemberThread = new KillMemberThread(); killMemberThread.start(); } System.out.println("=================================================================="); System.out.println("Test started."); System.out.println("=================================================================="); startLatch.countDown(); for (int k = 1; k <= RUNNING_TIME_SECONDS; k++) { try { Thread.sleep(TimeUnit.SECONDS.toMillis(1)); } catch (InterruptedException e) { throw new RuntimeException(e); } float percent = (k * 100.0f) / RUNNING_TIME_SECONDS; System.out.printf("%.1f Running for %s of %s seconds\n", percent, k, RUNNING_TIME_SECONDS); if (stopTest) { System.err.println("=================================================================="); System.err.println("Test ended premature!"); System.err.println("=================================================================="); return false; } } System.out.println("=================================================================="); System.out.println("Test completed."); System.out.println("=================================================================="); stopTest(); return true; } protected final void setStopOnError(boolean stopOnError) { this.stopOnError = stopOnError; } protected final void stopTest() { stopTest = true; } protected final boolean isStopped() { return stopTest; } public final void assertNoErrors(TestThread... threads) { for (TestThread thread : threads) { thread.assertNoError(); } } public final void joinAll(TestThread... threads) { for (TestThread t : threads) { try { t.join(60000); } catch (InterruptedException e) { throw new RuntimeException("Interrupted while joining thread:" + t); } if (t.isAlive()) { System.err.println("Could not join Thread:" + t.getName() + ", it is still alive"); for (StackTraceElement e : t.getStackTrace()) { System.err.println("\tat " + e); } throw new RuntimeException("Could not join thread:" + t + ", thread is still alive"); } } assertNoErrors(threads); } public static final AtomicLong ID_GENERATOR = new AtomicLong(1); public abstract class TestThread extends Thread { private volatile Throwable error; protected final Random random = new Random(); public TestThread() { setName(getClass().getName() + "" + ID_GENERATOR.getAndIncrement()); } @Override public final void run() { try { startLatch.await(); doRun(); } catch (Throwable t) { if (stopOnError) { stopTest(); } t.printStackTrace(); this.error = t; } } public final void assertNoError() { assertNull(getName() + " encountered an error", error); } public abstract void doRun() throws Exception; } public class KillMemberThread extends TestThread { @Override public void doRun() throws Exception { while (!stopTest) { try { Thread.sleep(TimeUnit.SECONDS.toMillis(KILL_DELAY_SECONDS)); } catch (InterruptedException e) { } int index = random.nextInt(CLUSTER_SIZE); HazelcastInstance instance = instances.remove(index); instance.shutdown(); HazelcastInstance newInstance = newHazelcastInstance(createClusterConfig()); instances.add(newInstance); } } } }