/**
* 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 javax.inject.Inject;
import com.google.common.base.Optional;
import com.google.common.eventbus.Subscribe;
import org.apache.aurora.common.quantity.Amount;
import org.apache.aurora.common.quantity.Time;
import org.apache.aurora.common.stats.SlidingStats;
import org.apache.aurora.common.util.Clock;
import org.apache.aurora.scheduler.BatchWorker;
import org.apache.aurora.scheduler.SchedulerModule.TaskEventBatchWorker;
import org.apache.aurora.scheduler.async.AsyncModule.AsyncExecutor;
import org.apache.aurora.scheduler.async.DelayExecutor;
import org.apache.aurora.scheduler.base.Tasks;
import org.apache.aurora.scheduler.events.PubsubEvent.EventSubscriber;
import org.apache.aurora.scheduler.events.PubsubEvent.TaskStateChange;
import org.apache.aurora.scheduler.state.StateManager;
import static java.util.Objects.requireNonNull;
import static org.apache.aurora.gen.ScheduleStatus.PENDING;
import static org.apache.aurora.gen.ScheduleStatus.THROTTLED;
/**
* A holding area for tasks that have been throttled. Tasks entering the
* {@link org.apache.aurora.gen.ScheduleStatus#THROTTLED} state will be transitioned to
* {@link org.apache.aurora.gen.ScheduleStatus#PENDING} after the penalty period (as dictated by
* {@link RescheduleCalculator} has expired.
*/
class TaskThrottler implements EventSubscriber {
private final RescheduleCalculator rescheduleCalculator;
private final Clock clock;
private final DelayExecutor executor;
private final StateManager stateManager;
private final TaskEventBatchWorker batchWorker;
private final SlidingStats throttleStats = new SlidingStats("task_throttle", "ms");
@Inject
TaskThrottler(
RescheduleCalculator rescheduleCalculator,
Clock clock,
@AsyncExecutor DelayExecutor executor,
StateManager stateManager,
TaskEventBatchWorker batchWorker) {
this.rescheduleCalculator = requireNonNull(rescheduleCalculator);
this.clock = requireNonNull(clock);
this.executor = requireNonNull(executor);
this.stateManager = requireNonNull(stateManager);
this.batchWorker = requireNonNull(batchWorker);
}
@Subscribe
public void taskChangedState(final TaskStateChange stateChange) {
if (stateChange.getNewState() == THROTTLED) {
long readyAtMs = Tasks.getLatestEvent(stateChange.getTask()).getTimestamp()
+ rescheduleCalculator.getFlappingPenaltyMs(stateChange.getTask());
long delayMs = Math.max(0, readyAtMs - clock.nowMillis());
throttleStats.accumulate(delayMs);
executor.execute(() ->
batchWorker.execute(storeProvider -> {
stateManager.changeState(
storeProvider,
stateChange.getTaskId(),
Optional.of(THROTTLED),
PENDING,
Optional.absent());
return BatchWorker.NO_RESULT;
}),
Amount.of(delayMs, Time.MILLISECONDS));
}
}
}