/**
* 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.updater;
import java.util.Set;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import org.apache.aurora.scheduler.base.TaskGroupKey;
import org.apache.aurora.scheduler.preemptor.BiCache;
import org.apache.aurora.scheduler.storage.entities.IInstanceKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.util.Objects.requireNonNull;
/**
* Reserves agents for instances being updated. Multiple instance keys can be registered against
* a single agent.
*/
public interface UpdateAgentReserver {
/**
* Reserves the agent id for the given key. Should behave like a multi-map under the hood.
*
* @param agentId The agent id to reserve.
* @param key The instance key that will use the reservation.
*/
void reserve(String agentId, IInstanceKey key);
/**
* Releases the reservation on an agent id for the given key.
*
* @param agentId The agent id to release the reservation on.
* @param key The instance key that should be removed.
*/
void release(String agentId, IInstanceKey key);
/**
* Returns the agent id associated with the given instance key.
*
* @param key The instance key to look up.
* @return An optional agent id string.
*/
Optional<String> getAgent(IInstanceKey key);
/**
* Get all reservations for a given agent id. Useful for skipping over the agent between the
* reserve/release window.
*
* @param agentId The agent id to look up reservations for.
* @return A set of keys reserved for that agent.
*/
Set<IInstanceKey> getReservations(String agentId);
/**
* Check if the agent reserver has any reservations for the provided key.
*
* @param groupKey The key to check.
* @return True if there are reservations against any instances in that key.
*/
boolean hasReservations(TaskGroupKey groupKey);
/**
* Implementation of the update reserver backed by a BiCache (the same mechanism we use for
* preemption). This means it will expire reservations that haven't been explicitly released
* after the configured timeout.
*/
class UpdateAgentReserverImpl implements UpdateAgentReserver {
private static final Logger LOG = LoggerFactory.getLogger(UpdateAgentReserverImpl.class);
private final BiCache<IInstanceKey, String> cache;
@Inject
UpdateAgentReserverImpl(BiCache<IInstanceKey, String> cache) {
this.cache = requireNonNull(cache);
}
public void reserve(String agentId, IInstanceKey key) {
LOG.info("Reserving {} for {}", agentId, key);
cache.put(key, agentId);
}
public void release(String agentId, IInstanceKey key) {
LOG.info("Releasing reservation on {} for {}", agentId, key);
cache.remove(key, agentId);
}
public Set<IInstanceKey> getReservations(String agentId) {
return cache.getByValue(agentId);
}
@Override
public boolean hasReservations(TaskGroupKey groupKey) {
return cache.asMap().entrySet().stream()
.filter(entry -> entry.getKey().getJobKey().equals(groupKey.getTask().getJob()))
.findFirst()
.isPresent();
}
@Override
public Optional<String> getAgent(IInstanceKey key) {
return cache.get(key);
}
}
/**
* Used to effectively disable reservations.
*/
class NullAgentReserver implements UpdateAgentReserver {
@Override
public void reserve(String agentId, IInstanceKey key) {
// noop
}
@Override
public void release(String agentId, IInstanceKey key) {
// noop
}
@Override
public Optional<String> getAgent(IInstanceKey key) {
return Optional.absent();
}
@Override
public Set<IInstanceKey> getReservations(String agentId) {
return ImmutableSet.of();
}
@Override
public boolean hasReservations(TaskGroupKey groupKey) {
return false;
}
}
}