/*
* 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.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.mapred.PoolFairnessCalculator;
import org.apache.hadoop.mapred.PoolMetadata;
import org.apache.hadoop.mapred.ResourceMetadata;
import org.apache.hadoop.metrics.MetricsRecord;
/**
* Schedules the sessions with the various resources available.
*/
public class Scheduler {
/** Class logger */
public static final Log LOG = LogFactory.getLog(Scheduler.class);
/** Map of resource type to particular scheduler */
private final Map<ResourceType, SchedulerForType> schedulersForTypes;
/** Reloadable configuration manager */
private final ConfigManager configManager;
/** Types of resources */
private final Collection<ResourceType> types;
/** Static configuration */
private CoronaConf conf;
/**
* Primary constructor.
*
* @param nodeManager Manages the nodes and their resources
* @param sessionManager Manages a collection of sessions
* @param sessionNotifier Delivers notifications to sessions asynchronously
* @param types Types of resources
* @param metrics Cluster Manager metrics
* @param conf Static configuration
*/
public Scheduler(
NodeManager nodeManager,
SessionManager sessionManager,
SessionNotifier sessionNotifier,
Collection<ResourceType> types,
ClusterManagerMetrics metrics,
CoronaConf conf) {
this(nodeManager, sessionManager, sessionNotifier, types, metrics, conf,
new ConfigManager(types, conf));
}
/**
* Used by unit test to fake the ConfigManager
*
* @param nodeManager Manages the nodes and their resources
* @param sessionManager Manages a collection of sessions
* @param sessionNotifier Delivers notifications to sessions asynchronously
* @param types Types of resources
* @param metrics Cluster Manager metrics.
* @param configManager Manages the reloadable configuration
* @param conf Static configuration
*/
public Scheduler(
NodeManager nodeManager,
SessionManager sessionManager,
SessionNotifier sessionNotifier,
Collection<ResourceType> types,
ClusterManagerMetrics metrics,
CoronaConf conf,
ConfigManager configManager) {
this.configManager = configManager;
this.schedulersForTypes =
new EnumMap<ResourceType, SchedulerForType>(ResourceType.class);
this.types = types;
for (ResourceType type : types) {
SchedulerForType schedulerForType =
new SchedulerForType(
type,
sessionManager,
sessionNotifier,
nodeManager,
configManager,
metrics,
conf);
schedulerForType.setDaemon(true);
schedulerForType.setName("Scheduler-" + type);
schedulersForTypes.put(type, schedulerForType);
}
this.conf = conf;
}
public ConfigManager getConfigManager() {
return configManager;
}
/**
* Add a session for scheduling.
*
* @param id Identifies the session
* @param session Actual session to be scheduled
*/
public void addSession(String id, Session session) {
for (SchedulerForType scheduleThread : schedulersForTypes.values()) {
scheduleThread.addSession(id, session);
}
}
/**
* Start the scheduling as well as the reloadable configuration manager.
*/
public void start() {
for (Thread schedulerForType : schedulersForTypes.values()) {
LOG.info("Starting " + schedulerForType.getName());
schedulerForType.start();
}
configManager.start();
}
public void setConf(CoronaConf conf) {
this.conf = conf;
}
public CoronaConf getConf() {
return conf;
}
/**
* Stop the scheduling and disable the reloadable configuration manager.
*/
public void close() {
for (SchedulerForType scheduleThread : schedulersForTypes.values()) {
scheduleThread.close();
}
for (Thread scheduleThread : schedulersForTypes.values()) {
Utilities.waitThreadTermination(scheduleThread);
}
configManager.close();
}
/**
* Notify the scheduler threads that the state has changed and
* scheduling should occur.
*/
public void notifyScheduler() {
for (SchedulerForType scheduleThread : schedulersForTypes.values()) {
synchronized (scheduleThread) {
scheduleThread.notifyAll();
}
}
}
/**
* Get an unmodifiable snapshot of the mapping from pool info to associated
* metrics.
*
* @param type Type of resource.
* @return Unmodifiable snapshot of mapping from pool info to metrics
*/
public Map<PoolInfo, PoolInfoMetrics> getPoolInfoMetrics(ResourceType type) {
return schedulersForTypes.get(type).getPoolInfoMetrics();
}
/**
* Get a snapshot of the {@link PoolMetadata} objects for all the schedulers
* for each resource type. This is used for gathering metrics.
*
* @return List of snapshots for each pool and its resources
*/
private List<PoolMetadata> getPoolMetadataList() {
Map<String, PoolMetadata> poolNameMetadataMap =
new HashMap<String, PoolMetadata>();
for (Map.Entry<ResourceType, SchedulerForType> schedulerEntry :
schedulersForTypes.entrySet()) {
for (Map.Entry<PoolInfo, PoolInfoMetrics> poolEntry :
schedulerEntry.getValue().getPoolInfoMetrics().entrySet()) {
ResourceMetadata resourceMetadata =
poolEntry.getValue().getResourceMetadata();
// Ignore any invalid pool metrics
if (resourceMetadata == null) {
continue;
}
String stringifiedPoolInfo =
PoolInfo.createStringFromPoolInfo(poolEntry.getKey());
PoolMetadata poolMetadata =
poolNameMetadataMap.get(stringifiedPoolInfo);
if (poolMetadata == null) {
poolMetadata = new PoolMetadata(stringifiedPoolInfo);
poolNameMetadataMap.put(stringifiedPoolInfo, poolMetadata);
}
poolMetadata.addResourceMetadata(schedulerEntry.getKey().toString(),
resourceMetadata);
}
}
return new ArrayList<PoolMetadata>(poolNameMetadataMap.values());
}
/**
* Get a snapshot of the pool infos that is sorted.
*
* @return Sorted snapshot of the pool infos.
*/
public List<PoolInfo> getPoolInfos() {
Set<PoolInfo> poolInfos = new HashSet<PoolInfo>();
for (ResourceType type : types) {
poolInfos.addAll(getPoolInfoMetrics(type).keySet());
}
List<PoolInfo> result = new ArrayList<PoolInfo>();
result.addAll(poolInfos);
Collections.sort(result);
return result;
}
/**
* Submit the metrics.
*
* @param metricsRecord Where the metrics will be submitted
*/
public void submitMetrics(MetricsRecord metricsRecord) {
List<PoolMetadata> poolMetadatas = getPoolMetadataList();
PoolFairnessCalculator.calculateFairness(poolMetadatas, metricsRecord);
for (SchedulerForType scheduler: schedulersForTypes.values()) {
scheduler.submitMetrics();
}
}
}