/*
* 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.executor;
import com.hazelcast.config.Config;
import com.hazelcast.config.ExecutorConfig;
import com.hazelcast.core.ExecutionCallback;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.HazelcastInstanceAware;
import com.hazelcast.core.IAtomicLong;
import com.hazelcast.core.ICompletableFuture;
import com.hazelcast.core.IExecutorService;
import com.hazelcast.core.ManagedContext;
import com.hazelcast.core.Member;
import com.hazelcast.core.MemberSelector;
import com.hazelcast.core.MultiExecutionCallback;
import com.hazelcast.core.PartitionAware;
import com.hazelcast.monitor.LocalExecutorStats;
import com.hazelcast.spi.ExecutionService;
import com.hazelcast.spi.properties.GroupProperty;
import com.hazelcast.test.AssertTask;
import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.TestHazelcastInstanceFactory;
import com.hazelcast.test.annotation.ParallelTest;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static com.hazelcast.config.ExecutorConfig.DEFAULT_POOL_SIZE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(HazelcastParallelClassRunner.class)
@Category({QuickTest.class, ParallelTest.class})
public class ExecutorServiceTest extends ExecutorServiceTestSupport {
private static final int NODE_COUNT = 3;
private static final int TASK_COUNT = 1000;
/* ############ andThen(Callback) ############ */
@Test
public void testPreregisteredExecutionCallbackCompletableFuture() {
ExecutionService es = getExecutionService(createHazelcastInstance());
CountDownLatch callableLatch = new CountDownLatch(1);
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
Future<String> future = executorService.submit(new CountDownLatchAwaitingCallable(callableLatch));
CountingDownExecutionCallback<String> callback = new CountingDownExecutionCallback<String>(1);
ICompletableFuture<String> completableFuture = es.asCompletableFuture(future);
completableFuture.andThen(callback);
callableLatch.countDown();
assertOpenEventually(callback.getLatch());
assertEquals(CountDownLatchAwaitingCallable.RESULT, callback.getResult());
} finally {
executorService.shutdown();
}
}
@Test
public void testMultiPreregisteredExecutionCallbackCompletableFuture() {
ExecutionService es = getExecutionService(createHazelcastInstance());
CountDownLatch callableLatch = new CountDownLatch(1);
CountDownLatch callbackLatch = new CountDownLatch(2);
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
Future<String> future = executorService.submit(new CountDownLatchAwaitingCallable(callableLatch));
CountingDownExecutionCallback<String> callback1 = new CountingDownExecutionCallback<String>(callbackLatch);
CountingDownExecutionCallback<String> callback2 = new CountingDownExecutionCallback<String>(callbackLatch);
ICompletableFuture<String> completableFuture = es.asCompletableFuture(future);
completableFuture.andThen(callback1);
completableFuture.andThen(callback2);
callableLatch.countDown();
assertOpenEventually(callbackLatch);
assertEquals(CountDownLatchAwaitingCallable.RESULT, callback1.getResult());
assertEquals(CountDownLatchAwaitingCallable.RESULT, callback2.getResult());
} finally {
executorService.shutdown();
}
}
@Test
public void testPostRegisteredExecutionCallbackCompletableFuture() throws Exception {
ExecutionService es = getExecutionService(createHazelcastInstance());
CountDownLatch callableLatch = new CountDownLatch(1);
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
Future<String> future = executorService.submit(new CountDownLatchAwaitingCallable(callableLatch));
ICompletableFuture<String> completableFuture = es.asCompletableFuture(future);
callableLatch.countDown();
future.get();
CountingDownExecutionCallback<String> callback = new CountingDownExecutionCallback<String>(1);
completableFuture.andThen(callback);
try {
assertOpenEventually(callback.getLatch());
assertEquals(CountDownLatchAwaitingCallable.RESULT, callback.getResult());
} catch (AssertionError error) {
System.out.println(callback.getLatch().getCount());
System.out.println(callback.getResult());
throw error;
}
} finally {
executorService.shutdown();
}
}
@Test
public void testMultiPostRegisteredExecutionCallbackCompletableFuture() throws Exception {
ExecutionService es = getExecutionService(createHazelcastInstance());
CountDownLatch callableLatch = new CountDownLatch(1);
CountDownLatch callbackLatch = new CountDownLatch(2);
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
Future<String> future = executorService.submit(new CountDownLatchAwaitingCallable(callableLatch));
ICompletableFuture<String> completableFuture = es.asCompletableFuture(future);
callableLatch.countDown();
future.get();
CountingDownExecutionCallback<String> callback1 = new CountingDownExecutionCallback<String>(callbackLatch);
completableFuture.andThen(callback1);
CountingDownExecutionCallback<String> callback2 = new CountingDownExecutionCallback<String>(callbackLatch);
completableFuture.andThen(callback2);
assertOpenEventually(callbackLatch);
assertEquals(CountDownLatchAwaitingCallable.RESULT, callback1.getResult());
assertEquals(CountDownLatchAwaitingCallable.RESULT, callback2.getResult());
} finally {
executorService.shutdown();
}
}
@Test
public void test_registerCallback_beforeFutureIsCompletedOnOtherNode() throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2);
HazelcastInstance instance1 = factory.newHazelcastInstance();
HazelcastInstance instance2 = factory.newHazelcastInstance();
assertTrue(instance1.getCountDownLatch("latch").trySetCount(1));
String name = randomString();
IExecutorService executorService = instance2.getExecutorService(name);
ICountDownLatchAwaitCallable task = new ICountDownLatchAwaitCallable("latch");
String key = generateKeyOwnedBy(instance1);
ICompletableFuture<Boolean> future = (ICompletableFuture<Boolean>) executorService.submitToKeyOwner(task, key);
CountingDownExecutionCallback<Boolean> callback = new CountingDownExecutionCallback<Boolean>(1);
future.andThen(callback);
instance1.getCountDownLatch("latch").countDown();
assertTrue(future.get());
assertOpenEventually(callback.getLatch());
}
@Test
public void test_registerCallback_afterFutureIsCompletedOnOtherNode() throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2);
HazelcastInstance instance1 = factory.newHazelcastInstance();
HazelcastInstance instance2 = factory.newHazelcastInstance();
String name = randomString();
IExecutorService executorService = instance2.getExecutorService(name);
BasicTestCallable task = new BasicTestCallable();
String key = generateKeyOwnedBy(instance1);
ICompletableFuture<String> future = (ICompletableFuture<String>) executorService.submitToKeyOwner(task, key);
assertEquals(BasicTestCallable.RESULT, future.get());
CountingDownExecutionCallback<String> callback = new CountingDownExecutionCallback<String>(1);
future.andThen(callback);
assertOpenEventually(callback.getLatch(), 10);
}
@Test
public void test_registerCallback_multipleTimes_futureIsCompletedOnOtherNode() throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2);
HazelcastInstance instance1 = factory.newHazelcastInstance();
HazelcastInstance instance2 = factory.newHazelcastInstance();
assertTrue(instance1.getCountDownLatch("latch").trySetCount(1));
String name = randomString();
IExecutorService executorService = instance2.getExecutorService(name);
ICountDownLatchAwaitCallable task = new ICountDownLatchAwaitCallable("latch");
String key = generateKeyOwnedBy(instance1);
ICompletableFuture<Boolean> future = (ICompletableFuture<Boolean>) executorService.submitToKeyOwner(task, key);
CountDownLatch latch = new CountDownLatch(2);
CountingDownExecutionCallback<Boolean> callback = new CountingDownExecutionCallback<Boolean>(latch);
future.andThen(callback);
future.andThen(callback);
instance1.getCountDownLatch("latch").countDown();
assertTrue(future.get());
assertOpenEventually(latch, 10);
}
@Test
public void testSubmitFailingCallableException_withExecutionCallback() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(1);
HazelcastInstance instance = factory.newHazelcastInstance();
IExecutorService service = instance.getExecutorService(randomString());
CountingDownExecutionCallback<String> callback = new CountingDownExecutionCallback<String>(1);
service.submit(new FailingTestTask(), callback);
assertOpenEventually(callback.getLatch());
assertTrue(callback.getResult() instanceof Throwable);
}
/* ############ submit(Runnable) ############ */
@Test(expected = RejectedExecutionException.class)
public void testEmptyMemberSelector() {
HazelcastInstance instance = createHazelcastInstance();
String name = randomString();
IExecutorService executorService = instance.getExecutorService(name);
HazelcastInstanceAwareRunnable task = new HazelcastInstanceAwareRunnable();
executorService.execute(task, new MemberSelector() {
@Override
public boolean select(Member member) {
return false;
}
});
}
@Test
public void testManagedContextAndLocal() throws Exception {
Config config = new Config();
config.addExecutorConfig(new ExecutorConfig("test", 1));
final AtomicBoolean initialized = new AtomicBoolean();
config.setManagedContext(new ManagedContext() {
@Override
public Object initialize(Object obj) {
if (obj instanceof RunnableWithManagedContext) {
initialized.set(true);
}
return obj;
}
});
HazelcastInstance instance = createHazelcastInstance(config);
IExecutorService executor = instance.getExecutorService("test");
RunnableWithManagedContext task = new RunnableWithManagedContext();
executor.submit(task).get();
assertTrue("The task should have been initialized by the ManagedContext", initialized.get());
}
static class RunnableWithManagedContext implements Runnable, Serializable {
@Override
public void run() {
}
}
@Test
public void hazelcastInstanceAwareAndLocal() throws Exception {
Config config = new Config();
config.addExecutorConfig(new ExecutorConfig("test", 1));
HazelcastInstance instance = createHazelcastInstance(config);
IExecutorService executor = instance.getExecutorService("test");
HazelcastInstanceAwareRunnable task = new HazelcastInstanceAwareRunnable();
// if 'setHazelcastInstance' not called we expect a RuntimeException
executor.submit(task).get();
}
static class HazelcastInstanceAwareRunnable implements Runnable, HazelcastInstanceAware, Serializable {
private transient boolean initializeCalled = false;
@Override
public void run() {
if (!initializeCalled) {
throw new RuntimeException("The setHazelcastInstance should have been called");
}
}
@Override
public void setHazelcastInstance(HazelcastInstance hazelcastInstance) {
initializeCalled = true;
}
}
@Test
public void testExecuteMultipleNode() throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
for (int i = 0; i < NODE_COUNT; i++) {
IExecutorService service = instances[i].getExecutorService("testExecuteMultipleNode");
int rand = new Random().nextInt(100);
Future<Integer> future = service.submit(new IncrementAtomicLongRunnable("count"), rand);
assertEquals(Integer.valueOf(rand), future.get(10, TimeUnit.SECONDS));
}
IAtomicLong count = instances[0].getAtomicLong("count");
assertEquals(NODE_COUNT, count.get());
}
@Test
public void testSubmitToKeyOwnerRunnable() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
final AtomicInteger nullResponseCount = new AtomicInteger(0);
final CountDownLatch responseLatch = new CountDownLatch(NODE_COUNT);
ExecutionCallback callback = new ExecutionCallback() {
public void onResponse(Object response) {
if (response == null) {
nullResponseCount.incrementAndGet();
}
responseLatch.countDown();
}
public void onFailure(Throwable t) {
}
};
for (int i = 0; i < NODE_COUNT; i++) {
HazelcastInstance instance = instances[i];
IExecutorService service = instance.getExecutorService("testSubmitToKeyOwnerRunnable");
Member localMember = instance.getCluster().getLocalMember();
int key = findNextKeyForMember(instance, localMember);
service.submitToKeyOwner(
new IncrementAtomicLongIfMemberUUIDNotMatchRunnable(localMember.getUuid(), "testSubmitToKeyOwnerRunnable"),
key, callback);
}
assertOpenEventually(responseLatch);
assertEquals(0, instances[0].getAtomicLong("testSubmitToKeyOwnerRunnable").get());
assertEquals(NODE_COUNT, nullResponseCount.get());
}
@Test
public void testSubmitToMemberRunnable() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
final AtomicInteger nullResponseCount = new AtomicInteger(0);
final CountDownLatch responseLatch = new CountDownLatch(NODE_COUNT);
ExecutionCallback callback = new ExecutionCallback() {
public void onResponse(Object response) {
if (response == null) {
nullResponseCount.incrementAndGet();
}
responseLatch.countDown();
}
public void onFailure(Throwable t) {
}
};
for (int i = 0; i < NODE_COUNT; i++) {
HazelcastInstance instance = instances[i];
IExecutorService service = instance.getExecutorService("testSubmitToMemberRunnable");
Member localMember = instance.getCluster().getLocalMember();
service.submitToMember(
new IncrementAtomicLongIfMemberUUIDNotMatchRunnable(localMember.getUuid(), "testSubmitToMemberRunnable"),
localMember, callback);
}
assertOpenEventually(responseLatch);
assertEquals(0, instances[0].getAtomicLong("testSubmitToMemberRunnable").get());
assertEquals(NODE_COUNT, nullResponseCount.get());
}
@Test
public void testSubmitToMembersRunnable() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
ResponseCountingMultiExecutionCallback callback = new ResponseCountingMultiExecutionCallback(NODE_COUNT);
int sum = 0;
Set<Member> membersSet = instances[0].getCluster().getMembers();
Member[] members = membersSet.toArray(new Member[membersSet.size()]);
Random random = new Random();
for (int i = 0; i < NODE_COUNT; i++) {
IExecutorService service = instances[i].getExecutorService("testSubmitToMembersRunnable");
int n = random.nextInt(NODE_COUNT) + 1;
sum += n;
Member[] m = new Member[n];
System.arraycopy(members, 0, m, 0, n);
service.submitToMembers(new IncrementAtomicLongRunnable("testSubmitToMembersRunnable"), Arrays.asList(m), callback);
}
assertOpenEventually(callback.getLatch());
IAtomicLong result = instances[0].getAtomicLong("testSubmitToMembersRunnable");
assertEquals(sum, result.get());
assertEquals(sum, callback.getCount());
}
@Test
public void testSubmitToAllMembersRunnable() throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
final AtomicInteger nullResponseCount = new AtomicInteger(0);
final CountDownLatch responseLatch = new CountDownLatch(NODE_COUNT * NODE_COUNT);
MultiExecutionCallback callback = new MultiExecutionCallback() {
public void onResponse(Member member, Object value) {
if (value == null) {
nullResponseCount.incrementAndGet();
}
responseLatch.countDown();
}
public void onComplete(Map<Member, Object> values) {
}
};
for (int i = 0; i < NODE_COUNT; i++) {
IExecutorService service = instances[i].getExecutorService("testSubmitToAllMembersRunnable");
service.submitToAllMembers(new IncrementAtomicLongRunnable("testSubmitToAllMembersRunnable"), callback);
}
assertTrue(responseLatch.await(30, TimeUnit.SECONDS));
IAtomicLong result = instances[0].getAtomicLong("testSubmitToAllMembersRunnable");
assertEquals(NODE_COUNT * NODE_COUNT, result.get());
assertEquals(NODE_COUNT * NODE_COUNT, nullResponseCount.get());
}
/* ############ submit(Callable) ############ */
/**
* Submit a null task must raise a NullPointerException
*/
@SuppressWarnings("ConstantConditions")
@Test(expected = NullPointerException.class)
public void submitNullTask() {
ExecutorService executor = createSingleNodeExecutorService("submitNullTask");
executor.submit((Callable) null);
}
/**
* Run a basic task
*/
@Test
public void testBasicTask() throws Exception {
Callable<String> task = new BasicTestCallable();
ExecutorService executor = createSingleNodeExecutorService("testBasicTask");
Future future = executor.submit(task);
assertEquals(future.get(), BasicTestCallable.RESULT);
}
@Test
public void testSubmitMultipleNode() throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
for (int i = 0; i < NODE_COUNT; i++) {
IExecutorService service = instances[i].getExecutorService("testSubmitMultipleNode");
Future future = service.submit(new IncrementAtomicLongCallable("testSubmitMultipleNode"));
assertEquals((long) (i + 1), future.get());
}
}
@Test
public void testSubmitToKeyOwnerCallable() throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
List<Future> futures = new ArrayList<Future>();
for (int i = 0; i < NODE_COUNT; i++) {
HazelcastInstance instance = instances[i];
IExecutorService service = instance.getExecutorService("testSubmitToKeyOwnerCallable");
Member localMember = instance.getCluster().getLocalMember();
int key = findNextKeyForMember(instance, localMember);
Future future = service.submitToKeyOwner(new MemberUUIDCheckCallable(localMember.getUuid()), key);
futures.add(future);
}
for (Future future : futures) {
assertTrue((Boolean) future.get(10, TimeUnit.SECONDS));
}
}
@Test
public void testSubmitToKeyOwnerCallable_withCallback() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
BooleanSuccessResponseCountingCallback callback = new BooleanSuccessResponseCountingCallback(NODE_COUNT);
for (int i = 0; i < NODE_COUNT; i++) {
HazelcastInstance instance = instances[i];
IExecutorService service = instance.getExecutorService("testSubmitToKeyOwnerCallable");
Member localMember = instance.getCluster().getLocalMember();
int key = findNextKeyForMember(instance, localMember);
service.submitToKeyOwner(new MemberUUIDCheckCallable(localMember.getUuid()), key, callback);
}
assertOpenEventually(callback.getResponseLatch());
assertEquals(NODE_COUNT, callback.getSuccessResponseCount());
}
@Test
public void testSubmitToMemberCallable() throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
List<Future> futures = new ArrayList<Future>();
for (int i = 0; i < NODE_COUNT; i++) {
HazelcastInstance instance = instances[i];
IExecutorService service = instance.getExecutorService("testSubmitToMemberCallable");
String memberUuid = instance.getCluster().getLocalMember().getUuid();
Future future = service.submitToMember(
new MemberUUIDCheckCallable(memberUuid), instance.getCluster().getLocalMember());
futures.add(future);
}
for (Future future : futures) {
assertTrue((Boolean) future.get(10, TimeUnit.SECONDS));
}
}
@Test
public void testSubmitToMemberCallable_withCallback() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
BooleanSuccessResponseCountingCallback callback = new BooleanSuccessResponseCountingCallback(NODE_COUNT);
for (int i = 0; i < NODE_COUNT; i++) {
HazelcastInstance instance = instances[i];
IExecutorService service = instance.getExecutorService("testSubmitToMemberCallable");
String memberUuid = instance.getCluster().getLocalMember().getUuid();
service.submitToMember(new MemberUUIDCheckCallable(memberUuid), instance.getCluster().getLocalMember(), callback);
}
assertOpenEventually(callback.getResponseLatch());
assertEquals(NODE_COUNT, callback.getSuccessResponseCount());
}
@Test
public void testSubmitToMembersCallable() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
final AtomicInteger count = new AtomicInteger(0);
final CountDownLatch latch = new CountDownLatch(NODE_COUNT);
MultiExecutionCallback callback = new MultiExecutionCallback() {
public void onResponse(Member member, Object value) {
count.incrementAndGet();
}
public void onComplete(Map<Member, Object> values) {
latch.countDown();
}
};
int sum = 0;
Set<Member> membersSet = instances[0].getCluster().getMembers();
Member[] members = membersSet.toArray(new Member[membersSet.size()]);
Random random = new Random();
String name = "testSubmitToMembersCallable";
for (int i = 0; i < NODE_COUNT; i++) {
IExecutorService service = instances[i].getExecutorService(name);
int n = random.nextInt(NODE_COUNT) + 1;
sum += n;
Member[] m = new Member[n];
System.arraycopy(members, 0, m, 0, n);
service.submitToMembers(new IncrementAtomicLongCallable(name), Arrays.asList(m), callback);
}
assertOpenEventually(latch);
IAtomicLong result = instances[0].getAtomicLong(name);
assertEquals(sum, result.get());
assertEquals(sum, count.get());
}
@Test
public void testSubmitToAllMembersCallable() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(NODE_COUNT);
HazelcastInstance[] instances = factory.newInstances(new Config());
final AtomicInteger count = new AtomicInteger(0);
final CountDownLatch countDownLatch = new CountDownLatch(NODE_COUNT * NODE_COUNT);
MultiExecutionCallback callback = new MultiExecutionCallback() {
public void onResponse(Member member, Object value) {
count.incrementAndGet();
countDownLatch.countDown();
}
public void onComplete(Map<Member, Object> values) {
}
};
for (int i = 0; i < NODE_COUNT; i++) {
IExecutorService service = instances[i].getExecutorService("testSubmitToAllMembersCallable");
service.submitToAllMembers(new IncrementAtomicLongCallable("testSubmitToAllMembersCallable"), callback);
}
assertOpenEventually(countDownLatch);
IAtomicLong result = instances[0].getAtomicLong("testSubmitToAllMembersCallable");
assertEquals(NODE_COUNT * NODE_COUNT, result.get());
assertEquals(NODE_COUNT * NODE_COUNT, count.get());
}
/* ############ cancellation ############ */
@Test
public void testCancellationAwareTask() throws Exception {
SleepingTask task = new SleepingTask(5);
ExecutorService executor = createSingleNodeExecutorService("testCancellationAwareTask");
Future future = executor.submit(task);
try {
future.get(2, TimeUnit.SECONDS);
fail("Should throw TimeoutException!");
} catch (TimeoutException expected) {
}
assertFalse(future.isDone());
assertTrue(future.cancel(true));
assertTrue(future.isCancelled());
assertTrue(future.isDone());
try {
future.get();
fail("Should not complete the task successfully");
} catch (CancellationException expected) {
} catch (Exception e) {
fail("Unexpected exception " + e);
}
}
@Test
public void testCancellationAwareTask2() {
Callable task1 = new SleepingTask(Integer.MAX_VALUE);
ExecutorService executor = createSingleNodeExecutorService("testCancellationAwareTask", 1);
Future future1 = executor.submit(task1);
try {
future1.get(2, TimeUnit.SECONDS);
fail("SleepingTask should not return response");
} catch (TimeoutException ignored) {
} catch (Exception e) {
if (e.getCause() instanceof RejectedExecutionException) {
fail("SleepingTask is rejected!");
}
}
assertFalse(future1.isDone());
Callable task2 = new BasicTestCallable();
Future future2 = executor.submit(task2);
assertFalse(future2.isDone());
assertTrue(future2.cancel(true));
assertTrue(future2.isCancelled());
assertTrue(future2.isDone());
try {
future2.get();
fail("Should not complete the task successfully");
} catch (CancellationException expected) {
} catch (Exception e) {
fail("Unexpected exception " + e);
}
}
/* ############ future ############ */
/**
* Test the method isDone()
*/
@Test
public void testIsDoneMethod() throws Exception {
Callable<String> task = new BasicTestCallable();
IExecutorService executor = createSingleNodeExecutorService("isDoneMethod");
Future future = executor.submit(task);
assertResult(future, BasicTestCallable.RESULT);
}
/**
* Test for the issue 129.
* Repeatedly runs tasks and check for isDone() status after get().
*/
@Test
public void testIsDoneMethod2() throws Exception {
ExecutorService executor = createSingleNodeExecutorService("isDoneMethod2");
for (int i = 0; i < TASK_COUNT; i++) {
Callable<String> task1 = new BasicTestCallable();
Callable<String> task2 = new BasicTestCallable();
Future future1 = executor.submit(task1);
Future future2 = executor.submit(task2);
assertResult(future2, BasicTestCallable.RESULT);
assertResult(future1, BasicTestCallable.RESULT);
}
}
/**
* Test multiple Future.get() invocation
*/
@Test
public void testMultipleFutureGets() throws Exception {
Callable<String> task = new BasicTestCallable();
ExecutorService executor = createSingleNodeExecutorService("isTwoGetFromFuture");
Future<String> future = executor.submit(task);
assertResult(future, BasicTestCallable.RESULT);
assertResult(future, BasicTestCallable.RESULT);
assertResult(future, BasicTestCallable.RESULT);
assertResult(future, BasicTestCallable.RESULT);
}
private void assertResult(Future future, Object expected) throws Exception {
assertEquals(future.get(), expected);
assertTrue(future.isDone());
}
@Test
public void testIssue292() {
CountingDownExecutionCallback<Member> callback = new CountingDownExecutionCallback<Member>(1);
createSingleNodeExecutorService("testIssue292").submit(new MemberCheck(), callback);
assertOpenEventually(callback.getLatch());
assertTrue(callback.getResult() instanceof Member);
}
/**
* Execute a task that is executing something else inside.
* Nested Execution.
*/
@Test
public void testNestedExecution() {
Callable<String> task = new NestedExecutorTask();
ExecutorService executor = createSingleNodeExecutorService("testNestedExecution");
Future future = executor.submit(task);
assertCompletesEventually(future);
}
/**
* invokeAll tests
*/
@Test
public void testInvokeAll() throws Exception {
ExecutorService executor = createSingleNodeExecutorService("testInvokeAll");
assertFalse(executor.isShutdown());
// only one task
ArrayList<Callable<String>> tasks = new ArrayList<Callable<String>>();
tasks.add(new BasicTestCallable());
List<Future<String>> futures = executor.invokeAll(tasks);
assertEquals(futures.size(), 1);
assertEquals(futures.get(0).get(), BasicTestCallable.RESULT);
// more tasks
tasks.clear();
for (int i = 0; i < TASK_COUNT; i++) {
tasks.add(new BasicTestCallable());
}
futures = executor.invokeAll(tasks);
assertEquals(futures.size(), TASK_COUNT);
for (int i = 0; i < TASK_COUNT; i++) {
assertEquals(futures.get(i).get(), BasicTestCallable.RESULT);
}
}
@Test
public void testInvokeAllTimeoutCancelled() throws Exception {
ExecutorService executor = createSingleNodeExecutorService("testInvokeAll");
assertFalse(executor.isShutdown());
// only one task
ArrayList<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>();
tasks.add(new SleepingTask(0));
List<Future<Boolean>> futures = executor.invokeAll(tasks, 5, TimeUnit.SECONDS);
assertEquals(futures.size(), 1);
assertEquals(futures.get(0).get(), Boolean.TRUE);
// more tasks
tasks.clear();
for (int i = 0; i < TASK_COUNT; i++) {
tasks.add(new SleepingTask(i < 2 ? 0 : 20));
}
futures = executor.invokeAll(tasks, 5, TimeUnit.SECONDS);
assertEquals(futures.size(), TASK_COUNT);
for (int i = 0; i < TASK_COUNT; i++) {
if (i < 2) {
assertEquals(futures.get(i).get(), Boolean.TRUE);
} else {
boolean excepted = false;
try {
futures.get(i).get();
} catch (CancellationException e) {
excepted = true;
}
assertTrue(excepted);
}
}
}
@Test
public void testInvokeAllTimeoutSuccess() throws Exception {
ExecutorService executor = createSingleNodeExecutorService("testInvokeAll");
assertFalse(executor.isShutdown());
// only one task
ArrayList<Callable<String>> tasks = new ArrayList<Callable<String>>();
tasks.add(new BasicTestCallable());
List<Future<String>> futures = executor.invokeAll(tasks, 5, TimeUnit.SECONDS);
assertEquals(futures.size(), 1);
assertEquals(futures.get(0).get(), BasicTestCallable.RESULT);
// more tasks
tasks.clear();
for (int i = 0; i < TASK_COUNT; i++) {
tasks.add(new BasicTestCallable());
}
futures = executor.invokeAll(tasks, 15, TimeUnit.SECONDS);
assertEquals(futures.size(), TASK_COUNT);
for (int i = 0; i < TASK_COUNT; i++) {
assertEquals(futures.get(i).get(), BasicTestCallable.RESULT);
}
}
/**
* Shutdown-related method behaviour when the cluster is running
*/
@Test
public void testShutdownBehaviour() {
ExecutorService executor = createSingleNodeExecutorService("testShutdownBehaviour");
// fresh instance, is not shutting down
assertFalse(executor.isShutdown());
assertFalse(executor.isTerminated());
executor.shutdown();
assertTrue(executor.isShutdown());
assertTrue(executor.isTerminated());
// shutdownNow() should return an empty list and be ignored
List<Runnable> pending = executor.shutdownNow();
assertTrue(pending.isEmpty());
assertTrue(executor.isShutdown());
assertTrue(executor.isTerminated());
// awaitTermination() should return immediately false
try {
boolean terminated = executor.awaitTermination(60L, TimeUnit.SECONDS);
assertFalse(terminated);
} catch (InterruptedException ie) {
fail("InterruptedException");
}
assertTrue(executor.isShutdown());
assertTrue(executor.isTerminated());
}
/**
* Shutting down the cluster should act as the ExecutorService shutdown
*/
@Test(expected = RejectedExecutionException.class)
public void test_whenClusterShutdown_thenNewTasksShouldBeRejected() {
ExecutorService executor = createSingleNodeExecutorService("testClusterShutdownTaskRejection");
shutdownNodeFactory();
sleepSeconds(2);
assertNotNull(executor);
assertTrue(executor.isShutdown());
assertTrue(executor.isTerminated());
// new tasks must be rejected
Callable<String> task = new BasicTestCallable();
executor.submit(task);
}
@Test
public void testClusterShutdown_whenMultipleNodes_thenAllExecutorsAreShutdown() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2);
HazelcastInstance instance1 = factory.newHazelcastInstance();
HazelcastInstance instance2 = factory.newHazelcastInstance();
final ExecutorService es1 = instance1.getExecutorService("testClusterShutdown");
final ExecutorService es2 = instance1.getExecutorService("testClusterShutdown");
assertFalse(es1.isTerminated());
assertFalse(es2.isTerminated());
// we shutdown the ExecutorService on the first instance
es1.shutdown();
// the ExecutorService on the second instance should be shutdown via a ShutdownOperation
assertTrueEventually(new AssertTask() {
@Override
public void run() throws Exception {
assertTrue(es2.isTerminated());
}
});
}
@Test
public void testStatsIssue2039() throws Exception {
Config config = new Config();
String name = "testStatsIssue2039";
config.addExecutorConfig(new ExecutorConfig(name).setQueueCapacity(1).setPoolSize(1));
HazelcastInstance instance = createHazelcastInstance(config);
IExecutorService executorService = instance.getExecutorService(name);
executorService.execute(new SleepLatchRunnable());
assertOpenEventually(SleepLatchRunnable.startLatch, 30);
Future waitingInQueue = executorService.submit(new EmptyRunnable());
Future rejected = executorService.submit(new EmptyRunnable());
try {
rejected.get(1, TimeUnit.MINUTES);
} catch (Exception e) {
boolean isRejected = e.getCause() instanceof RejectedExecutionException;
if (!isRejected) {
fail(e.toString());
}
} finally {
SleepLatchRunnable.sleepLatch.countDown();
}
waitingInQueue.get(1, TimeUnit.MINUTES);
LocalExecutorStats stats = executorService.getLocalExecutorStats();
assertEquals(2, stats.getStartedTaskCount());
assertEquals(0, stats.getPendingTaskCount());
}
@Test
public void testExecutorServiceStats() throws Exception {
int i = 10;
LocalExecutorStats stats = executeTasksForStats("testExecutorServiceStats", i, true);
assertEquals(i + 1, stats.getStartedTaskCount());
assertEquals(i, stats.getCompletedTaskCount());
assertEquals(0, stats.getPendingTaskCount());
assertEquals(1, stats.getCancelledTaskCount());
}
@Test
public void testExecutorServiceStats_whenStatsAreDisabled() throws Exception {
LocalExecutorStats stats = executeTasksForStats("testExecutorServiceStats_whenStatsDisabled", 10, false);
assertEquals(0, stats.getStartedTaskCount());
assertEquals(0, stats.getCompletedTaskCount());
assertEquals(0, stats.getPendingTaskCount());
assertEquals(0, stats.getCancelledTaskCount());
}
/**
* Executes {@code tasksToExecute}+1 tasks, allowing {@code tasksToExecute} to complete and cancelling 1.
*/
private LocalExecutorStats executeTasksForStats(String executorName, int tasksToExecute, boolean statsEnabled)
throws Exception {
IExecutorService executorService = createSingleNodeExecutorService(executorName, DEFAULT_POOL_SIZE, statsEnabled);
BlockingQueue<Future> taskQueue = new ArrayBlockingQueue<Future>(tasksToExecute);
CountDownLatch latch = new CountDownLatch(tasksToExecute);
for (int i = 0; i < tasksToExecute; i++) {
Future future = executorService.submit(new EmptyRunnable());
taskQueue.offer(future);
}
// await completion and countdown for each completed task
for (int i = 0; i < tasksToExecute; i++) {
Future future = taskQueue.take();
future.get();
latch.countDown();
}
assertOpenEventually(latch);
Future<Boolean> f = executorService.submit(new SleepingTask(10));
Thread.sleep(1000);
f.cancel(true);
try {
f.get();
} catch (CancellationException ignored) {
}
return executorService.getLocalExecutorStats();
}
@Test
public void testLongRunningCallable() throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2);
Config config = new Config();
long callTimeoutMillis = 3000;
config.setProperty(GroupProperty.OPERATION_CALL_TIMEOUT_MILLIS.getName(), String.valueOf(callTimeoutMillis));
HazelcastInstance hz1 = factory.newHazelcastInstance(config);
HazelcastInstance hz2 = factory.newHazelcastInstance(config);
IExecutorService executor = hz1.getExecutorService("test");
Future<Boolean> future = executor.submitToMember(
new SleepingTask(TimeUnit.MILLISECONDS.toSeconds(callTimeoutMillis) * 3), hz2.getCluster().getLocalMember());
Boolean result = future.get(1, TimeUnit.MINUTES);
assertTrue(result);
}
static class ICountDownLatchAwaitCallable implements Callable<Boolean>, HazelcastInstanceAware, Serializable {
private String name;
private HazelcastInstance instance;
ICountDownLatchAwaitCallable(String name) {
this.name = name;
}
@Override
public Boolean call() throws Exception {
return instance.getCountDownLatch(name).await(100, TimeUnit.SECONDS);
}
@Override
public void setHazelcastInstance(HazelcastInstance instance) {
this.instance = instance;
}
}
static class SleepLatchRunnable implements Runnable, Serializable {
static CountDownLatch startLatch;
static CountDownLatch sleepLatch;
SleepLatchRunnable() {
startLatch = new CountDownLatch(1);
sleepLatch = new CountDownLatch(1);
}
@Override
public void run() {
startLatch.countDown();
assertOpenEventually(sleepLatch);
}
}
static class EmptyRunnable implements Runnable, Serializable, PartitionAware {
@Override
public void run() {
}
@Override
public Object getPartitionKey() {
return "key";
}
}
}