/* * 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.scheduledexecutor; import com.hazelcast.config.Config; import com.hazelcast.config.ScheduledExecutorConfig; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IAtomicLong; import com.hazelcast.core.ICountDownLatch; import com.hazelcast.core.IMap; import com.hazelcast.core.Member; import com.hazelcast.core.PartitionAware; import com.hazelcast.instance.MemberImpl; import com.hazelcast.internal.partition.impl.InternalPartitionServiceImpl; import com.hazelcast.scheduledexecutor.impl.DistributedScheduledExecutorService; import com.hazelcast.spi.impl.NodeEngineImpl; import com.hazelcast.spi.partition.IPartitionLostEvent; import com.hazelcast.test.AssertTask; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import com.hazelcast.util.RootCauseMatcher; import com.hazelcast.util.executor.ManagedExecutorService; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static com.hazelcast.scheduledexecutor.TaskUtils.named; import static java.util.concurrent.TimeUnit.SECONDS; 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 ScheduledExecutorServiceBasicTest extends ScheduledExecutorServiceTestSupport { @Rule public ExpectedException expected = ExpectedException.none(); @Test public void config() throws ExecutionException, InterruptedException { String schedulerName = "foobar"; ScheduledExecutorConfig sec = new ScheduledExecutorConfig() .setName(schedulerName) .setDurability(5) .setPoolSize(24); Config config = new Config().addScheduledExecutorConfig(sec); HazelcastInstance[] instances = createClusterWithCount(1, config); IScheduledFuture future = instances[0].getScheduledExecutorService(schedulerName) .schedule(new PlainCallableTask(), 0, SECONDS); NodeEngineImpl nodeEngine = getNodeEngineImpl(instances[0]); ManagedExecutorService mes = (ManagedExecutorService) nodeEngine.getExecutionService() .getScheduledDurable(sec.getName()); DistributedScheduledExecutorService dses = nodeEngine.getService(DistributedScheduledExecutorService.SERVICE_NAME); assertNotNull(mes); assertEquals(24, mes.getMaximumPoolSize()); assertEquals(5, dses.getPartition(future.getHandler().getPartitionId()) .getOrCreateContainer(schedulerName).getDurability()); assertEquals(1, dses.getPartition(future.getHandler().getPartitionId()) .getOrCreateContainer("other").getDurability()); } @Test public void capacity_whenNoLimit() throws ExecutionException, InterruptedException { String schedulerName = "foobar"; ScheduledExecutorConfig sec = new ScheduledExecutorConfig() .setName(schedulerName) .setDurability(1) .setPoolSize(1) .setCapacity(0); Config config = new Config().addScheduledExecutorConfig(sec); HazelcastInstance[] instances = createClusterWithCount(1, config); IScheduledExecutorService service = instances[0].getScheduledExecutorService(schedulerName); String keyOwner = "hitSamePartitionToCheckCapacity"; for (int i = 0; i < 101; i++) { service.scheduleOnKeyOwner(new PlainCallableTask(), keyOwner, 0, TimeUnit.SECONDS); } } @Test public void capacity_whenDefault() throws ExecutionException, InterruptedException { String schedulerName = "foobar"; HazelcastInstance[] instances = createClusterWithCount(1, null); IScheduledExecutorService service = instances[0].getScheduledExecutorService(schedulerName); String keyOwner = "hitSamePartitionToCheckCapacity"; for (int i = 0; i < 100; i++) { service.scheduleOnKeyOwner(new PlainCallableTask(), keyOwner, 0, TimeUnit.SECONDS); } try { service.scheduleOnKeyOwner(new PlainCallableTask(), keyOwner, 0, TimeUnit.SECONDS); fail("Should have been rejected."); } catch (RejectedExecutionException ex) { assertTrue("Got wrong RejectedExecutionException", ex.getMessage().equals("Maximum capacity of tasks reached.")); } } @Test public void capacity_whenPositiveLimit() throws ExecutionException, InterruptedException { String schedulerName = "foobar"; ScheduledExecutorConfig sec = new ScheduledExecutorConfig() .setName(schedulerName) .setDurability(1) .setPoolSize(1) .setCapacity(10); Config config = new Config().addScheduledExecutorConfig(sec); HazelcastInstance[] instances = createClusterWithCount(1, config); IScheduledExecutorService service = instances[0].getScheduledExecutorService(schedulerName); String keyOwner = "hitSamePartitionToCheckCapacity"; for (int i = 0; i < 10; i++) { service.scheduleOnKeyOwner(new PlainCallableTask(), keyOwner, 0, TimeUnit.SECONDS); } try { service.scheduleOnKeyOwner(new PlainCallableTask(), keyOwner, 0, TimeUnit.SECONDS); fail("Should have been rejected."); } catch (RejectedExecutionException ex) { assertTrue("Got wrong RejectedExecutionException", ex.getMessage().equals("Maximum capacity of tasks reached.")); } } @Test public void capacity_onMember_whenPositiveLimit() throws ExecutionException, InterruptedException { String schedulerName = "foobar"; ScheduledExecutorConfig sec = new ScheduledExecutorConfig() .setName(schedulerName) .setDurability(1) .setPoolSize(1) .setCapacity(10); Config config = new Config().addScheduledExecutorConfig(sec); HazelcastInstance[] instances = createClusterWithCount(1, config); IScheduledExecutorService service = instances[0].getScheduledExecutorService(schedulerName); Member member = instances[0].getCluster().getLocalMember(); for (int i = 0; i < 10; i++) { service.scheduleOnMember(new PlainCallableTask(), member, 0, TimeUnit.SECONDS); } try { service.scheduleOnMember(new PlainCallableTask(), member, 0, TimeUnit.SECONDS); fail("Should have been rejected."); } catch (RejectedExecutionException ex) { assertTrue("Got wrong RejectedExecutionException", ex.getMessage().equals("Maximum capacity of tasks reached.")); } } @Test public void handlerTaskAndSchedulerNames_withCallable() throws ExecutionException, InterruptedException { int delay = 0; String schedulerName = "s"; String taskName = "TestCallable"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = instances[0].getScheduledExecutorService(schedulerName); IScheduledFuture<Double> future = executorService.schedule( named(taskName, new PlainCallableTask()), delay, SECONDS); future.get(); ScheduledTaskHandler handler = future.getHandler(); assertEquals(schedulerName, handler.getSchedulerName()); assertEquals(taskName, handler.getTaskName()); } @Test public void handlerTaskAndSchedulerNames_withRunnable() throws ExecutionException, InterruptedException { int delay = 0; String schedulerName = "s"; String taskName = "TestRunnable"; HazelcastInstance[] instances = createClusterWithCount(2); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(1); IScheduledExecutorService executorService = instances[0].getScheduledExecutorService(schedulerName); IScheduledFuture future = executorService.schedule( named(taskName, new ICountdownLatchRunnableTask("latch")), delay, SECONDS); latch.await(10, SECONDS); ScheduledTaskHandler handler = future.getHandler(); assertEquals(schedulerName, handler.getSchedulerName()); assertEquals(taskName, handler.getTaskName()); } @Test public void stats() throws ExecutionException, InterruptedException { double delay = 2.0; HazelcastInstance[] instances = createClusterWithCount(2); Object key = generateKeyOwnedBy(instances[1]); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> future = executorService.scheduleOnKeyOwner( new PlainCallableTask(), key, (int) delay, SECONDS); future.get(); ScheduledTaskStatistics stats = future.getStats(); assertEquals(1, stats.getTotalRuns()); assertNotNull(stats.getLastIdleTime(SECONDS)); assertNotNull(stats.getLastRunDuration(SECONDS)); assertNotNull(stats.getTotalIdleTime(SECONDS)); assertNotNull(stats.getTotalRunTime(SECONDS)); assertNotNull(stats.getTotalRuns()); } @Test public void stats_whenMemberOwned() throws ExecutionException, InterruptedException { double delay = 2.0; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> future = executorService.scheduleOnMember( new PlainCallableTask(), instances[0].getCluster().getLocalMember(), (int) delay, SECONDS); future.get(); ScheduledTaskStatistics stats = future.getStats(); assertEquals(1, stats.getTotalRuns()); assertNotNull(stats.getLastIdleTime(SECONDS)); assertNotNull(stats.getLastRunDuration(SECONDS)); assertNotNull(stats.getTotalIdleTime(SECONDS)); assertNotNull(stats.getTotalRunTime(SECONDS)); assertNotNull(stats.getTotalRuns()); } @Test public void scheduleAndGet_withCallable() throws ExecutionException, InterruptedException { int delay = 5; double expectedResult = 25.0; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> future = executorService.schedule( new PlainCallableTask(), delay, SECONDS); double result = future.get(); assertEquals(expectedResult, result, 0); assertTrue(future.isDone()); assertFalse(future.isCancelled()); } @Test public void scheduleAndGet_withCallable_durableAfterTaskCompletion() throws ExecutionException, InterruptedException { int delay = 5; double expectedResult = 25.0; HazelcastInstance[] instances = createClusterWithCount(2); String key = generateKeyOwnedBy(instances[1]); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> future = executorService.scheduleOnKeyOwner( new PlainCallableTask(), key, delay, SECONDS); double resultFromOriginalTask = future.get(); instances[1].getLifecycleService().shutdown(); double resultFromMigratedTask = future.get(); assertEquals(expectedResult, resultFromOriginalTask, 0); assertEquals(expectedResult, resultFromMigratedTask, 0); assertTrue(future.isDone()); assertFalse(future.isCancelled()); } @Test public void schedule_withMapChanges_durable() throws ExecutionException, InterruptedException { int delay = 0; HazelcastInstance[] instances = createClusterWithCount(2); IMap<String, Integer> map = instances[1].getMap("map"); for (int i = 0; i < 100000; i++) { map.put(String.valueOf(i), i); } Object key = generateKeyOwnedBy(instances[0]); ICountDownLatch runsCountLatch = instances[1].getCountDownLatch("runsCountLatchName"); runsCountLatch.trySetCount(1); IAtomicLong runEntryCounter = instances[1].getAtomicLong("runEntryCounterName"); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); executorService.scheduleOnKeyOwner( new ICountdownLatchMapIncrementCallableTask("map", "runEntryCounterName", "runsCountLatchName"), key, delay, SECONDS); Thread.sleep(2000); instances[0].getLifecycleService().shutdown(); runsCountLatch.await(2, TimeUnit.MINUTES); for (int i = 0; i < 100000; i++) { assertTrue(map.get(String.valueOf(i)) == (i + 1)); } assertEquals(2, runEntryCounter.get()); } @Test public void schedule_withLongSleepingCallable_cancelledAndGet() throws ExecutionException, InterruptedException { int delay = 0; HazelcastInstance[] instances = createClusterWithCount(2); ICountDownLatch runsCountLatch = instances[0].getCountDownLatch("runsCountLatchName"); runsCountLatch.trySetCount(1); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> future = executorService.schedule( new ICountdownLatchCallableTask("runsCountLatchName", 15000), delay, SECONDS); Thread.sleep(4000); future.cancel(false); runsCountLatch.await(15, SECONDS); assertTrue(future.isDone()); assertTrue(future.isCancelled()); } @Test public void schedule_withNegativeDelay() throws ExecutionException, InterruptedException { int delay = -2; double expectedResult = 25.0; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> future = executorService.schedule( new PlainCallableTask(), delay, SECONDS); double result = future.get(); assertEquals(expectedResult, result, 0); assertTrue(future.isDone()); assertFalse(future.isCancelled()); } @Test(expected = DuplicateTaskException.class) public void schedule_duplicate() throws ExecutionException, InterruptedException { int delay = 1; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); executorService.schedule( named(taskName, new PlainCallableTask()), delay, SECONDS); executorService.schedule( named(taskName, new PlainCallableTask()), delay, SECONDS); } @Test(expected = UnsupportedOperationException.class) public void schedule_thenCancelInterrupted() throws ExecutionException, InterruptedException { int delay = 1; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.schedule( named(taskName, new PlainCallableTask()), delay, TimeUnit.MINUTES); first.cancel(true); } @Test(expected = CancellationException.class) public void schedule_thenCancelAndGet() throws ExecutionException, InterruptedException { int delay = 1; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.schedule( named(taskName, new PlainCallableTask()), delay, TimeUnit.MINUTES); first.cancel(false); first.get(); } @Test(expected = TimeoutException.class) public void schedule_thenGetWithTimeout() throws ExecutionException, InterruptedException, TimeoutException { int delay = 5; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.schedule( named(taskName, new PlainCallableTask()), delay, TimeUnit.MINUTES); first.get(2, TimeUnit.SECONDS); } @Test() public void schedule_getDelay() throws ExecutionException, InterruptedException { int delay = 20; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.schedule( named(taskName, new PlainCallableTask()), delay, TimeUnit.MINUTES); assertEquals(19, first.getDelay(TimeUnit.MINUTES)); } @Test() public void scheduleOnKeyOwner_getDelay() throws ExecutionException, InterruptedException { int delay = 20; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); Object key = generateKeyOwnedBy(instances[1]); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.scheduleOnKeyOwner( named(taskName, new PlainCallableTask()), key, delay, TimeUnit.MINUTES); assertEquals(19, first.getDelay(TimeUnit.MINUTES)); } @Test() public void scheduleOnMember_getDelay() throws ExecutionException, InterruptedException { int delay = 20; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.scheduleOnMember( named(taskName, new PlainCallableTask()), instances[0].getCluster().getLocalMember(), delay, TimeUnit.MINUTES); assertEquals(19, first.getDelay(TimeUnit.MINUTES)); } @Test() public void schedule_andCancel() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(2); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(1); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture future = executorService.scheduleAtFixedRate( new ICountdownLatchRunnableTask("latch"), 1, 1, SECONDS); Thread.sleep(5000); assertFalse(future.isCancelled()); assertFalse(future.isDone()); future.cancel(false); assertTrue(future.isCancelled()); assertTrue(future.isDone()); } @Test() public void schedule_andCancel_onMember() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(2); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(1); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture future = executorService.scheduleOnMemberAtFixedRate( new ICountdownLatchRunnableTask("latch"), instances[0].getCluster().getLocalMember(), 1, 1, SECONDS); Thread.sleep(5000); assertFalse(future.isCancelled()); assertFalse(future.isDone()); future.cancel(false); assertTrue(future.isCancelled()); assertTrue(future.isDone()); } @Test() public void cancelledAndDone_durable() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(3); Object key = generateKeyOwnedBy(instances[1]); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(1); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture future = executorService.scheduleOnKeyOwnerAtFixedRate( new ICountdownLatchRunnableTask("latch"), key, 0, 1, SECONDS); latch.await(10, SECONDS); assertFalse(future.isCancelled()); assertFalse(future.isDone()); future.cancel(false); assertTrue(future.isCancelled()); assertTrue(future.isDone()); instances[1].getLifecycleService().shutdown(); assertTrue(future.isCancelled()); assertTrue(future.isDone()); } @Test(expected = UnsupportedOperationException.class) public void schedule_compareTo() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.schedule( new PlainCallableTask(), 1, TimeUnit.MINUTES); IScheduledFuture<Double> second = executorService.schedule( new PlainCallableTask(), 2, TimeUnit.MINUTES); assertTrue(first.compareTo(second) == -1); } @Test(expected = StaleTaskException.class) public void schedule_thenDisposeThenGet() throws ExecutionException, InterruptedException { int delay = 1; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.schedule( named(taskName, new PlainCallableTask()), delay, SECONDS); first.dispose(); first.get(); } @Test(expected = StaleTaskException.class) public void schedule_thenDisposeThenGet_onMember() throws ExecutionException, InterruptedException { int delay = 1; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.scheduleOnMember( named(taskName, new PlainCallableTask()), instances[0].getCluster().getLocalMember(), delay, SECONDS); first.dispose(); first.get(); } @Test(expected = RejectedExecutionException.class) public void schedule_whenShutdown() throws ExecutionException, InterruptedException { int delay = 1; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); executorService.schedule(new PlainCallableTask(), delay, SECONDS); executorService.shutdown(); executorService.schedule(new PlainCallableTask(), delay, SECONDS); } @Test() public void schedule_whenPartitionLost() throws ExecutionException, InterruptedException { int delay = 1; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); final IScheduledFuture future = executorService.schedule(new PlainCallableTask(), delay, SECONDS); ScheduledTaskHandler handler = future.getHandler(); int partitionOwner = handler.getPartitionId(); IPartitionLostEvent internalEvent = new IPartitionLostEvent(partitionOwner, 1, null); ((InternalPartitionServiceImpl) getNodeEngineImpl(instances[0]).getPartitionService()).onPartitionLost(internalEvent); assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { try { future.get(); } catch (IllegalStateException ex) { assertEquals("Partition holding this Scheduled task was lost along with all backups.", ex.getMessage()); } } }); } @Test public void schedule_getHandlerDisposeThenRecreateFutureAndGet() throws ExecutionException, InterruptedException { int delay = 1; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.schedule( named(taskName, new PlainCallableTask()), delay, SECONDS); ScheduledTaskHandler handler = first.getHandler(); first.dispose(); expected.expect(ExecutionException.class); expected.expectCause(new RootCauseMatcher(StaleTaskException.class)); executorService.getScheduledFuture(handler).get(); } @Test() public void schedule_partitionAware() throws ExecutionException, InterruptedException { int delay = 1; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); Callable<Double> task = new PlainPartitionAwareCallableTask(); IScheduledFuture<Double> first = executorService.schedule( task, delay, SECONDS); ScheduledTaskHandler handler = first.getHandler(); int expectedPartition = getPartitionIdFromPartitionAwareTask(instances[0], (PartitionAware) task); assertEquals(expectedPartition, handler.getPartitionId()); } @Test() public void schedule_partitionAware_runnable() throws ExecutionException, InterruptedException { int delay = 1; String completionLatchName = "completionLatch"; HazelcastInstance[] instances = createClusterWithCount(2); ICountDownLatch completionLatch = instances[0].getCountDownLatch(completionLatchName); completionLatch.trySetCount(1); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); Runnable task = new PlainPartitionAwareRunnableTask(completionLatchName); IScheduledFuture first = executorService.schedule( task, delay, SECONDS); completionLatch.await(10, SECONDS); ScheduledTaskHandler handler = first.getHandler(); int expectedPartition = getPartitionIdFromPartitionAwareTask(instances[0], (PartitionAware) task); assertEquals(expectedPartition, handler.getPartitionId()); } @Test public void schedule_withStatefulRunnable() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(4); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(1); executorService.schedule( new StatefulRunnableTask("latch", "runC", "loadC"), 2, SECONDS); latch.await(10, SECONDS); } @Test public void scheduleWithRepetition() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService s = getScheduledExecutor(instances, "s"); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(3); IScheduledFuture future = s.scheduleAtFixedRate(new ICountdownLatchRunnableTask("latch"), 0, 1, SECONDS); latch.await(10, SECONDS); future.cancel(false); assertEquals(0, latch.getCount()); } @Test public void scheduleOnMember() throws ExecutionException, InterruptedException { int delay = 1; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); MemberImpl member = getNodeEngineImpl(instances[0]).getLocalMember(); IScheduledFuture<Double> future = executorService.scheduleOnMember(new PlainCallableTask(), member, delay, SECONDS); assertTrue(future.getHandler().isAssignedToMember()); assertEquals(25.0, future.get(), 0); } @Test public void scheduleOnMemberWithRepetition() throws InterruptedException { HazelcastInstance[] instances = createClusterWithCount(4); IScheduledExecutorService s = getScheduledExecutor(instances, "s"); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(4); Map<Member, IScheduledFuture<?>> futures = s .scheduleOnAllMembersAtFixedRate(new ICountdownLatchRunnableTask("latch"), 0, 3, SECONDS); latch.await(10, SECONDS); assertEquals(0, latch.getCount()); assertEquals(4, futures.size()); } @Test public void scheduleOnKeyOwner_thenGet() throws InterruptedException, ExecutionException { HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); String key = generateKeyOwnedBy(instances[1]); IScheduledFuture<Double> future = executorService.scheduleOnKeyOwner( new PlainCallableTask(), key, 2, SECONDS); assertEquals(25.0, future.get(), 0.0); } @Test public void scheduleOnKeyOwner_withNotPeriodicRunable() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(2); String key = generateKeyOwnedBy(instances[0]); IScheduledExecutorService s = getScheduledExecutor(instances, "s"); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(1); s.scheduleOnKeyOwner(new ICountdownLatchRunnableTask("latch"), key, 2, SECONDS).get(); assertEquals(0, latch.getCount()); } @Test public void scheduleOnKeyOwner_withNotPeriodicRunableDurable() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(2); String key = generateKeyOwnedBy(instances[1]); IScheduledExecutorService s = getScheduledExecutor(instances, "s"); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(1); IScheduledFuture future = s.scheduleOnKeyOwner(new ICountdownLatchRunnableTask("latch"), key, 2, SECONDS); instances[1].getLifecycleService().shutdown(); future.get(); assertEquals(0, latch.getCount()); } @Test public void scheduleOnKeyOwner_withCallable() throws ExecutionException, InterruptedException { int delay = 1; String key = "TestKey"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); Callable<Double> task = new PlainPartitionAwareCallableTask(); IScheduledFuture<Double> first = executorService.scheduleOnKeyOwner( task, key, delay, SECONDS); ScheduledTaskHandler handler = first.getHandler(); int expectedPartition = instances[0].getPartitionService() .getPartition(key) .getPartitionId(); assertEquals(expectedPartition, handler.getPartitionId()); assertEquals(25, first.get(), 0); } @Test public void scheduleOnKeyOwnerWithRepetition() throws InterruptedException { String key = "TestKey"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(5); IScheduledFuture future = executorService.scheduleOnKeyOwnerAtFixedRate( new ICountdownLatchRunnableTask("latch"), key, 0, 1, SECONDS); ScheduledTaskHandler handler = future.getHandler(); int expectedPartition = instances[0].getPartitionService() .getPartition(key) .getPartitionId(); assertEquals(expectedPartition, handler.getPartitionId()); latch.await(10, SECONDS); assertEquals(0, latch.getCount()); } @Test public void getScheduled() { int delay = 1; String taskName = "Test"; HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture<Double> first = executorService.schedule( named(taskName, new PlainCallableTask()), delay, SECONDS); ScheduledTaskHandler handler = first.getHandler(); IScheduledFuture<Double> copy = executorService.getScheduledFuture(handler); assertEquals(first, copy); } @Test public void scheduleOnAllMembers_getAllScheduled() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(3); IScheduledExecutorService s = getScheduledExecutor(instances, "s"); s.scheduleOnAllMembers(new PlainCallableTask(), 0, SECONDS); Set<Member> members = instances[0].getCluster().getMembers(); Map<Member, List<IScheduledFuture<Double>>> allScheduled = s.getAllScheduledFutures(); assertEquals(members.size(), allScheduled.size()); for (Member member : members) { assertEquals(1, allScheduled.get(member).size()); assertEquals(25.0, allScheduled.get(member).get(0).get(), 0); } } @Test public void scheduleRandomPartitions_getAllScheduled() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(2); IScheduledExecutorService s = getScheduledExecutor(instances, "s"); int expectedTotal = 11; IScheduledFuture[] futures = new IScheduledFuture[expectedTotal]; for (int i = 0; i < expectedTotal; i++) { futures[i] = s.schedule(new PlainCallableTask(i), 0, SECONDS); } assertEquals(expectedTotal, countScheduledTasksOn(s), 0); // Dispose 1 task futures[0].dispose(); // Recount assertEquals(expectedTotal - 1, countScheduledTasksOn(s), 0); // Verify all tasks for (int i = 1; i < expectedTotal; i++) { assertEquals(25.0 + i, futures[i].get()); } } @Test public void getErroneous() throws InterruptedException, ExecutionException { int delay = 2; String taskName = "Test"; String completionLatchName = "completionLatch"; HazelcastInstance[] instances = createClusterWithCount(2); String key = generateKeyOwnedBy(instances[1]); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); ICountDownLatch latch = instances[1].getCountDownLatch(completionLatchName); latch.trySetCount(1); IScheduledFuture<Double> future = executorService.scheduleOnKeyOwner( named(taskName, new ErroneousCallableTask(completionLatchName)), key, delay, SECONDS); latch.await(10, SECONDS); expected.expect(ExecutionException.class); expected.expectCause(new RootCauseMatcher(IllegalStateException.class, "Erroneous task")); future.get(); } @Test public void getErroneous_durable() throws InterruptedException, ExecutionException { int delay = 2; String taskName = "Test"; String completionLatchName = "completionLatch"; HazelcastInstance[] instances = createClusterWithCount(2); String key = generateKeyOwnedBy(instances[1]); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); ICountDownLatch latch = instances[1].getCountDownLatch(completionLatchName); latch.trySetCount(1); IScheduledFuture<Double> future = executorService.scheduleOnKeyOwner( named(taskName, new ErroneousCallableTask(completionLatchName)), key, delay, SECONDS); latch.await(10, SECONDS); instances[1].getLifecycleService().shutdown(); Thread.sleep(2000); expected.expect(ExecutionException.class); expected.expectCause(new RootCauseMatcher(IllegalStateException.class, "Erroneous task")); future.get(); } }