/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.hadoop.corona;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Manages pool groups and pools for a given type. Needs to be thread safe
* because addSession() and getPools() can be called from different threads.
*/
public class PoolGroupManager {
/** Default pool group */
public static final String DEFAULT_POOL_GROUP = "default";
/** Default pool */
public static final String DEFAULT_POOL = "defaultpool";
/** Default pool info */
public static final PoolInfo DEFAULT_POOL_INFO =
new PoolInfo(DEFAULT_POOL_GROUP, DEFAULT_POOL);
/** Logger */
private static final Log LOG = LogFactory.getLog(PoolGroupManager.class);
/** The resource being managed by this PoolMnanager */
private final ResourceType type;
/**
* A lookup table for the pool groups. Thread-safe for addSession() and
* getPoolGroups() by since it's a concurrent hash map and pool groups are
* never deleted.
*/
private final ConcurrentHashMap<String, PoolGroupSchedulable>
nameToPoolGroup = new ConcurrentHashMap<String, PoolGroupSchedulable>();
/** Config manager */
private final ConfigManager configManager;
/** Configuration */
private final CoronaConf conf;
/** The list of pool group snapshots used for scheduling */
private Collection<PoolGroupSchedulable> snapshotPoolGroups;
/** The queue of pool groups for scheduling */
private Queue<PoolGroupSchedulable> scheduleQueue;
/** The queue of pool groups for preemption */
private Queue<PoolGroupSchedulable> preemptQueue;
/**
* Create a PoolGroupManager for a given {@link ResourceType}
* with a given {@link ConfigManager}
* @param type the type of resource to manage
* @param configManager the configuration for this PoolManager
* @param conf Static configuration
*/
public PoolGroupManager(ResourceType type, ConfigManager configManager,
CoronaConf conf) {
this.type = type;
this.configManager = configManager;
this.conf = conf;
}
/**
* Take snapshots for all pools groups and sessions.
*/
public void snapshot() {
snapshotPoolGroups =
new ArrayList<PoolGroupSchedulable>(nameToPoolGroup.values());
for (PoolGroupSchedulable poolGroup : snapshotPoolGroups) {
poolGroup.snapshot();
}
scheduleQueue = null;
preemptQueue = null;
// Load the configured pools for stats and cm.jsp
// (needs to modify nameToPoolGroup)
Collection<PoolInfo> configuredPoolInfos =
configManager.getConfiguredPoolInfos();
if (configuredPoolInfos != null) {
for (PoolInfo poolInfo : configuredPoolInfos) {
getPoolSchedulable(poolInfo);
}
}
}
/**
* Get the queue of pool groups sorted for scheduling
* @return the queue of pools sorted for scheduling
*/
public Queue<PoolGroupSchedulable> getScheduleQueue() {
if (scheduleQueue == null) {
scheduleQueue = createPoolGroupQueue(ScheduleComparator.FAIR);
}
return scheduleQueue;
}
/**
* Get the queue of the pool groups sorted for preemption
* @return the queue of pool sorted for preemption
*/
public Queue<PoolGroupSchedulable> getPreemptQueue() {
if (preemptQueue == null) {
preemptQueue = createPoolGroupQueue(ScheduleComparator.FAIR_PREEMPT);
}
return preemptQueue;
}
/**
* Put all the pool groups into the priority queue sorted by a comparator
* @param comparator the comparator to sort all the pool groups in the queue
* @return the queue of the pool groups sorted by a comparator
*/
private Queue<PoolGroupSchedulable> createPoolGroupQueue(
ScheduleComparator comparator) {
int initCapacity =
snapshotPoolGroups.size() == 0 ? 1 : snapshotPoolGroups.size();
Queue<PoolGroupSchedulable> poolGroupQueue =
new PriorityQueue<PoolGroupSchedulable>(initCapacity, comparator);
poolGroupQueue.addAll(snapshotPoolGroups);
return poolGroupQueue;
}
/**
* Add a session to the scheduler
* @param id the id of the session
* @param session the session object to add
*/
public void addSession(String id, Session session) {
PoolInfo poolInfo = getPoolInfo(session);
LOG.info("Session " + id + " added to pool info " +
poolInfo + " (originally " + session.getInfo().getPoolInfoStrings() +") for " + type);
getPoolSchedulable(poolInfo).addSession(id, session);
}
/**
* If the cluster is set to configured pools only, do not allow unset pool
* information or pool info that doesn't match a valid pool info. Throws
* an InvalidSessionHandle exception in either of the failure cases.
*
* @param poolInfo Pool info to check
* @param configManager Configuration to check
* @param conf Corona configuration to check if configured pools
* @throws InvalidSessionHandle
*/
public static void checkPoolInfoIfStrict(PoolInfo poolInfo,
ConfigManager configManager,
CoronaConf conf)
throws InvalidSessionHandle {
if (!conf.onlyAllowConfiguredPools()) {
return;
}
// When only allowing configured pools, check the pool name to ensure
// it is a configured pool name. Not setting the pool info is also
// invalid. A legal name must be specified.
if (poolInfo == null) {
throw new InvalidSessionHandle("This cluster is operating in " +
"configured pools only mode. The pool group " +
"and pool was not specified. Please use the Corona parameter " +
CoronaConf.EXPLICIT_POOL_PROPERTY + " to set a valid poolgroup and " +
"pool in the format '<poolgroup>.<pool>'");
}
if (!configManager.isConfiguredPoolInfo(poolInfo)) {
throw new InvalidSessionHandle("This cluster is operating in " +
"configured pools only mode. The pool group " +
"and pool was specified as '" + poolInfo.getPoolGroupName() +
"." + poolInfo.getPoolName() +
"' and is not part of this cluster. " +
"Please use the Corona parameter " +
CoronaConf.EXPLICIT_POOL_PROPERTY + " to set a valid pool " +
"group and pool in the format <poolgroup>.<pool>");
}
if (!PoolInfo.isLegalPoolInfo(poolInfo)) {
throw new InvalidSessionHandle("This cluster is operating in " +
"configured pools only mode. The pool group " +
"and pool was specified as '" + poolInfo.getPoolGroupName() +
"." + poolInfo.getPoolName() +
"' and has illegal characters (Something not in " +
PoolInfo.INVALID_REGEX + "). Please use the Corona parameter " +
CoronaConf.EXPLICIT_POOL_PROPERTY + " to set a valid pool " +
"group and pool in the format <poolgroup>.<pool>");
}
}
/**
* Get the pool name for a given session, using the default pool
* information if the name is illegal. Redirection should happen prior to
* this.
*
* @param session the session to get the pool name for
* @return the pool info that the session is running in
*/
public static PoolInfo getPoolInfo(
Session session) {
PoolInfo poolInfo = session.getPoolInfo();
// If there is no explicit pool info set, take user name.
if (poolInfo == null || poolInfo.getPoolName().equals("")) {
poolInfo = new PoolInfo(DEFAULT_POOL_GROUP, session.getUserId());
}
if (!PoolInfo.isLegalPoolInfo(poolInfo)) {
LOG.warn("Illegal pool info :" + poolInfo +
" from session " + session.getSessionId());
return DEFAULT_POOL_INFO;
}
return poolInfo;
}
/**
* Get the Schedulable representing the pool with a given name
* If it doesn't exist - create it and add it to the list
*
* @param poolInfo Group and pool names
* @return the PoolSchedulable for a pool with a given name
*/
private PoolSchedulable getPoolSchedulable(PoolInfo poolInfo) {
PoolGroupSchedulable poolGroup =
nameToPoolGroup.get(poolInfo.getPoolGroupName());
if (poolGroup == null) {
poolGroup = new PoolGroupSchedulable(
poolInfo.getPoolGroupName(), type, configManager);
PoolGroupSchedulable prevPoolGroup =
nameToPoolGroup.putIfAbsent(poolInfo.getPoolGroupName(), poolGroup);
if (prevPoolGroup != null) {
poolGroup = prevPoolGroup;
}
}
return poolGroup.getPool(poolInfo);
}
/**
* Distribute the share between the pool groups
* @param total the share to distribute
*/
public void distributeShare(int total) {
Schedulable.distributeShare(
total, snapshotPoolGroups, ScheduleComparator.FAIR);
}
/**
* Get a snapshot of the pool groups that is generated from snapshot().
*
* @return An unmodifiable snapshot of the pool groups.
*/
public Collection<PoolGroupSchedulable> getPoolGroups() {
return Collections.unmodifiableCollection(snapshotPoolGroups);
}
}