/* * 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.core.HazelcastInstance; import com.hazelcast.core.IAtomicLong; import com.hazelcast.core.ICountDownLatch; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.SlowTest; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.util.concurrent.ExecutionException; import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @RunWith(HazelcastParallelClassRunner.class) @Category({SlowTest.class, ParallelTest.class}) public class ScheduledExecutorServiceSlowTest extends ScheduledExecutorServiceTestSupport { @Test public void schedule_withLongSleepingCallable_blockingOnGet() throws ExecutionException, InterruptedException { int delay = 0; double expectedResult = 169.4; 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); double result = future.get(); assertEquals(expectedResult, result, 0); assertTrue(future.isDone()); assertFalse(future.isCancelled()); } @Test public void schedule_withStatefulRunnable_durable() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(4); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); int waitStateSyncPeriodToAvoidPassiveState = 2000; String key = generateKeyOwnedBy(instances[1]); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); IAtomicLong runC = instances[0].getAtomicLong("runC"); IAtomicLong loadC = instances[0].getAtomicLong("loadC"); latch.trySetCount(1); IScheduledFuture future = executorService.scheduleOnKeyOwnerAtFixedRate( new StatefulRunnableTask("latch", "runC", "loadC"), key, 10, 10, SECONDS); // Wait for task to get scheduled and start latch.await(11, SECONDS); Thread.sleep(waitStateSyncPeriodToAvoidPassiveState); instances[1].getLifecycleService().shutdown(); // Reset latch - task should be running on a replica now latch.trySetCount(7); latch.await(70, SECONDS); future.cancel(false); assertEquals(getPartitionService(instances[0]).getPartitionId(key), future.getHandler().getPartitionId()); assertEquals(8, runC.get(), 1); assertEquals(1, loadC.get()); } @Test public void stats_longRunningTask_durable() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(4); String key = generateKeyOwnedBy(instances[1]); ICountDownLatch firstLatch = instances[0].getCountDownLatch("firstLatch"); firstLatch.trySetCount(2); ICountDownLatch lastLatch = instances[0].getCountDownLatch("lastLatch"); lastLatch.trySetCount(6); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture future = executorService.scheduleOnKeyOwnerAtFixedRate( new ICountdownLatchRunnableTask("firstLatch", "lastLatch"), key, 0, 10, SECONDS); firstLatch.await(12, SECONDS); instances[1].getLifecycleService().shutdown(); lastLatch.await(70, SECONDS); sleepSeconds(4); // Wait for run-cycle to finish before cancelling, in order for stats to get updated. future.cancel(false); ScheduledTaskStatistics stats = future.getStats(); assertEquals(6, stats.getTotalRuns(), 1); } @Test public void stats_manyRepetitionsTask() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(4); ICountDownLatch latch = instances[0].getCountDownLatch("latch"); latch.trySetCount(6); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture future = executorService.scheduleAtFixedRate( new ICountdownLatchRunnableTask("latch"), 0, 10, SECONDS); latch.await(120, SECONDS); sleepSeconds(4); // Wait for run-cycle to finish before cancelling, in order for stats to get updated. future.cancel(false); ScheduledTaskStatistics stats = future.getStats(); assertEquals(6, stats.getTotalRuns()); } @Test public void scheduleRandomPartitions_getAllScheduled_durable() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(3); 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); } instances[1].getLifecycleService().shutdown(); assertEquals(expectedTotal, countScheduledTasksOn(s), 0); // Verify all tasks for (int i = 0; i < expectedTotal; i++) { assertEquals(25.0 + i, futures[i].get()); } } @Test public void scheduleRandomPartitions_periodicTask_getAllScheduled_durable() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(3); IScheduledExecutorService s = getScheduledExecutor(instances, "s"); String key = generateKeyOwnedBy(instances[1]); String runsCounterName = "runs"; ICountDownLatch runsLatch = instances[0].getCountDownLatch(runsCounterName); runsLatch.trySetCount(2); int expectedTotal = 11; for (int i = 0; i < expectedTotal; i++) { s.scheduleOnKeyOwnerAtFixedRate(new ICountdownLatchRunnableTask(runsCounterName), key, 0, 2, SECONDS); } runsLatch.await(10, SECONDS); instances[1].getLifecycleService().shutdown(); assertEquals(expectedTotal, countScheduledTasksOn(s), 0); } @Test public void schedulePeriodicTask_withMultipleSchedulers_atRandomPartitions_thenGetAllScheduled() throws ExecutionException, InterruptedException { String runsCounterName = "runs"; HazelcastInstance[] instances = createClusterWithCount(3); ICountDownLatch runsLatch = instances[0].getCountDownLatch(runsCounterName); int numOfSchedulers = 10; int numOfTasks = 10; int expectedTotal = numOfSchedulers * numOfTasks; runsLatch.trySetCount(expectedTotal); for (int i = 0; i < numOfSchedulers; i++) { IScheduledExecutorService s = getScheduledExecutor(instances, "scheduler_" + i); String key = generateKeyOwnedBy(instances[1]); for (int k = 0; k < numOfTasks; k++) { s.scheduleOnKeyOwnerAtFixedRate(new ICountdownLatchRunnableTask(runsCounterName), key, 0, 2, SECONDS); } } runsLatch.await(10, SECONDS); int actualTotal = 0; for (int i = 0; i < numOfSchedulers; i++) { actualTotal += countScheduledTasksOn(getScheduledExecutor(instances, "scheduler_" + i)); } assertEquals(expectedTotal, actualTotal, 0); } @Test public void schedulePeriodicTask_withMultipleSchedulers_atRandomPartitions_shutdownOrDestroy_thenGetAllScheduled() throws ExecutionException, InterruptedException { String runsCounterName = "runs"; HazelcastInstance[] instances = createClusterWithCount(3); ICountDownLatch runsLatch = instances[0].getCountDownLatch(runsCounterName); int numOfSchedulers = 10; int numOfTasks = 10; int expectedTotal = numOfSchedulers * numOfTasks; runsLatch.trySetCount(expectedTotal); for (int i = 0; i < numOfSchedulers; i++) { IScheduledExecutorService s = getScheduledExecutor(instances, "scheduler_" + i); String key = generateKeyOwnedBy(instances[1]); for (int k = 0; k < numOfTasks; k++) { s.scheduleOnKeyOwnerAtFixedRate(new ICountdownLatchRunnableTask(runsCounterName), key, 0, 2, SECONDS); } } runsLatch.await(10, SECONDS); getScheduledExecutor(instances, "scheduler_" + 0).shutdown(); getScheduledExecutor(instances, "scheduler_" + 1).shutdown(); getScheduledExecutor(instances, "scheduler_" + 3).destroy(); int actualTotal = 0; for (int i = 0; i < numOfSchedulers; i++) { actualTotal += countScheduledTasksOn(getScheduledExecutor(instances, "scheduler_" + i)); } assertEquals(expectedTotal - 3 /*numOfShutdownOrDestroy*/ * numOfTasks, actualTotal, 0); } @Test public void schedulePeriodicTask_withMultipleSchedulers_atRandomPartitions_killMember_thenGetAllScheduled() throws ExecutionException, InterruptedException { String runsCounterName = "runs"; HazelcastInstance[] instances = createClusterWithCount(10); ICountDownLatch runsLatch = instances[0].getCountDownLatch(runsCounterName); int numOfSchedulers = 20; int numOfTasks = 10; int expectedTotal = numOfSchedulers * numOfTasks; runsLatch.trySetCount(expectedTotal); for (int i = 0; i < numOfSchedulers; i++) { IScheduledExecutorService s = getScheduledExecutor(instances, "scheduler_" + i); String key = generateKeyOwnedBy(instances[i % instances.length]); for (int k = 0; k < numOfTasks; k++) { s.scheduleOnKeyOwner(new ICountdownLatchRunnableTask(runsCounterName), key, 0, SECONDS); } } runsLatch.await(10, SECONDS); instances[1].getLifecycleService().terminate(); int actualTotal = 0; for (int i = 0; i < numOfSchedulers; i++) { actualTotal += countScheduledTasksOn(getScheduledExecutor(instances, "scheduler_" + i)); } assertEquals(expectedTotal, actualTotal, 0); } @Test public void cancelUninterruptedTask_waitUntilRunCompleted_checkStatusIsCancelled() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(1); String runFinishedLatchName = "runFinishedLatch"; ICountDownLatch latch = instances[0].getCountDownLatch(runFinishedLatchName); latch.trySetCount(1); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture future = executorService.scheduleAtFixedRate( new HotLoopBusyTask(runFinishedLatchName), 0, 1, SECONDS); assertFalse(future.isCancelled()); assertFalse(future.isDone()); future.cancel(false); assertTrue(future.isCancelled()); assertTrue(future.isDone()); // Even though we cancelled the task is current task is still running. // Wait till the task is actually done latch.await(60, SECONDS); // Make sure SyncState goes through sleepSeconds(10); // Check once more that the task status is consistent assertTrue(future.isCancelled()); assertTrue(future.isDone()); } @Test public void cancelUninterruptedTask_waitUntilRunCompleted_killMember_checkStatusIsCancelled() throws ExecutionException, InterruptedException { HazelcastInstance[] instances = createClusterWithCount(2); String key = generateKeyOwnedBy(instances[1]); String runFinishedLatchName = "runFinishedLatch"; ICountDownLatch latch = instances[0].getCountDownLatch(runFinishedLatchName); latch.trySetCount(1); IScheduledExecutorService executorService = getScheduledExecutor(instances, "s"); IScheduledFuture future = executorService.scheduleOnKeyOwnerAtFixedRate( new HotLoopBusyTask(runFinishedLatchName), key, 0, 1, SECONDS); assertFalse(future.isCancelled()); assertFalse(future.isDone()); future.cancel(false); assertTrue(future.isCancelled()); assertTrue(future.isDone()); // Even though we cancelled the task is current task is still running. // Wait till the task is actually done latch.await(60, SECONDS); // Make sure SyncState goes through sleepSeconds(10); instances[1].getLifecycleService().terminate(); // Check once more that the task status is consistent assertTrue(future.isCancelled()); assertTrue(future.isDone()); } }