/** * 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.mapred; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import junit.extensions.TestSetup; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.apache.hadoop.mapred.FakeObjectUtilities.FakeJobTrackerMetricsInst; import org.apache.hadoop.mapred.TestTaskTrackerBlacklisting.FakeJobTracker; import org.apache.hadoop.mapred.UtilsForTests.FakeClock; import org.apache.hadoop.mapreduce.TaskType; import org.apache.hadoop.mapreduce.server.jobtracker.JTConfig; import org.apache.hadoop.mapreduce.server.jobtracker.TaskTracker; @SuppressWarnings("deprecation") public class TestJobTrackerInstrumentation extends TestCase { static String trackers[] = new String[] { "tracker_tracker1:1000", "tracker_tracker2:1000", "tracker_tracker3:1000" }; static String hosts[] = new String[] { "tracker1", "tracker2", "tracker3" }; // heartbeat responseId. increment this after sending a heartbeat private static short responseId = 1; private static FakeJobTracker jobTracker; private static FakeJobInProgress fakeJob; private static int mapSlotsPerTracker = 4; private static int reduceSlotsPerTracker = 2; private static int numMapSlotsToReserve = 2; private static int numReduceSlotsToReserve = 2; private static FakeJobTrackerMetricsInst mi; public static Test suite() { TestSetup setup = new TestSetup(new TestSuite(TestJobTrackerInstrumentation.class)) { protected void setUp() throws Exception { JobConf conf = new JobConf(); conf.set(JTConfig.JT_IPC_ADDRESS, "localhost:0"); conf.set(JTConfig.JT_HTTP_ADDRESS, "0.0.0.0:0"); conf.setInt(JTConfig.JT_MAX_TRACKER_BLACKLISTS, 1); conf.setClass(JTConfig.JT_TASK_SCHEDULER, FakeTaskScheduler.class, TaskScheduler.class); conf.set(JTConfig.JT_INSTRUMENTATION, FakeJobTrackerMetricsInst.class.getName()); jobTracker = new FakeJobTracker(conf, new FakeClock(), trackers); mi = (FakeJobTrackerMetricsInst) jobTracker.getInstrumentation(); for (String tracker : trackers) { FakeObjectUtilities.establishFirstContact(jobTracker, tracker); } } protected void tearDown() throws Exception { } }; return setup; } private TaskTrackerStatus getTTStatus(String trackerName, List<TaskStatus> taskStatuses) { return new TaskTrackerStatus(trackerName, JobInProgress.convertTrackerNameToHostName(trackerName), 0, taskStatuses, 0, mapSlotsPerTracker, reduceSlotsPerTracker); } public void testMetrics() throws Exception { TaskAttemptID[] taskAttemptID = new TaskAttemptID[3]; // create TaskTrackerStatus and send heartbeats TaskTrackerStatus[] status = new TaskTrackerStatus[trackers.length]; status[0] = getTTStatus(trackers[0], new ArrayList<TaskStatus>()); status[1] = getTTStatus(trackers[1], new ArrayList<TaskStatus>()); status[2] = getTTStatus(trackers[2], new ArrayList<TaskStatus>()); for (int i = 0; i< trackers.length; i++) { FakeObjectUtilities.sendHeartBeat(jobTracker, status[i], false, false, trackers[i], responseId); } responseId++; assertEquals("Mismatch in number of trackers", trackers.length, mi.numTrackers); int numMaps = 2; int numReds = 1; JobConf conf = new JobConf(); conf.setSpeculativeExecution(false); conf.setNumMapTasks(numMaps); conf.setNumReduceTasks(numReds); conf.setMaxTaskFailuresPerTracker(1); conf.setBoolean(JobContext.SETUP_CLEANUP_NEEDED, false); FakeJobInProgress job = new FakeJobInProgress(conf, jobTracker); assertTrue(mi.numJobsPreparing == 1); job.setClusterSize(trackers.length); job.initTasks(); jobTracker.addJob(job.getJobID(), job); taskAttemptID[0] = job.findMapTask(trackers[0]); taskAttemptID[1] = job.findMapTask(trackers[1]); taskAttemptID[2] = job.findReduceTask(trackers[2]); job.finishTask(taskAttemptID[0]); job.finishTask(taskAttemptID[1]); job.finishTask(taskAttemptID[2]); jobTracker.finalizeJob(job); assertTrue("Mismatch in map tasks launched", mi.numMapTasksLaunched == numMaps); assertTrue("Mismatch in map tasks completed", mi.numMapTasksCompleted == numMaps); assertTrue("Mismatch in map tasks failed", mi.numMapTasksFailed == 0); assertTrue("Mismatch in reduce tasks launched", mi.numReduceTasksLaunched == numReds); assertTrue("Mismatch in reduce tasks completed", mi.numReduceTasksCompleted == numReds); assertTrue("Mismatch in reduce tasks failed", mi.numReduceTasksFailed == 0); assertTrue("Mismatch in num Jobs submitted", mi.numJobsSubmitted == 1); assertTrue("Mismatch in num map slots", mi.numMapSlots == (mapSlotsPerTracker * trackers.length)); assertTrue("Mismatch in num reduce slots", mi.numReduceSlots == (reduceSlotsPerTracker * trackers.length)); assertTrue("No heartbeats were recorded, but at least one was sent.", mi.numHeartbeats > 0); } public void testBlackListing() throws IOException { int numMaps, numReds; JobConf conf = new JobConf(); conf.setSpeculativeExecution(false); conf.setMaxTaskFailuresPerTracker(1); conf.setBoolean(JobContext.SETUP_CLEANUP_NEEDED, false); TaskAttemptID[] taskAttemptID = new TaskAttemptID[3]; numMaps = 1; numReds = 1; conf.setNumMapTasks(numMaps); conf.setNumReduceTasks(numReds); conf.setBoolean(JobContext.SETUP_CLEANUP_NEEDED, false); FakeJobInProgress job1 = new FakeJobInProgress(conf, jobTracker); job1.setClusterSize(trackers.length); job1.initTasks(); jobTracker.addJob(job1.getJobID(), job1); taskAttemptID[0] = job1.findMapTask(trackers[0]); job1.failTask(taskAttemptID[0]); taskAttemptID[1] = job1.findMapTask(trackers[1]); job1.finishTask(taskAttemptID[1]); taskAttemptID[2] = job1.findReduceTask(trackers[0]); job1.failTask(taskAttemptID[2]); taskAttemptID[2] = job1.findReduceTask(trackers[2]); job1.finishTask(taskAttemptID[2]); jobTracker.finalizeJob(job1); assertEquals("Mismatch in number of failed map tasks", 1, mi.numMapTasksFailed); assertEquals("Mismatch in number of failed reduce tasks", 1, mi.numReduceTasksFailed); assertEquals("Mismatch in number of blacklisted trackers", 1, mi.numTrackersBlackListed); assertEquals("Mismatch in blacklisted map slots", mi.numBlackListedMapSlots, (mapSlotsPerTracker * mi.numTrackersBlackListed)); assertEquals("Mismatch in blacklisted reduce slots", mi.numBlackListedReduceSlots, (reduceSlotsPerTracker * mi.numTrackersBlackListed)); } public void testOccupiedSlotCounts() throws Exception { TaskTrackerStatus[] status = new TaskTrackerStatus[trackers.length]; List<TaskStatus> list = new ArrayList<TaskStatus>(); // create a map task status, which uses 2 slots. int mapSlotsPerTask = 2; TaskStatus ts = TaskStatus.createTaskStatus(true, new TaskAttemptID("jt", 1, TaskType.MAP, 0, 0), 0.0f, mapSlotsPerTask, TaskStatus.State.RUNNING, "", "", trackers[0], TaskStatus.Phase.MAP, null); list.add(ts); int mapSlotsPerTask1 = 1; ts = TaskStatus.createTaskStatus(true, new TaskAttemptID("jt", 1, TaskType.MAP, 0, 0), 0.0f, mapSlotsPerTask1, TaskStatus.State.RUNNING, "", "", trackers[0], TaskStatus.Phase.MAP, null); list.add(ts); // create a reduce task status, which uses 3 slot. int reduceSlotsPerTask = 3; ts = TaskStatus.createTaskStatus(false, new TaskAttemptID("jt", 1, TaskType.REDUCE, 0, 0), 0.0f, reduceSlotsPerTask, TaskStatus.State.RUNNING, "", "", trackers[0], TaskStatus.Phase.REDUCE, null); list.add(ts); int reduceSlotsPerTask1 = 1; ts = TaskStatus.createTaskStatus(false, new TaskAttemptID("jt", 1, TaskType.REDUCE, 0, 0), 0.0f, reduceSlotsPerTask1, TaskStatus.State.RUNNING, "", "", trackers[0], TaskStatus.Phase.REDUCE, null); list.add(ts); // create TaskTrackerStatus and send heartbeats status = new TaskTrackerStatus[trackers.length]; status[0] = getTTStatus(trackers[0], list); status[1] = getTTStatus(trackers[1], new ArrayList<TaskStatus>()); status[2] = getTTStatus(trackers[2], new ArrayList<TaskStatus>()); for (int i = 0; i< trackers.length; i++) { FakeObjectUtilities.sendHeartBeat(jobTracker, status[i], false, false, trackers[i], responseId); } responseId++; assertEquals("Mismatch in map slots occupied", mapSlotsPerTask+mapSlotsPerTask1, mi.numOccupiedMapSlots); assertEquals("Mismatch in reduce slots occupied", reduceSlotsPerTask+reduceSlotsPerTask1, mi.numOccupiedReduceSlots); assertEquals("Mismatch in num running maps", 2, mi.numRunningMaps); assertEquals("Mismatch in num running reduces", 2, mi.numRunningReduces); //now send heartbeat with no running tasks status = new TaskTrackerStatus[1]; status[0] = getTTStatus(trackers[0], new ArrayList<TaskStatus>()); FakeObjectUtilities.sendHeartBeat(jobTracker, status[0], false, false, trackers[0], responseId); assertEquals("Mismatch in map slots occupied", 0, mi.numOccupiedMapSlots); assertEquals("Mismatch in reduce slots occupied", 0, mi.numOccupiedReduceSlots); assertEquals("Mismatch in num running maps", 0, mi.numRunningMaps); assertEquals("Mismatch in num running reduces", 0, mi.numRunningReduces); } public void testReservedSlots() throws IOException { JobConf conf = new JobConf(); conf.setNumMapTasks(1); conf.setNumReduceTasks(1); conf.setSpeculativeExecution(false); //Set task tracker objects for reservation. TaskTracker tt2 = jobTracker.getTaskTracker(trackers[1]); TaskTrackerStatus status2 = new TaskTrackerStatus( trackers[1],JobInProgress.convertTrackerNameToHostName( trackers[1]),0,new ArrayList<TaskStatus>(), 0, 2, 2); tt2.setStatus(status2); fakeJob = new FakeJobInProgress(conf, jobTracker); fakeJob.setClusterSize(3); fakeJob.initTasks(); FakeObjectUtilities.sendHeartBeat(jobTracker, status2, false, true, trackers[1], responseId); responseId++; assertEquals("Mismtach in reserved map slots", numMapSlotsToReserve, mi.numReservedMapSlots); assertEquals("Mismtach in reserved red slots", numReduceSlotsToReserve, mi.numReservedReduceSlots); } public void testDecomissionedTrackers() throws IOException { // create TaskTrackerStatus and send heartbeats TaskTrackerStatus[] status = new TaskTrackerStatus[trackers.length]; status[0] = getTTStatus(trackers[0], new ArrayList<TaskStatus>()); status[1] = getTTStatus(trackers[1], new ArrayList<TaskStatus>()); status[2] = getTTStatus(trackers[2], new ArrayList<TaskStatus>()); for (int i = 0; i< trackers.length; i++) { FakeObjectUtilities.sendHeartBeat(jobTracker, status[i], false, false, trackers[i], responseId); } assertEquals("Mismatch in number of trackers", trackers.length, mi.numTrackers); Set<String> dHosts = new HashSet<String>(); dHosts.add(hosts[1]); assertEquals("Mismatch in number of decommissioned trackers", 0, mi.numTrackersDecommissioned); jobTracker.decommissionNodes(dHosts); assertEquals("Mismatch in number of decommissioned trackers", 1, mi.numTrackersDecommissioned); assertEquals("Mismatch in number of trackers", trackers.length - 1, mi.numTrackers); } public void testKillTasks() throws IOException { int numMaps, numReds; JobConf conf = new JobConf(); conf.setSpeculativeExecution(false); conf.setMaxTaskFailuresPerTracker(1); conf.setBoolean(JobContext.SETUP_CLEANUP_NEEDED, false); TaskAttemptID[] taskAttemptID = new TaskAttemptID[2]; numMaps = 1; numReds = 1; conf.setNumMapTasks(numMaps); conf.setNumReduceTasks(numReds); conf.setBoolean(JobContext.SETUP_CLEANUP_NEEDED, false); assertEquals("Mismatch in number of killed map tasks", 0, mi.numMapTasksKilled); assertEquals("Mismatch in number of killed reduce tasks", 0, mi.numReduceTasksKilled); FakeJobInProgress job1 = new FakeJobInProgress(conf, jobTracker); job1.setClusterSize(trackers.length); job1.initTasks(); jobTracker.addJob(job1.getJobID(), job1); taskAttemptID[0] = job1.findMapTask(trackers[0]); job1.killTask(taskAttemptID[0]); taskAttemptID[1] = job1.findReduceTask(trackers[0]); job1.killTask(taskAttemptID[1]); jobTracker.finalizeJob(job1); assertEquals("Mismatch in number of killed map tasks", 1, mi.numMapTasksKilled); assertEquals("Mismatch in number of killed reduce tasks", 1, mi.numReduceTasksKilled); } static class FakeTaskScheduler extends JobQueueTaskScheduler { public FakeTaskScheduler() { super(); } public List<Task> assignTasks(TaskTracker tt) { tt.reserveSlots(TaskType.MAP, fakeJob, numMapSlotsToReserve); tt.reserveSlots(TaskType.REDUCE, fakeJob, numReduceSlotsToReserve); return new ArrayList<Task>(); } } static class FakeJobInProgress extends org.apache.hadoop.mapred.TestTaskTrackerBlacklisting.FakeJobInProgress { FakeJobInProgress(JobConf jobConf, JobTracker tracker) throws IOException { super(jobConf, tracker); } @Override public synchronized void initTasks() throws IOException { super.initTasks(); jobtracker.getInstrumentation().addWaitingMaps(getJobID(), numMapTasks); jobtracker.getInstrumentation().addWaitingReduces(getJobID(), numReduceTasks); } } }