package org.apache.hadoop.corona; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.PriorityQueue; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Manages sessions in a pool for a given type. */ public class PoolSchedulable extends Schedulable { public static final Log LOG = LogFactory.getLog(PoolSchedulable.class); private static final long MIN_PREEMPT_PERIOD = 60 * 1000; private final ConfigManager configManager; private final Map<String, SessionSchedulable> idToSession; private final Collection<SessionSchedulable> snapshotSessions; private Queue<SessionSchedulable> scheduleQueue; private Queue<SessionSchedulable> preemptQueue; private long lastTimeAboveMinimum; private long lastTimeAboveStarvingShare; private long lastPreemptTime; private boolean needRedistributed; private int maximum; private int minimum; private double weight; private double shareStarvingRatio; private long starvingTimeForShare; private long starvingTimeForMinimum; public PoolSchedulable(String name, String type, ConfigManager configManager) { super(name, type); this.configManager = configManager; this.scheduleQueue = null; this.preemptQueue = null; this.snapshotSessions = new ArrayList<SessionSchedulable>(); this.lastTimeAboveMinimum = -1L; this.lastTimeAboveStarvingShare = -1L; this.needRedistributed = true; // This needs to be thread safe because addSession() and getSortedSessions() // are called from different threads idToSession = new ConcurrentHashMap<String, SessionSchedulable>(); } /** * Take snapshots of this pool and the sessions within */ @Override public void snapshot() { granted = 0; requested = 0; snapshotSessions.clear(); Set<String> toBeDeleted = new HashSet<String>(); for (Entry<String, SessionSchedulable> entry : idToSession.entrySet()) { SessionSchedulable schedulable = entry.getValue(); Session session = entry.getValue().getSession(); synchronized (session) { if (session.deleted) { toBeDeleted.add(entry.getKey()); } else { schedulable.snapshot(); snapshotSessions.add(schedulable); granted += schedulable.getGranted(); requested += schedulable.getRequested(); } } } for (String id : toBeDeleted) { idToSession.remove(id); } snapshotConfig(); scheduleQueue = null; preemptQueue = null; needRedistributed = true; } private void snapshotConfig() { synchronized (configManager) { maximum = configManager.getMaximum(getName(), getType()); minimum = configManager.getMinimum(getName(), getType()); weight = configManager.getWeight(getName()); shareStarvingRatio = configManager.getShareStarvingRatio(); starvingTimeForShare = configManager.getStarvingTimeForShare(); starvingTimeForMinimum = configManager.getStarvingTimeForMinimum(); } } @Override public int getMaximum() { return maximum; } @Override public int getMinimum() { return minimum; } @Override public double getWeight() { return weight; } public boolean reachedMaximum() { return granted >= getMaximum() || granted >= requested; } public void addSession(String id, Session session) { synchronized (session) { SessionSchedulable schedulable = new SessionSchedulable(session, getType()); idToSession.put(id, schedulable); } } public Queue<SessionSchedulable> getScheduleQueue() { if (scheduleQueue == null) { scheduleQueue = createSessionQueue(configManager.getComparator(getName())); } return scheduleQueue; } public Queue<SessionSchedulable> getPreemptQueue() { if (preemptQueue == null) { ScheduleComparator comparator = configManager.getComparator(getName()) == ScheduleComparator.FIFO ? ScheduleComparator.FIFO_PREEMPT : ScheduleComparator.FAIR_PREEMPT; preemptQueue = createSessionQueue(comparator); } return preemptQueue; } public Queue<SessionSchedulable> createSessionQueue( ScheduleComparator comparator) { int initCapacity = snapshotSessions.size() == 0 ? 1 : snapshotSessions.size(); Queue<SessionSchedulable> sessionQueue = new PriorityQueue<SessionSchedulable>(initCapacity, comparator); sessionQueue.addAll(snapshotSessions); return sessionQueue; } public void distributeShare() { if (needRedistributed == true) { ScheduleComparator comparator = configManager.getComparator(getName()); Schedulable.distributeShare(getShare(), snapshotSessions, comparator); } needRedistributed = false; } public boolean isStarving(long now) { double starvingShare = getShare() * shareStarvingRatio; if (getGranted() >= starvingShare) { lastTimeAboveStarvingShare = now; } if (getGranted() >= getMinimum()) { lastTimeAboveMinimum = now; } if (now - lastPreemptTime < MIN_PREEMPT_PERIOD) { // Prevent duplicate preemption return false; } if (LOG.isDebugEnabled()) { LOG.debug("Pool:" + getName() + " lastTimeAboveMinimum:" + lastTimeAboveMinimum + " lastTimeAboveStarvingShare:" + lastTimeAboveStarvingShare + " minimumStarvingTime:" + getMinimumStarvingTime(now) + " shareStarvingTime:" + getShareStarvingTime(now) + " starvingTime:" + getStarvingTime(now)); } if (getMinimumStarvingTime(now) >= 0) { LOG.info("Pool:" + getName() + " for type:" + getType() + " is starving min:" + getMinimum() + " granted:" + getGranted()); lastPreemptTime = now; return true; } if (getShareStarvingTime(now) >= 0) { LOG.info("Pool:" + getName() + " for type:" + getType() + " is starving share:" + getShare() + " starvingRatio:" + shareStarvingRatio + " starvingShare:" + starvingShare + " granted:" + getGranted()); lastPreemptTime = now; return true; } return false; } public long getStarvingTime(long now) { long starvingTime = Math.max( getMinimumStarvingTime(now), getShareStarvingTime(now)); return starvingTime; } private long getMinimumStarvingTime(long now) { return (now - lastTimeAboveMinimum) - starvingTimeForMinimum; } private long getShareStarvingTime(long now) { return (now - lastTimeAboveStarvingShare) - starvingTimeForShare; } }