package org.apache.hadoop.corona; import java.io.IOException; import java.util.List; import junit.framework.TestCase; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.mapred.ResourceTracker; import org.apache.thrift.TException; public class TestPreemption extends TestCase { final static Log LOG = LogFactory.getLog(TestPreemption.class); public final static String sessionHost = "localhost"; public static int getSessionPort(int i) { return (7000 + i); } final static String M = ResourceTracker.RESOURCE_TYPE_MAP; final static String R = ResourceTracker.RESOURCE_TYPE_REDUCE; private Configuration conf; private ClusterManagerTestable cm; private ClusterNodeInfo nodes []; private int numNodes; private SessionInfo sessionInfos []; private int numSessions; private String handles []; private Session sessions []; protected TopologyCache topologyCache; protected void setUp() throws IOException { conf = new Configuration(); conf.setClass("topology.node.switch.mapping.impl", org.apache.hadoop.net.IPv4AddressTruncationMapping.class, org.apache.hadoop.net.DNSToSwitchMapping.class); conf.set(CoronaConf.CPU_TO_RESOURCE_PARTITIONING, TstUtils.std_cpu_to_resource_partitioning); topologyCache = new TopologyCache(conf); cm = new ClusterManagerTestable(conf); numNodes = 10; nodes = new ClusterNodeInfo[numNodes]; for (int i=0; i<numNodes; i++) { nodes[i] = new ClusterNodeInfo(TstUtils.getNodeHost(i), new InetAddress(TstUtils.getNodeHost(i), TstUtils.getNodePort(i)), TstUtils.std_spec); nodes[i].setUsed(TstUtils.free_spec); } numSessions = 3; sessionInfos = new SessionInfo [numSessions]; handles = new String [numSessions]; sessions = new Session [numSessions]; for (int i =0; i<numSessions; i++) { sessionInfos[i] = new SessionInfo(new InetAddress(sessionHost, getSessionPort(i)), "s_" + i, "hadoop"); sessionInfos[i].setPriority(SessionPriority.NORMAL); sessionInfos[i].setPoolId("pool" + i); } } public void testPreemptForMinimum() throws Throwable { FakeConfigManager configManager = cm.getConfigManager(); int s1MinSlots = 60; configManager.setMinimum("pool1", M, s1MinSlots); configManager.setStarvingTimeForMinimum(200L); try { for (int i=0; i<numSessions; i++) { handles[i] = cm.sessionStart(sessionInfos[i]).handle; sessions[i] = cm.getSessionManager().getSession(handles[i]); TstUtils.reliableSleep(500); } int [] maps = {800, 100}; int [] reduces = {800, 100}; submitRequests(handles[0], maps[0], reduces[0]); verifySession(sessions[0], M, maps[0], 0); verifySession(sessions[0], R, reduces[0], 0); addAllNodes(); TstUtils.reliableSleep(100); int maxMaps = cm.getNodeManager().getMaxCpuForType(M); int maxReduces = cm.getNodeManager().getMaxCpuForType(R); verifySession(sessions[0], M, maps[0], maxMaps); verifySession(sessions[0], R, reduces[0], maxReduces); // Pool1 has a minimum of 60 for M, so it preempts 60 slots submitRequests(handles[1], maps[1], reduces[1]); TstUtils.reliableSleep(SchedulerForType.PREEMPTION_PERIOD * 2); verifySession(sessions[0], M, maps[0], maxMaps - s1MinSlots); verifySession(sessions[0], R, reduces[0], maxReduces); verifySession(sessions[1], M, maps[1], s1MinSlots); verifySession(sessions[1], R, reduces[1], 0); for (int i = 0; i < numSessions; i++) { cm.sessionEnd(handles[i], SessionStatus.SUCCESSFUL); } } catch (InvalidSessionHandle e) { LOG.error("Bad Session Handle"); assertEquals("Bad Session Handle", null); } catch (Throwable t) { t.printStackTrace(); throw t; } } public void testPreemptForShare() throws Throwable { FakeConfigManager configManager = cm.getConfigManager(); configManager.setShareStarvingRatio(0.5); configManager.setStarvingTimeForShare(200L); try { for (int i=0; i<numSessions; i++) { handles[i] = cm.sessionStart(sessionInfos[i]).handle; sessions[i] = cm.getSessionManager().getSession(handles[i]); TstUtils.reliableSleep(500); } int [] maps = {800, 100}; int [] reduces = {800, 100}; submitRequests(handles[0], maps[0], reduces[0]); verifySession(sessions[0], M, maps[0], 0); verifySession(sessions[0], R, reduces[0], 0); addAllNodes(); TstUtils.reliableSleep(100); int maxMaps = cm.getNodeManager().getMaxCpuForType(M); int maxReduces = cm.getNodeManager().getMaxCpuForType(R); verifySession(sessions[0], M, maps[0], maxMaps); verifySession(sessions[0], R, reduces[0], maxReduces); // Pool1 will starving for share. So it preempt half of M and R slots submitRequests(handles[1], maps[1], reduces[1]); TstUtils.reliableSleep(SchedulerForType.PREEMPTION_PERIOD * 2); verifySession(sessions[0], M, maps[0], maxMaps / 2); verifySession(sessions[0], R, reduces[0], maxReduces / 2); verifySession(sessions[1], M, maps[1], maxMaps / 2); verifySession(sessions[1], R, reduces[1], maxReduces / 2); for (int i = 0; i < numSessions; i++) { cm.sessionEnd(handles[i], SessionStatus.SUCCESSFUL); } } catch (InvalidSessionHandle e) { LOG.error("Bad Session Handle"); assertEquals("Bad Session Handle", null); } catch (Throwable t) { t.printStackTrace(); throw t; } } private void submitRequests(String handle, int maps, int reduces) throws TException, InvalidSessionHandle { List<ResourceRequest> requests = TstUtils.createRequests(this.numNodes, maps, reduces); cm.requestResource(handle, requests); } private void verifySession(Session session, String type, int request, int grant) { synchronized (session) { assertEquals(grant, session.getGrantCountForType(type)); assertEquals(request, session.getRequestCountForType(type)); assertEquals(request - grant, session.getPendingRequestForType(type).size()); } } private void addSomeNodes(int count) throws TException { for (int i=0; i<count; i++) { cm.nodeHeartbeat(nodes[i]); } } private void addAllNodes() throws TException { addSomeNodes(this.numNodes); } }