/** * 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 org.apache.aurora.scheduler.scheduling; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import org.apache.aurora.common.quantity.Amount; import org.apache.aurora.common.quantity.Time; import org.apache.aurora.common.testing.easymock.EasyMockTest; import org.apache.aurora.common.util.testing.FakeClock; import org.apache.aurora.gen.AssignedTask; import org.apache.aurora.gen.ScheduleStatus; import org.apache.aurora.gen.ScheduledTask; import org.apache.aurora.gen.TaskEvent; import org.apache.aurora.scheduler.SchedulerModule.TaskEventBatchWorker; import org.apache.aurora.scheduler.async.DelayExecutor; import org.apache.aurora.scheduler.base.Tasks; import org.apache.aurora.scheduler.events.PubsubEvent.TaskStateChange; import org.apache.aurora.scheduler.state.StateChangeResult; import org.apache.aurora.scheduler.state.StateManager; import org.apache.aurora.scheduler.storage.entities.IScheduledTask; import org.apache.aurora.scheduler.storage.testing.StorageTestUtil; import org.easymock.Capture; import org.junit.Before; import org.junit.Test; import static org.apache.aurora.gen.ScheduleStatus.INIT; import static org.apache.aurora.gen.ScheduleStatus.PENDING; import static org.apache.aurora.gen.ScheduleStatus.RUNNING; import static org.apache.aurora.gen.ScheduleStatus.THROTTLED; import static org.apache.aurora.scheduler.testing.BatchWorkerUtil.expectBatchExecute; import static org.easymock.EasyMock.capture; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; public class TaskThrottlerTest extends EasyMockTest { private RescheduleCalculator rescheduleCalculator; private FakeClock clock; private DelayExecutor executor; private StorageTestUtil storageUtil; private StateManager stateManager; private TaskThrottler throttler; @Before public void setUp() throws Exception { rescheduleCalculator = createMock(RescheduleCalculator.class); clock = new FakeClock(); executor = createMock(DelayExecutor.class); storageUtil = new StorageTestUtil(this); storageUtil.expectOperations(); stateManager = createMock(StateManager.class); TaskEventBatchWorker batchWorker = createMock(TaskEventBatchWorker.class); expectBatchExecute(batchWorker, storageUtil.storage, control).anyTimes(); throttler = new TaskThrottler( rescheduleCalculator, clock, executor, stateManager, batchWorker); } @Test public void testIgnoresNonThrottledTasks() { control.replay(); throttler.taskChangedState(TaskStateChange.transition(makeTask("a", PENDING), INIT)); throttler.taskChangedState(TaskStateChange.transition(makeTask("a", RUNNING), PENDING)); } @Test public void testThrottledTask() { IScheduledTask task = makeTask("a", THROTTLED); long penaltyMs = 100; expect(rescheduleCalculator.getFlappingPenaltyMs(task)).andReturn(penaltyMs); Capture<Runnable> stateChangeCapture = expectThrottled(penaltyMs); expectMovedToPending(task); control.replay(); throttler.taskChangedState(TaskStateChange.transition(task, INIT)); stateChangeCapture.getValue().run(); } @Test public void testThrottledTaskReady() { // Ensures that a sane delay is used when the task's penalty was already expired when // the -> THROTTLED transition occurred (such as in the event of a scheduler failover). IScheduledTask task = makeTask("a", THROTTLED); long penaltyMs = 100; expect(rescheduleCalculator.getFlappingPenaltyMs(task)).andReturn(penaltyMs); Capture<Runnable> stateChangeCapture = expectThrottled(0); expectMovedToPending(task); control.replay(); clock.advance(Amount.of(1L, Time.HOURS)); throttler.taskChangedState(TaskStateChange.transition(task, INIT)); stateChangeCapture.getValue().run(); } private Capture<Runnable> expectThrottled(long penaltyMs) { Capture<Runnable> stateChangeCapture = createCapture(); executor.execute( capture(stateChangeCapture), eq(Amount.of(penaltyMs, Time.MILLISECONDS))); return stateChangeCapture; } private void expectMovedToPending(IScheduledTask task) { expect(stateManager.changeState( storageUtil.mutableStoreProvider, Tasks.id(task), Optional.of(THROTTLED), PENDING, Optional.absent())) .andReturn(StateChangeResult.SUCCESS); } private IScheduledTask makeTask(String id, ScheduleStatus status) { return IScheduledTask.build(new ScheduledTask() .setTaskEvents(ImmutableList.of( new TaskEvent() .setStatus(status) .setTimestamp(clock.nowMillis()))) .setStatus(status) .setAssignedTask(new AssignedTask().setTaskId(id))); } }