/**
* 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.preemptor;
import java.util.Iterator;
import javax.inject.Inject;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import org.apache.aurora.scheduler.base.TaskGroupKey;
import org.apache.aurora.scheduler.filter.AttributeAggregate;
import org.apache.aurora.scheduler.offers.OfferManager;
import org.apache.aurora.scheduler.state.StateManager;
import org.apache.aurora.scheduler.storage.Storage.MutableStoreProvider;
import org.apache.aurora.scheduler.storage.entities.IAssignedTask;
import org.apache.mesos.v1.Protos.AgentID;
import static java.util.Objects.requireNonNull;
import static org.apache.aurora.gen.ScheduleStatus.PREEMPTING;
/**
* Attempts to preempt active tasks in favor of the provided PENDING task in case a preemption
* slot has been previously found.
*/
public interface Preemptor {
/**
* Preempts victim tasks in case a valid preemption slot exists.
*
* @param task Preempting task.
* @param jobState Current job state aggregate.
* @param storeProvider Store provider to use for task preemption.
* @return ID of the slave where preemption occurred.
*/
Optional<String> attemptPreemptionFor(
IAssignedTask task,
AttributeAggregate jobState,
MutableStoreProvider storeProvider);
class PreemptorImpl implements Preemptor {
private final StateManager stateManager;
private final OfferManager offerManager;
private final PreemptionVictimFilter preemptionVictimFilter;
private final PreemptorMetrics metrics;
private final BiCache<PreemptionProposal, TaskGroupKey> slotCache;
@Inject
PreemptorImpl(
StateManager stateManager,
OfferManager offerManager,
PreemptionVictimFilter preemptionVictimFilter,
PreemptorMetrics metrics,
BiCache<PreemptionProposal, TaskGroupKey> slotCache) {
this.stateManager = requireNonNull(stateManager);
this.offerManager = requireNonNull(offerManager);
this.preemptionVictimFilter = requireNonNull(preemptionVictimFilter);
this.metrics = requireNonNull(metrics);
this.slotCache = requireNonNull(slotCache);
}
@Override
public Optional<String> attemptPreemptionFor(
IAssignedTask pendingTask,
AttributeAggregate jobState,
MutableStoreProvider store) {
TaskGroupKey groupKey = TaskGroupKey.from(pendingTask.getTask());
Iterator<PreemptionProposal> proposalIterator = slotCache.getByValue(groupKey).iterator();
// A preemption slot is available -> attempt to preempt tasks.
while (proposalIterator.hasNext()) {
// Get the next available preemption slot.
PreemptionProposal slot = proposalIterator.next();
slotCache.remove(slot, groupKey);
// Validate PreemptionProposal is still valid for the given task.
AgentID slaveId = AgentID.newBuilder().setValue(slot.getSlaveId()).build();
Optional<ImmutableSet<PreemptionVictim>> validatedVictims =
preemptionVictimFilter.filterPreemptionVictims(
pendingTask.getTask(),
slot.getVictims(),
jobState,
offerManager.getOffer(slaveId),
store);
metrics.recordSlotValidationResult(validatedVictims);
if (validatedVictims.isPresent()) {
for (PreemptionVictim toPreempt : validatedVictims.get()) {
metrics.recordTaskPreemption(toPreempt);
stateManager.changeState(
store,
toPreempt.getTaskId(),
Optional.absent(),
PREEMPTING,
Optional.of("Preempting in favor of " + pendingTask.getTaskId()));
}
return Optional.of(slot.getSlaveId());
}
}
return Optional.absent();
}
}
}