Package org.apache.hadoop.mapred

Source Code of org.apache.hadoop.mapred.CoronaJobInProgress$JobSummary

/**
* 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.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.corona.SessionPriority;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapred.Counters;
import org.apache.hadoop.mapred.TaskStatus;
import org.apache.hadoop.mapred.JobHistory.Values;
import org.apache.hadoop.mapred.JobInProgress.Counter;
import org.apache.hadoop.mapred.TaskStatus.Phase;
import org.apache.hadoop.mapreduce.TaskType;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.net.NetworkTopology;
import org.apache.hadoop.net.Node;
import org.apache.hadoop.net.TopologyCache;
import org.apache.hadoop.util.StringUtils;


public class CoronaJobInProgress extends JobInProgressTraits {
  static final Log LOG = LogFactory.getLog(CoronaJobInProgress.class);

  Clock clock;

  @SuppressWarnings("deprecation")
  JobID jobId;
  SessionPriority priority;
  JobProfile profile;
  JobStatus status;
  private final JobStats jobStats = new JobStats();
  long startTime;
  long launchTime;
  long finishTime;
  long deadline;

  String user;
  @SuppressWarnings("deprecation")
  JobConf jobConf;

  Path jobFile;  // non-local
  Path localJobFile;

  // XXX: Do not limit number of tasks ourselves. Let Cluster Manager handle it.
  int maxTasks = 0;
  int numMapTasks;
  int numReduceTasks;
  long memoryPerMap;
  long memoryPerReduce;
  volatile int numSlotsPerMap = 1;
  volatile int numSlotsPerReduce = 1;
  List<TaskCompletionEvent> taskCompletionEvents;
  private int taskCompletionEventCounter = 0;
  static final float DEFAULT_COMPLETED_MAPS_PERCENT_FOR_REDUCE_SLOWSTART = 0.05f;
  public static final String SPECULATIVE_MAP_UNFINISHED_THRESHOLD_KEY =
      "mapred.map.tasks.speculation.unfinished.threshold";
  public static final String SPECULATIVE_REDUCE_UNFINISHED_THRESHOLD_KEY =
      "mapred.reduce.tasks.speculation.unfinished.threshold";

  static final int NUM_SLOTS_PER_MAP = 1;
  static final int NUM_SLOTS_PER_REDUCE = 1;

  private final boolean jobSetupCleanupNeeded;
  private final boolean jobFinishWhenReducesDone;
  private final boolean taskCleanupNeeded;
  private volatile boolean launchedSetup = false;
  private volatile boolean tasksInited = false;
  private final AtomicBoolean reduceResourcesRequested = new AtomicBoolean(false);
  private volatile boolean launchedCleanup = false;
  private volatile boolean jobKilled = false;
  private volatile boolean jobFailed = false;

  String[][] mapLocations;  // Has an array of locations for each map.
  List<TaskInProgress> nonRunningMaps;
  List<TaskInProgress> nonRunningReduces = new LinkedList<TaskInProgress>();
  int runningMapTasks = 0;
  int runningReduceTasks = 0;
  int failedMapTasks = 0// includes killed
  int failedReduceTasks = 0// includes killed
  int killedMapTasks = 0;
  int killedReduceTasks = 0;
  int failedMapTIPs = 0;
  int failedReduceTIPs = 0;
  int finishedMapTasks = 0;
  int finishedReduceTasks = 0;
  int completedMapsForReduceSlowstart = 0;
  int rushReduceReduces = 5;
  int rushReduceMaps = 5;
  float speculativeMapUnfininshedThreshold = 0.001F;
  float speculativeReduceUnfininshedThreshold = 0.001F;
  float speculativeMapLogRateThreshold = 0.001F;
  int speculativeMapLogNumThreshold = 3;
  float speculativeReduceLogRateThreshold = 0.001F;
  int speculativeReduceLogNumThreshold = 3;

  Set<TaskInProgress> runningMaps = new LinkedHashSet<TaskInProgress>();
  Set<TaskInProgress> runningReduces = new LinkedHashSet<TaskInProgress>();
  @SuppressWarnings("deprecation")
  List<TaskAttemptID> mapCleanupTasks = new LinkedList<TaskAttemptID>();
  @SuppressWarnings("deprecation")
  List<TaskAttemptID> reduceCleanupTasks = new LinkedList<TaskAttemptID>();

  int maxLevel;
  int anyCacheLevel;
  private final LocalityStats localityStats;
  private final Thread localityStatsThread;

  // runningMapTasks include speculative tasks, so we need to capture
  // speculative tasks separately
  int speculativeMapTasks = 0;
  int speculativeReduceTasks = 0;

  int mapFailuresPercent = 0;
  int reduceFailuresPercent = 0;
  @SuppressWarnings("deprecation")
  Counters jobCounters = new Counters();

  TaskErrorCollector taskErrorCollector;

  // Maximum no. of fetch-failure notifications after which
  // the map task is killed
  private static final int MAX_FETCH_FAILURES_NOTIFICATIONS = 3;
  private static final int MAX_FETCH_FAILURES_PER_MAP_DEFAULT = 50;
  private static final String MAX_FETCH_FAILURES_PER_MAP_KEY =
     "mapred.job.per.map.maxfetchfailures";
  private int maxFetchFailuresPerMapper;

  // The key for the property holding the job deadline value
  private static final String JOB_DEADLINE_KEY = "mapred.job.deadline";
  // The default value of the job deadline property
  private static final long JOB_DEADLINE_DEFAULT_VALUE = 0L;
  // The key for the property holding the job priority
  private static final String SESSION_PRIORITY_KEY = "mapred.job.priority";
  // The default value of the job priority
  private static final String SESSION_PRIORITY_DEFAULT = "NORMAL";
  // The maximum percentage of fetch failures allowed for a map
  private static final double MAX_ALLOWED_FETCH_FAILURES_PERCENT = 0.5;
  // Map of mapTaskId -> no. of fetch failures
  private final Map<TaskAttemptID, Integer> mapTaskIdToFetchFailuresMap =
    new TreeMap<TaskAttemptID, Integer>();

  // Don't lower speculativeCap below one TT's worth (for small clusters)
  private static final int MIN_SPEC_CAP = 10;
  public static final String SPECULATIVE_SLOWTASK_THRESHOLD =
    "mapreduce.job.speculative.slowtaskthreshold";
  public static final String RUSH_REDUCER_MAP_THRESHOLD =
    "mapred.job.rushreduce.map.threshold";
  public static final String RUSH_REDUCER_REDUCE_THRESHOLD =
    "mapred.job.rushreduce.reduce.threshold";
  public static final String SPECULATIVECAP =
    "mapreduce.job.speculative.speculativecap";
  public static final String SPECULATIVE_SLOWNODE_THRESHOLD =
    "mapreduce.job.speculative.slownodethreshold";
  public static final String SPECULATIVE_REFRESH_TIMEOUT =
    "mapreduce.job.speculative.refresh.timeout";
  public static final String LOG_CANNOTSPECULATIVE_INTERVAL =
    "mapreduce.job.log.cannotspeculative.interval";
  public static final String SPECULATIVE_STDDEVMEANRATIO_MAX =
    "mapreduce.job.speculative.stddevmeanratio.max";

  // thresholds for speculative execution
  float slowTaskThreshold;
  float speculativeCap;
  float slowNodeThreshold;
  private long lastSpeculativeMapRefresh, lastSpeculativeReduceRefresh;
  private long lastTimeCannotspeculativeMapLog, lastTimeCannotspeculativeReduceLog;
  private final long speculativeRefreshTimeout;
  private final long logCannotspeculativeInterval;
  private final float speculativeStddevMeanRatioMax;
  volatile private boolean hasSpeculativeMaps;
  volatile private boolean hasSpeculativeReduces;
  private List<TaskInProgress> candidateSpeculativeMaps, candidateSpeculativeReduces;
  //Statistics are maintained for a couple of things
  //mapTaskStats is used for maintaining statistics about
  //the completion time of map tasks on the trackers. On a per
  //tracker basis, the mean time for task completion is maintained
  private final DataStatistics mapTaskStats = new DataStatistics();
  //reduceTaskStats is used for maintaining statistics about
  //the completion time of reduce tasks on the trackers. On a per
  //tracker basis, the mean time for task completion is maintained
  private final DataStatistics reduceTaskStats = new DataStatistics();
  //trackerMapStats used to maintain a mapping from the tracker to the
  //the statistics about completion time of map tasks
  private final Map<String,DataStatistics> trackerMapStats =
    new HashMap<String,DataStatistics>();
  //trackerReduceStats used to maintain a mapping from the tracker to the
  //the statistics about completion time of reduce tasks
  private final Map<String,DataStatistics> trackerReduceStats =
    new HashMap<String,DataStatistics>();
  //runningMapStats used to maintain the RUNNING map tasks' statistics
  private final DataStatistics runningMapTaskStats = new DataStatistics();
  //runningReduceStats used to maintain the RUNNING reduce tasks' statistics
  private final DataStatistics runningReduceTaskStats = new DataStatistics();
  private static final String JOB_KILLED_REASON = "Job killed";
  private static final String EMPTY_TRACKER_NAME =
    "tracker_:localhost.localdomain/127.0.0.1:";


  protected CoronaJobTracker.TaskLookupTable taskLookupTable;
  private final TaskStateChangeListener taskStateChangeListener;

  private final Object lockObject;
  private final CoronaJobHistory jobHistory;
 
  private String jobTrackerId;

  private int terminated = -1;


  @SuppressWarnings("deprecation")
  public CoronaJobInProgress(
    Object lockObject,
    JobID jobId, Path systemDir, JobConf jobConf,
    CoronaJobTracker.TaskLookupTable taskLookupTable,
    TaskStateChangeListener taskStateChangeListener,
    TopologyCache topologyCache,
    CoronaJobHistory jobHistory, String url, String jobTrackerId)
    throws IOException {
    this.lockObject = lockObject;
    this.clock = JobTracker.getClock();

    this.jobId = jobId;
    this.jobConf = jobConf;
    this.taskLookupTable = taskLookupTable;
    this.taskStateChangeListener = taskStateChangeListener;
    this.jobHistory = jobHistory;
    this.jobTrackerId = jobTrackerId;

    // Status.
    this.startTime = clock.getTime();
    this.status = new JobStatus(jobId, 0.0f, 0.0f, JobStatus.PREP);
    status.setStartTime(startTime);

    // Job file.
    this.jobFile = getJobFile(systemDir, jobId);

    this.user = jobConf.getUser();
    this.profile = new JobProfile(
      user, jobId, jobFile.toString(), url, jobConf.getJobName(),
      jobConf.getQueueName());


    this.numMapTasks = jobConf.getNumMapTasks();
    this.numReduceTasks = jobConf.getNumReduceTasks();
    this.memoryPerMap = jobConf.getMemoryForMapTask();
    this.memoryPerReduce = jobConf.getMemoryForReduceTask();
    this.taskCompletionEvents = new ArrayList<TaskCompletionEvent>
       (numMapTasks + numReduceTasks + 10);
    this.jobSetupCleanupNeeded = jobConf.getJobSetupCleanupNeeded();
    this.jobFinishWhenReducesDone = jobConf.getJobFinishWhenReducesDone();
    this.taskCleanupNeeded = jobConf.getTaskCleanupNeeded();

    this.mapFailuresPercent = jobConf.getMaxMapTaskFailuresPercent();
    this.reduceFailuresPercent = jobConf.getMaxReduceTaskFailuresPercent();

    this.maxLevel = jobConf.getInt("mapred.task.cache.levels",
        NetworkTopology.DEFAULT_HOST_LEVEL);
    this.anyCacheLevel = this.maxLevel+1;
    this.localityStats = new LocalityStats(
      jobConf, maxLevel, jobCounters, jobStats, topologyCache);
    localityStatsThread = new Thread(localityStats);
    localityStatsThread.setName("Locality Stats");
    localityStatsThread.setDaemon(true);
    localityStatsThread.start();

    this.taskErrorCollector = new TaskErrorCollector(
      jobConf, Integer.MAX_VALUE, 1);

    this.slowTaskThreshold = Math.max(0.0f,
        jobConf.getFloat(CoronaJobInProgress.SPECULATIVE_SLOWTASK_THRESHOLD,1.0f));
    this.speculativeCap = jobConf.getFloat(
        CoronaJobInProgress.SPECULATIVECAP,0.1f);
    this.slowNodeThreshold = jobConf.getFloat(
        CoronaJobInProgress.SPECULATIVE_SLOWNODE_THRESHOLD,1.0f);
    this.speculativeRefreshTimeout = jobConf.getLong(
        CoronaJobInProgress.SPECULATIVE_REFRESH_TIMEOUT, 5000L);
    this.logCannotspeculativeInterval = jobConf.getLong(
        CoronaJobInProgress.LOG_CANNOTSPECULATIVE_INTERVAL, 150000L);
    this.speculativeStddevMeanRatioMax = jobConf.getFloat(
        CoronaJobInProgress.SPECULATIVE_STDDEVMEANRATIO_MAX, 0.33f);

    this.speculativeMapUnfininshedThreshold = jobConf.getFloat(
        CoronaJobInProgress.SPECULATIVE_MAP_UNFINISHED_THRESHOLD_KEY,
        speculativeMapUnfininshedThreshold);
    this.speculativeReduceUnfininshedThreshold = jobConf.getFloat(
        CoronaJobInProgress.SPECULATIVE_REDUCE_UNFINISHED_THRESHOLD_KEY,
        speculativeReduceUnfininshedThreshold);
    this.deadline = jobConf.getLong(JOB_DEADLINE_KEY,
        JOB_DEADLINE_DEFAULT_VALUE);


    this.priority = SessionPriority.valueOf(
        jobConf.get(SESSION_PRIORITY_KEY, SESSION_PRIORITY_DEFAULT));
    hasSpeculativeMaps = jobConf.getMapSpeculativeExecution();
    hasSpeculativeReduces = jobConf.getReduceSpeculativeExecution();
    LOG.info(jobId + ": hasSpeculativeMaps = " + hasSpeculativeMaps +
             ", hasSpeculativeReduces = " + hasSpeculativeReduces);
  }

  public JobStats getJobStats() {
    return jobStats;
  }

  @Override
  public String getUser() { return user; }

  public JobProfile getProfile() {  return profile; }

  @Override
  public JobStatus getStatus() { return status; }

  public boolean isSetupCleanupRequired() { return jobSetupCleanupNeeded; }

  public SessionPriority getPriority() {
    return this.priority;
  }
 
  public void setPriority(SessionPriority priority) {
    this.priority = priority;
  }

  @Override
  DataStatistics getRunningTaskStatistics(boolean isMap) {
    if (isMap) {
      return runningMapTaskStats;
    } else {
      return runningReduceTaskStats;
    }
  }

  @Override
  public float getSlowTaskThreshold() { return slowTaskThreshold; }

  @Override
  public float getStddevMeanRatioMax() { return speculativeStddevMeanRatioMax; }

  @Override
  public int getNumRestarts() { return 0; }

  public long getLaunchTime() { return launchTime; }

  public long getStartTime() { return startTime; }

  public long getFinishTime() { return finishTime; }

  public long getJobDeadline() { return deadline; }

  public int getNumMapTasks() { return numMapTasks; }

  public int getNumReduceTasks() { return numReduceTasks; }

  @SuppressWarnings("deprecation")
  public Counters getJobCounters() { return jobCounters; }

  /**
   *  Returns map phase counters by summing over all map tasks in progress.
   */
  @SuppressWarnings("deprecation")
  public Counters getMapCounters() {
    synchronized (lockObject) {
      return incrementTaskCountersUnprotected(new Counters(), maps);
    }
  }

  /**
   *  Returns map phase counters by summing over all map tasks in progress.
   */
  @SuppressWarnings("deprecation")
  public Counters getReduceCounters() {
    synchronized (lockObject) {
      return incrementTaskCountersUnprotected(new Counters(), reduces);
    }
  }

  /**
   * Get all the tasks of the desired type in this job.
   */
  TaskInProgress[] getTasks(TaskType type) {
    TaskInProgress[] tasks = null;
    switch (type) {
    case MAP:
      tasks = maps;
      break;
    case REDUCE:
      tasks = reduces;
      break;
    case JOB_SETUP:
      tasks = setup;
      break;
    case JOB_CLEANUP:
      tasks = cleanup;
      break;
    default:
      tasks = new TaskInProgress[0];
      break;
    }

    return tasks;
  }

  public TaskCompletionEvent[] getTaskCompletionEvents(
        int fromEventId, int maxEvents) {
    TaskCompletionEvent[] events = TaskCompletionEvent.EMPTY_ARRAY;
    synchronized (lockObject) {
      if (!tasksInited) {
        return events;
      }
      if (taskCompletionEvents.size() > fromEventId) {
        int actualMax = Math.min(maxEvents,
                                 (taskCompletionEvents.size() - fromEventId));
        events = taskCompletionEvents.subList(fromEventId,
          actualMax + fromEventId).toArray(events);
      }
      return events;
    }
  }

  public int getTaskCompletionEventsSize() {
    synchronized (lockObject) {
      return taskCompletionEvents.size();
    }
  }

  boolean fetchFailureNotification(
        TaskAttemptID reportingAttempt,
        TaskInProgress tip, TaskAttemptID mapAttemptId, String trackerName) {
    jobStats.incNumMapFetchFailures();
    synchronized (lockObject) {
      Integer fetchFailures = mapTaskIdToFetchFailuresMap.get(mapAttemptId);
      fetchFailures = (fetchFailures == null) ? 1 : (fetchFailures + 1);
      mapTaskIdToFetchFailuresMap.put(mapAttemptId, fetchFailures);
      LOG.info("Failed fetch notification #" + fetchFailures + " by " + reportingAttempt +
          " for task " + mapAttemptId + " tracker " + trackerName);

      float failureRate = (float)fetchFailures / runningReduceTasks;
      // declare faulty if fetch-failures >= max-allowed-failures
      final boolean isMapFaulty = (failureRate >= MAX_ALLOWED_FETCH_FAILURES_PERCENT) ||
          fetchFailures > maxFetchFailuresPerMapper;
      if (fetchFailures >= MAX_FETCH_FAILURES_NOTIFICATIONS && isMapFaulty) {
        String reason = "Too many fetch-failures (" + fetchFailures + ") at " +
          new Date();
        LOG.info(reason + " for " + mapAttemptId + " ... killing it");

        final boolean isFailed = true;
        TaskTrackerInfo ttStatus = null;
        jobStats.incNumMapTasksFailedByFetchFailures();
        failedTask(tip, mapAttemptId, reason,
            (tip.isMapTask() ? TaskStatus.Phase.MAP : TaskStatus.Phase.REDUCE),
            isFailed, trackerName, ttStatus);

        mapTaskIdToFetchFailuresMap.remove(mapAttemptId);
        return true;
      }
    }
    return false;
  }

  @SuppressWarnings("deprecation")
  public static Path getJobFile(Path systemDir, JobID jobId) {
    Path systemDirForJob = new Path(systemDir, jobId.toString());
    return new Path(systemDirForJob, "job.xml");
  }

  public int getMaxTasksPerJob() {
    return 0; // No maximum.
  }

  public void close() {
    localityStats.stop();
    localityStatsThread.interrupt();
    // The thread may be stuck in DNS lookups. This thread is a daemon,
    // so it will not prevent process exit.
    try {
      localityStatsThread.join(1000);
    } catch (InterruptedException e) {
      LOG.warn("localityStatsThread.join interrupted");
    }
  }

  /**
   * Read input splits and create a map per split.
   */
  public void initTasks() throws IOException {
    // log job info
    jobHistory.logSubmitted(jobFile.toString(), this.startTime, this.jobTrackerId);
    // log the job priority
    JobClient.RawSplit[] splits = null;
    splits = JobClient.getAndRemoveCachedSplits(jobId);
    if (splits == null) {
      FileSystem fs = jobFile.getFileSystem(jobConf);
      Path splitFile = new Path(jobFile.getParent(), "job.split");
      LOG.info("Reading splits from " + splitFile);
      DataInputStream splitFileIn = fs.open(splitFile);
      try {
        splits = JobClient.readSplitFile(splitFileIn);
      } finally {
        splitFileIn.close();
      }
    }
    initTasksFromSplits(splits);
    jobHistory.logInited(this.launchTime,
                         numMapTasks, numReduceTasks);
  }

  /**
   * Used by test code.
   */
  void initTasksFromSplits(JobClient.RawSplit[] splits) throws IOException {
    synchronized (lockObject) {
      initTasksFromSplitsUnprotected(splits);
    }
  }

  private void initTasksFromSplitsUnprotected(JobClient.RawSplit[] splits)
      throws IOException {
    String jobFile = profile.getJobFile();

    numMapTasks = splits.length;
    if (maxTasks > 0 && numMapTasks + numReduceTasks > maxTasks) {
      throw new IOException(
                "The number of tasks for this job " +
                (numMapTasks + numReduceTasks) +
                " exceeds the configured limit " + maxTasks);
    }

    long inputLength = 0;
    maps = new TaskInProgress[numMapTasks];
    mapLocations = new String[numMapTasks][];
    nonRunningMaps = new ArrayList<TaskInProgress>(numMapTasks);
    for(int i = 0; i < numMapTasks; i++) {
      inputLength += splits[i].getDataLength();
      maps[i] = new TaskInProgress(jobId, jobFile, splits[i],
                                 jobConf, this, i, 1); // numSlotsPerMap = 1
      nonRunningMaps.add(maps[i]);
      mapLocations[i] = splits[i].getLocations();
    }
    LOG.info("Input size for job " + jobId + " = " + inputLength
        + ". Number of splits = " + splits.length);

    this.launchTime = clock.getTime();
    LOG.info("Number of splits for job " + jobId + " = " + splits.length);

    // Create reduce tasks
    this.reduces = new TaskInProgress[numReduceTasks];
    for (int i = 0; i < numReduceTasks; i++) {
      reduces[i] = new TaskInProgress(jobId, jobFile, numMapTasks, i,
                                  jobConf, this, 1); // numSlotsPerReduce = 1
      nonRunningReduces.add(reduces[i]);
    }
    LOG.info("Number of reduces for job " + jobId + " = " + reduces.length);

    // Calculate the minimum number of maps to be complete before
    // we should start scheduling reduces
    completedMapsForReduceSlowstart =
      (int)Math.ceil(
          (jobConf.getFloat("mapred.reduce.slowstart.completed.maps",
                         DEFAULT_COMPLETED_MAPS_PERCENT_FOR_REDUCE_SLOWSTART) *
           numMapTasks));
    // The thresholds of total maps and reduces for scheduling reducers
    // immediately.
    rushReduceMaps =
      jobConf.getInt(RUSH_REDUCER_MAP_THRESHOLD, rushReduceMaps);
    rushReduceReduces =
      jobConf.getInt(RUSH_REDUCER_REDUCE_THRESHOLD, rushReduceReduces);
    maxFetchFailuresPerMapper = jobConf.getInt(MAX_FETCH_FAILURES_PER_MAP_KEY,
      MAX_FETCH_FAILURES_PER_MAP_DEFAULT);

    // Proceed to Setup/Cleanup.
    if (jobSetupCleanupNeeded) {
      // create cleanup two cleanup tips, one map and one reduce.
      cleanup = new TaskInProgress[2];

      // cleanup map tip. This map doesn't use any splits. Just assign an empty
      // split.
      JobClient.RawSplit emptySplit = new JobClient.RawSplit();
      cleanup[0] = new TaskInProgress(jobId, jobFile, emptySplit,
              jobConf, this, numMapTasks, 1);
      cleanup[0].setJobCleanupTask();

      // cleanup reduce tip.
      cleanup[1] = new TaskInProgress(jobId, jobFile, numMapTasks,
                         numReduceTasks, jobConf, this, 1);
      cleanup[1].setJobCleanupTask();

      // create two setup tips, one map and one reduce.
      setup = new TaskInProgress[2];

      // setup map tip. This map doesn't use any split. Just assign an empty
      // split.
      setup[0] = new TaskInProgress(jobId, jobFile, emptySplit,
              jobConf, this, numMapTasks + 1, 1);
      setup[0].setJobSetupTask();

      // setup reduce tip.
      setup[1] = new TaskInProgress(jobId, jobFile, numMapTasks,
                         numReduceTasks + 1, jobConf, this, 1);
      setup[1].setJobSetupTask();
    }
    tasksInited = true;
  }

  boolean scheduleReducesUnprotected() {
    // Start scheduling reducers if we have enough maps finished or
    // if the job has very few mappers or reducers.
    return numMapTasks <= rushReduceMaps ||
           numReduceTasks <= rushReduceReduces ||
           finishedMapTasks >= completedMapsForReduceSlowstart;
  }

  /**
   * Signals that the reduce resources are being requested
   * as soon as one process starts this nobody else should be
   * trying to request reduce resources, so get current and set to true
   *
   * @return false if the resources have not been requested yet,
   * true if they have
   */
  boolean initializeReducers() {
    return this.reduceResourcesRequested.getAndSet(true);
  }

  boolean areReducersInitialized() {
    return this.reduceResourcesRequested.get();
  }
  public Task obtainNewMapTaskForTip(
      String taskTrackerName, String hostName, TaskInProgress intendedTip) {
    synchronized (lockObject) {
      Task result = obtainTaskCleanupTask(taskTrackerName, intendedTip);
      if (result != null) {
        return result;
      }

      if (status.getRunState() != JobStatus.RUNNING) {
        return null;
      }

      TaskInProgress tip = removeMatchingTipUnprotected(nonRunningMaps, hostName, intendedTip);
      if (tip != null) {
        LOG.info("Running task " + tip.getTIPId() + " on " + taskTrackerName +
            "(" + hostName + ")");
      }

      if (tip == null && hasSpeculativeMaps) {
        SpeculationStatus speculationStatus = confirmSpeculativeTaskUnprotected(
            candidateSpeculativeMaps, intendedTip,
            taskTrackerName, hostName, TaskType.MAP);
        if (speculationStatus == SpeculationStatus.CAN_BE_SPECULATED) {
          tip = intendedTip;
          LOG.info("Speculating task " + tip.getTIPId() + " on " + taskTrackerName +
              "(" + hostName + ")");
        } else {
          LOG.warn("Cant speculate for given resource " +
            taskTrackerName + " because " + speculationStatus);
        }
      }

      if (tip == null) {
        return null;
      } else  if (tip != intendedTip) {
        throw new RuntimeException("Logic error:" + tip.getTIPId() +
            " was chosen instead of " + intendedTip.getTIPId());
      }
      scheduleMapUnprotected(tip);

      result = tip.getTaskToRun(taskTrackerName);
      if (result != null) {
        addRunningTaskToTIPUnprotected(tip, result.getTaskID(),
            taskTrackerName, hostName, true);
      }
      return result;
    }
  }
 
  /**
   * Registers new task attempt for given task
   * @param taskTrackerName name of destination tracker
   * @param hostName hostname of tracker
   * @param forcedTip task in progress to run
   * @return task to run
   */
  public Task forceNewMapTaskForTip(String taskTrackerName, String hostName,
      TaskInProgress forcedTip) {
    synchronized (lockObject) {
      Task result = obtainTaskCleanupTask(taskTrackerName, forcedTip);
      if (result != null) {
        return result;
      }

      removeMatchingTipUnprotectedUnconditional(nonRunningMaps, forcedTip);
      LOG.info("Running task " + forcedTip.getTIPId() + " on "
          + taskTrackerName + "(" + hostName + ")");
      scheduleMapUnprotected(forcedTip);
      result = forcedTip.getTaskToRun(taskTrackerName);
      if (result != null) {
        addRunningTaskToTIPUnprotected(forcedTip, result.getTaskID(),
            taskTrackerName, hostName, true);
        // Handle cleanup task
        setJobCleanupTaskState(result);
      }
      return result;
    }
  }


  @Override
  public boolean hasSpeculativeMaps() { return hasSpeculativeMaps; }

  @Override
  public boolean hasSpeculativeReduces() { return hasSpeculativeReduces; }

  private void refreshCandidateSpeculativeMapsUnprotected() {
    long now = clock.getTime();
    if ((now - lastSpeculativeMapRefresh) > speculativeRefreshTimeout) {
      // update the progress rates of all the candidate tips ..
      for(TaskInProgress tip: runningMaps) {
        tip.updateProgressRate(now);
      }
      candidateSpeculativeMaps =
        findSpeculativeTaskCandidatesUnprotected(runningMaps);

      int cap = getSpeculativeCap(TaskType.MAP);
      cap = Math.max(0, cap - speculativeMapTasks);
      cap = Math.min(candidateSpeculativeMaps.size(), cap);
      candidateSpeculativeMaps = candidateSpeculativeMaps.subList(0, cap);
      lastSpeculativeMapRefresh = now;
    }
  }

  public int getSpeculativeCap(TaskType type) {
    int cap = 0;
    synchronized (lockObject) {
      int numRunningTasks = (type == TaskType.MAP) ?
          (runningMapTasks - speculativeMapTasks) :
            (runningReduceTasks - speculativeReduceTasks);
      cap = (int) Math.max(MIN_SPEC_CAP, speculativeCap * numRunningTasks);
    }
    return cap;
  }

  public List<TaskInProgress> getSpeculativeCandidates(TaskType type) {
    synchronized (lockObject) {
      if (TaskType.MAP == type) {
        return candidateSpeculativeMaps;
      } else {
        return candidateSpeculativeReduces;
      }
    }
  }

  public void updateSpeculationCandidates() {
    synchronized (lockObject) {
      if (hasSpeculativeMaps()) {
        refreshCandidateSpeculativeMapsUnprotected();
      }
      if (hasSpeculativeReduces()) {
        refreshCandidateSpeculativeReducesUnprotected();
      }
    }
  }

  /**
   * Given a candidate set of tasks, find and order the ones that
   * can be speculated and return the same.
   */
  protected List<TaskInProgress> findSpeculativeTaskCandidatesUnprotected(
      Collection<TaskInProgress> list) {
    ArrayList<TaskInProgress> candidates = new ArrayList<TaskInProgress>();

    long now = clock.getTime();
    Iterator<TaskInProgress> iter = list.iterator();
    while (iter.hasNext()) {
      TaskInProgress tip = iter.next();
      if (tip.canBeSpeculated(now)) {
        candidates.add(tip);
      }
    }
    if (candidates.size() > 0 ) {
      Comparator<TaskInProgress> LateComparator =
        new JobInProgress.EstimatedTimeLeftComparator(now);

      Collections.sort(candidates, LateComparator);
    }
    return candidates;
  }

  private enum SpeculationStatus {
    CAN_BE_SPECULATED,
    CAN_NO_LONGER_BE_SPECULATED,
    HAS_RUN_ON_MACHINE,
    MACHINE_IS_SLOW
  };

  public boolean confirmSpeculativeTask(TaskInProgress tip,
      String taskTrackerName, String taskTrackerHost) {
    synchronized (lockObject) {
      if (tip.isMapTask()) {
        return confirmSpeculativeTaskUnprotected(candidateSpeculativeMaps,
            tip, taskTrackerName, taskTrackerHost, TaskType.MAP)
            == SpeculationStatus.CAN_BE_SPECULATED;
      } else {
        return confirmSpeculativeTaskUnprotected(candidateSpeculativeReduces,
            tip, taskTrackerName, taskTrackerHost, TaskType.REDUCE)
            == SpeculationStatus.CAN_BE_SPECULATED;
      }
    }
  }

  public boolean isBadSpeculativeResource(TaskInProgress tip,
      String taskTrackerName, String taskTrackerHost) {
    synchronized (lockObject) {
      SpeculationStatus status = null;
      if (tip.isMapTask()) {
        status = confirmSpeculativeTaskUnprotected(candidateSpeculativeMaps,
            tip, taskTrackerName, taskTrackerHost, TaskType.MAP);

      } else {
        status = confirmSpeculativeTaskUnprotected(candidateSpeculativeReduces,
            tip, taskTrackerName, taskTrackerHost, TaskType.REDUCE);
      }
      return status == SpeculationStatus.HAS_RUN_ON_MACHINE ||
      status == SpeculationStatus.MACHINE_IS_SLOW;
    }
  }

  protected SpeculationStatus confirmSpeculativeTaskUnprotected(
      List<TaskInProgress> candidates, TaskInProgress intendedTip,
      String taskTrackerName, String taskTrackerHost, TaskType taskType) {
    if ((candidates == null) || candidates.isEmpty()) {
      return null;
    }
    if (isSlowTrackerUnprotected(taskTrackerName)) {
      // TODO: request another resource if this happens.
      return SpeculationStatus.MACHINE_IS_SLOW;
    }

    if (!candidates.contains(intendedTip)) {
      return SpeculationStatus.CAN_NO_LONGER_BE_SPECULATED;
    }
    if (intendedTip.hasRunOnMachine(taskTrackerHost, taskTrackerName)) {
      return SpeculationStatus.HAS_RUN_ON_MACHINE;
    }

    long now = clock.getTime();
    if (intendedTip.canBeSpeculated(now)) {
      return SpeculationStatus.CAN_BE_SPECULATED;
    } else {
      // if it can't be speculated, then:
      // A. it has completed/failed etc. - in which case makes sense to never
      //    speculate again
      // B. it's relative progress does not allow speculation. in this case
      //    it's fair to treat it as if it was never eligible for speculation
      //    to begin with.
      return SpeculationStatus.CAN_NO_LONGER_BE_SPECULATED;
    }
  }

  /**
   * Compares the ave progressRate of tasks that have finished on this
   * taskTracker to the ave of all succesfull tasks thus far to see if this
   * TT one is too slow for speculating.
   * slowNodeThreshold is used to determine the number of standard deviations
   * @param taskTracker the name of the TaskTracker we are checking
   * @return is this TaskTracker slow
   */
  protected boolean isSlowTrackerUnprotected(String taskTracker) {
    // TODO - use statistics here.
    return false;
  }

  private TaskInProgress removeMatchingTipUnprotected(
      List<TaskInProgress> taskList, String hostName,
      TaskInProgress intendedTip) {
    for (Iterator<TaskInProgress> iter = taskList.iterator();
                            iter.hasNext();) {
      TaskInProgress t = iter.next();
      if (t == intendedTip &&
          t.isRunnable() && !t.isRunning() &&
          !t.hasFailedOnMachine(hostName)) {
        iter.remove();
        return t;
      }
    }
    return null;
  }
 
  /**
   * Removes matching TIP without checking any conditions
   * @param taskList list of tasks to remove from
   * @param intendedTip tip to remove
   * @return removed tip
   */
  private TaskInProgress removeMatchingTipUnprotectedUnconditional(
      List<TaskInProgress> taskList, TaskInProgress intendedTip) {
    for (Iterator<TaskInProgress> iter = taskList.iterator(); iter.hasNext();) {
      TaskInProgress t = iter.next();
      if (t.getTIPId().equals(intendedTip.getTIPId())) {
        iter.remove();
        return t;
      }
    }
    return null;
  }

  /**
   * Find a non-running task in the passed list of TIPs
   * @param tips a collection of TIPs
   * @param hostName the host name of tracker that has requested a task to run
   * @param removeFailedTip whether to remove the failed tips
   */
  private static TaskInProgress findTaskFromList(
      Collection<TaskInProgress> tips, String hostName,
      boolean removeFailedTip) {
    Iterator<TaskInProgress> iter = tips.iterator();
    while (iter.hasNext()) {
      TaskInProgress tip = iter.next();

      // Select a tip if
      //   1. runnable   : still needs to be run and is not completed
      //   2. ~running   : no other node is running it
      //   3. earlier attempt failed : has not failed on this host
      //                               and has failed on all the other hosts
      // A TIP is removed from the list if
      // (1) this tip is scheduled
      // (2) if the passed list is a level 0 (host) cache
      // (3) when the TIP is non-schedulable (running, killed, complete)
      if (tip.isRunnable() && !tip.isRunning()) {
        // check if the tip has failed on this host
        if (!tip.hasFailedOnMachine(hostName)) {
          // TODO: check if the tip has failed on all the nodes
          iter.remove();
          return tip;
        } else if (removeFailedTip) {
          // the case where we want to remove a failed tip from the host cache
          // point#3 in the TIP removal logic above
          iter.remove();
        }
      } else {
        // see point#3 in the comment above for TIP removal logic
        iter.remove();
      }
    }
    return null;
  }

  public Task obtainNewReduceTaskForTip(String taskTrackerName, String hostName,
      TaskInProgress intendedTip) {
    synchronized (lockObject) {
      // TODO: check for resource constraints.
      Task result = obtainTaskCleanupTask(taskTrackerName, intendedTip);
      if (result != null) {
        return result;
      }
      TaskInProgress tip = removeMatchingTipUnprotected(
          nonRunningReduces, hostName, intendedTip);
      if (tip != null) {
        LOG.info("Running task " + tip.getTIPId() + " on " + taskTrackerName +
            "(" + hostName + ")");
      }
      // 2. check for a reduce tip to be speculated
      if (tip == null && hasSpeculativeReduces) {
        SpeculationStatus speculationStatus = confirmSpeculativeTaskUnprotected(
            candidateSpeculativeReduces, intendedTip,
            taskTrackerName, hostName, TaskType.REDUCE);
        if (speculationStatus == SpeculationStatus.CAN_BE_SPECULATED) {
          tip = intendedTip;
          LOG.info("Speculating task " + tip.getTIPId() + " on " + taskTrackerName +
              "(" + hostName + ")");
        } else {
          LOG.warn("Cant speculate for given resource " +
            taskTrackerName + " because " + speculationStatus);
        }
      }

      if (tip == null) {
        return null;
      } else  if (tip != intendedTip) {
        throw new RuntimeException("Logic error:" + tip.getTIPId() +
            " was chosen instead of " + intendedTip.getTIPId());
      }

      scheduleReduceUnprotected(tip);
      result = tip.getTaskToRun(taskTrackerName);
      if (result != null) {
        addRunningTaskToTIPUnprotected(
            tip, result.getTaskID(), taskTrackerName, hostName, true);
      }
      return result;
    }
  }
 
  /**
   * Registers new task attempt for given task
   * @param taskTrackerName name of destination tracker
   * @param hostName hostname of tracker
   * @param forcedTip task in progress to run
   * @return task to run
   */
  public Task forceNewReduceTaskForTip(String taskTrackerName, String hostName,
      TaskInProgress forcedTip) {
    synchronized (lockObject) {
      Task result = obtainTaskCleanupTask(taskTrackerName, forcedTip);
      if (result != null) {
        return result;
      }

      removeMatchingTipUnprotectedUnconditional(nonRunningMaps, forcedTip);
      LOG.info("Running task " + forcedTip.getTIPId() + " on "
          + taskTrackerName + "(" + hostName + ")");
      scheduleReduceUnprotected(forcedTip);
      result = forcedTip.getTaskToRun(taskTrackerName);
      if (result != null) {
        addRunningTaskToTIPUnprotected(forcedTip, result.getTaskID(),
            taskTrackerName, hostName, true);
        // Handle cleanup task
        setJobCleanupTaskState(result);
      }
      return result;
    }
  }

  private void refreshCandidateSpeculativeReducesUnprotected() {
    long now = clock.getTime();
    if ((now - lastSpeculativeReduceRefresh) > speculativeRefreshTimeout) {
      // update the progress rates of all the candidate tips ..
      for(TaskInProgress tip: runningReduces) {
        tip.updateProgressRate(now);
      }
      candidateSpeculativeReduces =
        findSpeculativeTaskCandidatesUnprotected(runningReduces);
      int cap = getSpeculativeCap(TaskType.REDUCE);
      cap = Math.max(0, cap - speculativeReduceTasks);
      cap = Math.min(candidateSpeculativeReduces.size(), cap);
      candidateSpeculativeReduces =
        candidateSpeculativeReduces.subList(0, cap);
      lastSpeculativeReduceRefresh = now;
    }
  }

  /**
   * Can a tracker be used for a TIP?
   */
  public boolean canTrackerBeUsed(
      String taskTracker, String trackerHost, TaskInProgress tip) {
    synchronized (lockObject) {
      return !tip.hasFailedOnMachine(trackerHost);
    }
  }

  /**
   * Return a CleanupTask, if appropriate, to run on the given tasktracker
   */
  public Task obtainJobCleanupTask(
      String taskTrackerName, String hostName, boolean isMapSlot) {
    synchronized (lockObject) {
      if(!tasksInited || !jobSetupCleanupNeeded) {
        return null;
      }

      if (!canLaunchJobCleanupTaskUnprotected()) {
        return null;
      }

      List<TaskInProgress> cleanupTaskList = new ArrayList<TaskInProgress>();
      if (isMapSlot) {
        cleanupTaskList.add(cleanup[0]);
      } else {
        cleanupTaskList.add(cleanup[1]);
      }
      TaskInProgress tip =
        findTaskFromList(cleanupTaskList, hostName, false);
      if (tip == null) {
        return null;
      }

      // Now launch the cleanupTask
      Task result = tip.getTaskToRun(taskTrackerName);

      if (result != null) {
        addRunningTaskToTIPUnprotected(tip, result.getTaskID(), taskTrackerName, hostName,
            true);
        // Handle cleanup task
        setJobCleanupTaskState(result);
      }
      return result;
    }
  }
 
  /**
   * Sets task state according to job state if given task is cleanup one
   * @param task task to handle
   */
  private void setJobCleanupTaskState(Task task) {
    if (task.isJobCleanupTask()) {
      if (jobFailed) {
        task.setJobCleanupTaskState(
            org.apache.hadoop.mapreduce.JobStatus.State.FAILED);
      } else if (jobKilled) {
        task.setJobCleanupTaskState(
            org.apache.hadoop.mapreduce.JobStatus.State.KILLED);
      } else {
        task.setJobCleanupTaskState(
            org.apache.hadoop.mapreduce.JobStatus.State.SUCCEEDED);
      }
    }
  }
  @SuppressWarnings("deprecation")
  public boolean needsTaskCleanup(TaskInProgress tip) {
    synchronized (lockObject) {
      Iterator<TaskAttemptID> cleanupCandidates;
      if (tip.isMapTask()) {
        cleanupCandidates = mapCleanupTasks.iterator();
      } else {
        cleanupCandidates = reduceCleanupTasks.iterator();
      }
     
      while (cleanupCandidates.hasNext()) {
        if (cleanupCandidates.next().getTaskID().equals(tip.getTIPId())) {
          return true;
        }
      }
    }
    return false;
  }

  @SuppressWarnings("deprecation")
  public Task obtainTaskCleanupTask(String taskTracker, TaskInProgress tip) {
    synchronized (lockObject) {
      if (!tasksInited) {
        return null;
      }
      if (this.status.getRunState() != JobStatus.RUNNING ||
          jobFailed || jobKilled) {
        return null;
      }

      if (tip.isMapTask()) {
        if (mapCleanupTasks.isEmpty())
          return null;
      } else {
        if (reduceCleanupTasks.isEmpty())
          return null;
      }

      if (this.status.getRunState() != JobStatus.RUNNING ||
          jobFailed || jobKilled) {
        return null;
      }
      TaskAttemptID taskid = null;
      Iterator<TaskAttemptID> cleanupCandidates = null;
      boolean foundCleanup = false;
      if (tip.isMapTask()) {
        if (!mapCleanupTasks.isEmpty()) {
          cleanupCandidates = mapCleanupTasks.iterator();
        }
      } else {
        if (!reduceCleanupTasks.isEmpty()) {
          cleanupCandidates = reduceCleanupTasks.iterator();
        }
      }
     
      while (cleanupCandidates.hasNext()) {
         taskid = cleanupCandidates.next();
       
        if (taskid.getTaskID().equals(tip.getTIPId())) {
          // The task requires a cleanup so we are going to do that right now
          cleanupCandidates.remove();
          foundCleanup = true;
          break;
        }
      }
      if (foundCleanup) {
        return tip.addRunningTask(taskid, taskTracker, true);
      }
      return null;
    }
  }

  /**
   * Return a SetupTask, if appropriate, to run on the given tasktracker
   */
  public Task obtainJobSetupTask(
      String taskTrackerName, String hostName, boolean isMapSlot) {
    synchronized (lockObject) {
      if (!tasksInited || !jobSetupCleanupNeeded) {
        return null;
      }

      if (!(tasksInited && status.getRunState() == JobStatus.PREP &&
           !launchedSetup && !jobKilled && !jobFailed)) {
        return null;
      }
      List<TaskInProgress> setupTaskList = new ArrayList<TaskInProgress>();
      if (isMapSlot) {
        setupTaskList.add(setup[0]);
      } else {
        setupTaskList.add(setup[1]);
      }
      TaskInProgress tip = findTaskFromList(setupTaskList, hostName, false);
      if (tip == null) {
        return null;
      }

      // Now launch the setupTask
      Task result = tip.getTaskToRun(taskTrackerName);
      if (result != null) {
        addRunningTaskToTIPUnprotected(tip, result.getTaskID(), taskTrackerName, hostName,
            true);
      }
      return result;
    }
  }

  public void updateTaskStatus(TaskInProgress tip, TaskStatus status,
      TaskTrackerInfo ttStatus) {
    synchronized (lockObject) {
      updateTaskStatusUnprotected(tip, status, ttStatus);
    }
  }
 
  private boolean isTaskKilledHighMemory(TaskStatus status, String keyword) {
    String diagnosticInfo = status.getDiagnosticInfo();
    if (diagnosticInfo == null) {
      return false;
    }
    String[] splitdiagnosticInfo = diagnosticInfo.split("\\s+");
    for (String info : splitdiagnosticInfo) {
      if (keyword.equals(info)) {
        return true;
      }
    }
    return false;
  }
 
  private boolean isTaskKilledWithHighMemory(TaskStatus status) {
    return isTaskKilledHighMemory(status,
      TaskMemoryManagerThread.HIGH_MEMORY_KEYWORD);
  }
 
  private boolean isTaskKilledWithCGroupMemory(TaskStatus status) {
    return isTaskKilledHighMemory(status,
      CGroupMemoryWatcher.CGROUPHIGH_MEMORY_KEYWORD);
  }
 
  private void updateCGResourceCounters(TaskStatus status, boolean isMap) {
    Counters taskCounters = status.getCounters();
    long maxMem = taskCounters.getCounter(Task.Counter.MAX_MEMORY_BYTES);
    long rssMem = taskCounters.getCounter(Task.Counter.MAX_RSS_MEMORY_BYTES);
    long instMem = taskCounters.getCounter(Task.Counter.INST_MEMORY_BYTES);
   
    if (isMap) {
     if (jobCounters.getCounter(Counter.MAX_MAP_MEM_BYTES) < maxMem) {
       jobCounters.findCounter(Counter.MAX_MAP_MEM_BYTES).setValue(maxMem);
     }
     if (jobCounters.getCounter(Counter.MAX_MAP_RSS_MEM_BYTES) < rssMem) {
       jobCounters.findCounter(Counter.MAX_MAP_RSS_MEM_BYTES).setValue(rssMem);
     }
     if (jobCounters.getCounter(Counter.MAX_MAP_INST_MEM_BYTES) < instMem) {
       jobCounters.findCounter(Counter.MAX_MAP_INST_MEM_BYTES).setValue(instMem);
     }
    } else {
      if (jobCounters.getCounter(Counter.MAX_REDUCE_MEM_BYTES) < maxMem) {
        jobCounters.findCounter(Counter.MAX_REDUCE_MEM_BYTES).setValue(maxMem);
      }
      if (jobCounters.getCounter(Counter.MAX_REDUCE_RSS_MEM_BYTES) < rssMem) {
        jobCounters.findCounter(Counter.MAX_REDUCE_RSS_MEM_BYTES).setValue(rssMem);
      }
      if (jobCounters.getCounter(Counter.MAX_REDUCE_INST_MEM_BYTES) < instMem) {
        jobCounters.findCounter(Counter.MAX_REDUCE_INST_MEM_BYTES).setValue(instMem);
      }
    }
  }
 
  @SuppressWarnings("deprecation")
  private void updateTaskStatusUnprotected(TaskInProgress tip, TaskStatus status,
      TaskTrackerInfo ttStatus) {
    double oldProgress = tip.getProgress();   // save old progress
    boolean wasRunning = tip.isRunning();
    boolean wasComplete = tip.isComplete();
    boolean wasPending = tip.isOnlyCommitPending();
    TaskAttemptID taskid = status.getTaskID();
    boolean wasAttemptRunning = tip.isAttemptRunning(taskid);
   
    // If the TIP is already completed and the task reports as SUCCEEDED then
    // mark the task as KILLED.
    // In case of task with no promotion the task tracker will mark the task
    // as SUCCEEDED.
    // User has requested to kill the task, but TT reported SUCCEEDED,
    // mark the task KILLED.
    if ((wasComplete || tip.wasKilled(taskid)) &&
        (status.getRunState() == TaskStatus.State.SUCCEEDED)) {
      status.setRunState(TaskStatus.State.KILLED);
    }

    // When a task has just reported its state as FAILED_UNCLEAN/KILLED_UNCLEAN,
    // if the job is complete or cleanup task is switched off,
    // make the task's state FAILED/KILLED without launching cleanup attempt.
    // Note that if task is already a cleanup attempt,
    // we don't change the state to make sure the task gets a killTaskAction
    if ((this.status.isJobComplete() || jobFailed ||
         jobKilled || !taskCleanupNeeded) && !tip.isCleanupAttempt(taskid)) {
      if (status.getRunState() == TaskStatus.State.FAILED_UNCLEAN) {
        status.setRunState(TaskStatus.State.FAILED);
      } else if (status.getRunState() == TaskStatus.State.KILLED_UNCLEAN) {
        status.setRunState(TaskStatus.State.KILLED);
      }
    }
   
    // aggregate the task tracker reported cgroup resource counters in job
    updateCGResourceCounters(status, tip.isMapTask());
   
    boolean change = tip.updateStatus(status);
    if (change) {
      TaskStatus.State state = status.getRunState();
      String httpTaskLogLocation = null; // TODO fix this
      if (ttStatus != null){
        String host;
        if (NetUtils.getStaticResolution(ttStatus.getHost()) != null) {
          host = NetUtils.getStaticResolution(ttStatus.getHost());
        } else {
          host = ttStatus.getHost();
        }
        httpTaskLogLocation = "http://" + host + ":" + ttStatus.getHttpPort();
      }
      TaskCompletionEvent taskEvent = null;
      if (state == TaskStatus.State.SUCCEEDED) {
        taskEvent = new TaskCompletionEvent(
                                            taskCompletionEventCounter,
                                            taskid,
                                            tip.idWithinJob(),
                                            status.getIsMap() &&
                                            !tip.isJobCleanupTask() &&
                                            !tip.isJobSetupTask(),
                                            TaskCompletionEvent.Status.SUCCEEDED,
                                            httpTaskLogLocation
                                           );
        taskEvent.setTaskRunTime((int)(status.getFinishTime()
                                       - status.getStartTime()));
        tip.setSuccessEventNumber(taskCompletionEventCounter);
      } else if (state == TaskStatus.State.COMMIT_PENDING) {
        // If it is the first attempt reporting COMMIT_PENDING
        // ask the task to commit.
        if (!wasComplete && !wasPending) {
          tip.doCommit(taskid);
        }
        return;
      } else if (state == TaskStatus.State.FAILED_UNCLEAN ||
                 state == TaskStatus.State.KILLED_UNCLEAN) {
        tip.incompleteSubTask(taskid, this.status);
        // add this task, to be rescheduled as cleanup attempt
        if (tip.isMapTask()) {
          mapCleanupTasks.add(taskid);
        } else {
          reduceCleanupTasks.add(taskid);
        }
        if (isTaskKilledWithCGroupMemory(status)) {
          //Increment the High Memory killed count for Reduce and Map Tasks
          if (status.getIsMap()) {
            jobCounters.incrCounter(
              Counter.TOTAL_CGROUP_MEMORY_MAP_TASK_KILLED, 1);
          }
          else {
            jobCounters.incrCounter(
              Counter.TOTAL_CGROUP_MEMORY_REDUCE_TASK_KILLED, 1);
          }
        }
      }
      //For a failed task update the JT datastructures.
      else if (state == TaskStatus.State.FAILED ||
               state == TaskStatus.State.KILLED) {
        if (isTaskKilledWithHighMemory(status)) {
          //Increment the High Memory killed count for Reduce and Map Tasks
          if (status.getIsMap()) {
            jobCounters.incrCounter(Counter.TOTAL_HIGH_MEMORY_MAP_TASK_KILLED, 1);
          }
          else {
            jobCounters.incrCounter(Counter.TOTAL_HIGH_MEMORY_REDUCE_TASK_KILLED, 1);
          }
        }
        if (isTaskKilledWithCGroupMemory(status)) {
          //Increment the High Memory killed count for Reduce and Map Tasks
          if (status.getIsMap()) {
            jobCounters.incrCounter(
              Counter.TOTAL_CGROUP_MEMORY_MAP_TASK_KILLED, 1);
          }
          else {
            jobCounters.incrCounter(
              Counter.TOTAL_CGROUP_MEMORY_REDUCE_TASK_KILLED, 1);
          }
        }
        // Get the event number for the (possibly) previously successful
        // task. If there exists one, then set that status to OBSOLETE
        int eventNumber;
        if ((eventNumber = tip.getSuccessEventNumber()) != -1) {
          TaskCompletionEvent t =
            this.taskCompletionEvents.get(eventNumber);
          if (t.getTaskAttemptId().equals(taskid))
            t.setTaskStatus(TaskCompletionEvent.Status.OBSOLETE);
        }

        // Tell the job to fail the relevant task
        failedTask(tip, taskid, status, ttStatus, wasRunning,
                  wasComplete, wasAttemptRunning);

        // Did the task failure lead to tip failure?
        TaskCompletionEvent.Status taskCompletionStatus =
          (state == TaskStatus.State.FAILED ) ?
              TaskCompletionEvent.Status.FAILED :
              TaskCompletionEvent.Status.KILLED;
        if (tip.isFailed()) {
          taskCompletionStatus = TaskCompletionEvent.Status.TIPFAILED;
        }
        taskEvent = new TaskCompletionEvent(taskCompletionEventCounter,
                                            taskid,
                                            tip.idWithinJob(),
                                            status.getIsMap() &&
                                            !tip.isJobCleanupTask() &&
                                            !tip.isJobSetupTask(),
                                            taskCompletionStatus,
                                            httpTaskLogLocation
                                           );
      }

      // Add the 'complete' task i.e. successful/failed
      // It _is_ safe to add the TaskCompletionEvent.Status.SUCCEEDED
      // *before* calling TIP.completedTask since:
      // a. One and only one task of a TIP is declared as a SUCCESS, the
      //    other (speculative tasks) are marked KILLED by the TaskCommitThread
      // b. TIP.completedTask *does not* throw _any_ exception at all.
      if (taskEvent != null) {
        this.taskCompletionEvents.add(taskEvent);
        taskCompletionEventCounter++;
        if (state == TaskStatus.State.SUCCEEDED) {
          completedTask(tip, status, ttStatus);
        }
      }
      taskStateChangeListener.taskStateChange(state, tip, taskid,
          (ttStatus == null ? "null" : ttStatus.getHost()));
    }

    //
    // Update CoronaJobInProgress status
    //
    if(LOG.isDebugEnabled()) {
      LOG.debug("Taking progress for " + tip.getTIPId() + " from " +
                 oldProgress + " to " + tip.getProgress());
    }

    if (!tip.isJobCleanupTask() && !tip.isJobSetupTask()) {
      double progressDelta = tip.getProgress() - oldProgress;
      if (tip.isMapTask()) {
          this.status.setMapProgress((float) (this.status.mapProgress() +
                                              progressDelta / maps.length));
      } else {
        this.status.setReduceProgress((float) (this.status.reduceProgress() +
                                           (progressDelta / reduces.length)));
      }
    }
  }


  /**
   * Should we reuse the resource of this succeeded task attempt
   * return true if we should reuse
   */
  boolean shouldReuseTaskResource(TaskInProgress tip) {
    synchronized (lockObject) {
      return tip.isJobSetupTask() || canLaunchJobCleanupTaskUnprotected()
          || tip.isJobCleanupTask();
    }
    // TIP is a job setup/cleanup task or job is ready for cleanup.
    //
    // Since job setup/cleanup does not get an explicit resource, reuse
    // the resource. For Map/Reduce tasks, we would want normally want
    // to release the resource. But if the job is ready for cleanup,
    // releasing the resource could mean that the job cleanup task can't
    // run, so reuse the resource.
  }

  public boolean completedTask(TaskInProgress tip, TaskStatus status,
      TaskTrackerInfo ttStatus) {
    synchronized (lockObject) {
      return completedTaskUnprotected(tip, status, ttStatus);
    }
  }

  /**
   * A taskId assigned to this CoronaJobInProgress has reported in successfully.
   */
  @SuppressWarnings("deprecation")
  private boolean completedTaskUnprotected(TaskInProgress tip, TaskStatus status,
      TaskTrackerInfo ttStatus) {
    int oldNumAttempts = tip.getActiveTasks().size();
    // Metering
    meterTaskAttemptUnprotected(tip, status);

    // It _is_ safe to not decrement running{Map|Reduce}Tasks and
    // finished{Map|Reduce}Tasks variables here because one and only
    // one task-attempt of a TIP gets to completedTask. This is because
    // the TaskCommitThread in the JobTracker marks other, completed,
    // speculative tasks as _complete_.
    TaskAttemptID taskId = status.getTaskID();
    if (tip.isComplete()) {
      // Mark this task as KILLED
      tip.alreadyCompletedTask(taskId);
      return false;
    }

    LOG.info("Task '" + taskId + "' has completed " + tip.getTIPId() +
           " successfully.");
    // Mark the TIP as complete
    tip.completed(taskId);

    // Update jobhistory
    String taskType = getTaskType(tip);
    if (status.getIsMap()){
      jobHistory.logMapTaskStarted(status.getTaskID(), status.getStartTime(),
                                       status.getTaskTracker(),
                                       ttStatus.getHttpPort(),
                                       taskType);
      jobHistory.logMapTaskFinished(status.getTaskID(), status.getFinishTime(),
                                        ttStatus.getHost(), taskType,
                                        status.getStateString(),
                                        status.getCounters());
    }else{
      jobHistory.logReduceTaskStarted( status.getTaskID(), status.getStartTime(),
                                          status.getTaskTracker(),
                                          ttStatus.getHttpPort(),
                                          taskType);
      jobHistory.logReduceTaskFinished(status.getTaskID(), status.getShuffleFinishTime(),
                                           status.getSortFinishTime(), status.getFinishTime(),
                                           ttStatus.getHost(),
                                           taskType,
                                           status.getStateString(),
                                           status.getCounters());
    }
    jobHistory.logTaskFinished(tip.getTIPId(),
                                taskType,
                                tip.getExecFinishTime(),
                                status.getCounters());

    int newNumAttempts = tip.getActiveTasks().size();
    if (tip.isJobSetupTask()) {
      // setup task has finished. kill the extra setup tip
      killSetupTipUnprotected(!tip.isMapTask());
      setupCompleteUnprotected();
    } else if (tip.isJobCleanupTask()) {
      // cleanup task has finished. Kill the extra cleanup tip
      if (tip.isMapTask()) {
        // kill the reduce tip
        cleanup[1].kill();
      } else {
        cleanup[0].kill();
      }

      if (jobFailed) {
        terminateJob(JobStatus.FAILED);
      } else if (jobKilled) {
        terminateJob(JobStatus.KILLED);
      } else {
        jobCompleteUnprotected();
      }
    } else if (tip.isMapTask()) {
      runningMapTasks--;
      // Update locality counters.
      long inputBytes = tip.getCounters()
                .getGroup("org.apache.hadoop.mapred.Task$Counter")
                .getCounter("Map input bytes");
      jobStats.incTotalMapInputBytes(inputBytes);
      localityStats.record(tip, ttStatus.getHost(), inputBytes);
      // check if this was a speculative task.
      if (oldNumAttempts > 1) {
        speculativeMapTasks -= (oldNumAttempts - newNumAttempts);
        jobStats.incNumSpeculativeSucceededMaps();
      }
      finishedMapTasks += 1;
      jobStats.incNumMapTasksCompleted();
      if (!tip.isJobSetupTask() && hasSpeculativeMaps) {
        updateTaskTrackerStats(tip,ttStatus,trackerMapStats,mapTaskStats);
      }
      // remove the completed map from the resp running caches
      retireMapUnprotected(tip);
      if ((finishedMapTasks + failedMapTIPs) == (numMapTasks)) {
        this.status.setMapProgress(1.0f);
      }
    } else {
      runningReduceTasks -= 1;
      if (oldNumAttempts > 1) {
        speculativeReduceTasks -= (oldNumAttempts - newNumAttempts);
        jobStats.incNumSpeculativeSucceededReduces();
      }
      finishedReduceTasks += 1;
      jobStats.incNumReduceTasksCompleted();
      if (!tip.isJobSetupTask() && hasSpeculativeReduces) {
        updateTaskTrackerStats(tip,ttStatus,trackerReduceStats,reduceTaskStats);
      }
      // remove the completed reduces from the running reducers set
      retireReduceUnprotected(tip);
      if ((finishedReduceTasks + failedReduceTIPs) == (numReduceTasks)) {
        this.status.setReduceProgress(1.0f);
      }
    }

    // is job complete?
    if (!jobSetupCleanupNeeded && canLaunchJobCleanupTaskUnprotected()) {
      jobCompleteUnprotected();
    }

    return true;
  }

  /**
   * Fail a task with a given reason, but without a status object.
   */
  @SuppressWarnings("deprecation")
  public void failedTask(
    TaskInProgress tip,
    TaskAttemptID taskid,
    String reason,
    TaskStatus.Phase phase,
    boolean isFailed,
    String trackerName,
    TaskTrackerInfo ttStatus) {
    TaskStatus.State state =
      isFailed ? TaskStatus.State.FAILED: TaskStatus.State.KILLED;
    TaskStatus status = TaskStatus.createTaskStatus(tip.isMapTask(),
                                                    taskid,
                                                    0.0f,
                                                    1,
                                                    state,
                                                    reason,
                                                    reason,
                                                    trackerName, phase,
                                                    new Counters());
    synchronized (lockObject) {
      // update the actual start-time of the attempt
      TaskStatus oldStatus = tip.getTaskStatus(taskid);
      long startTime = oldStatus == null
      ? JobTracker.getClock().getTime()
          : oldStatus.getStartTime();
      if (startTime < 0) {
        startTime = JobTracker.getClock().getTime();
      }
      status.setStartTime(startTime);
      status.setFinishTime(JobTracker.getClock().getTime());
      boolean wasComplete = tip.isComplete();
      updateTaskStatus(tip, status, ttStatus);
      boolean isComplete = tip.isComplete();
      if (wasComplete && !isComplete) { // mark a successful tip as failed
        String taskType = getTaskType(tip);
        JobHistory.Task.logFailed(tip.getTIPId(), taskType,
            tip.getExecFinishTime(), reason, taskid);
      }
    }
  }


  @SuppressWarnings("deprecation")
  public void failedTask(TaskInProgress tip, TaskAttemptID taskid,
                          TaskStatus status, TaskTrackerInfo taskTrackerStatus,
                          boolean wasRunning, boolean wasComplete,
                          boolean wasAttemptRunning) {
    synchronized (lockObject) {
      failedTaskUnprotected(tip, taskid, status, taskTrackerStatus,
          wasRunning, wasComplete, wasAttemptRunning);
    }
  }

  /**
   * A task assigned to this CoronaJobInProgress has reported in as failed.
   * Most of the time, we'll just reschedule execution.  However, after
   * many repeated failures we may instead decide to allow the entire
   * job to fail or succeed if the user doesn't care about a few tasks failing.
   *
   * Even if a task has reported as completed in the past, it might later
   * be reported as failed.  That's because the TaskTracker that hosts a map
   * task might die before the entire job can complete.  If that happens,
   * we need to schedule reexecution so that downstream reduce tasks can
   * obtain the map task's output.
   */
  @SuppressWarnings("deprecation")
  private void failedTaskUnprotected(TaskInProgress tip, TaskAttemptID taskid,
                          TaskStatus status, TaskTrackerInfo taskTrackerStatus,
                          boolean wasRunning, boolean wasComplete,
                          boolean wasAttemptRunning) {
    taskErrorCollector.collect(tip, taskid, clock.getTime());
    // check if the TIP is already failed
    boolean wasFailed = tip.isFailed();

    // Mark the taskid as FAILED or KILLED
    tip.incompleteSubTask(taskid, this.status);

    boolean isRunning = tip.isRunning();
    boolean isComplete = tip.isComplete();

    if (wasAttemptRunning) {
      if (!tip.isJobCleanupTask() && !tip.isJobSetupTask()) {
        long timeSpent = clock.getTime() - status.getStartTime();
        boolean isSpeculative = tip.isSpeculativeAttempt(taskid);
        if (tip.isMapTask()) {
          runningMapTasks -= 1;
          if (wasFailed) {
            jobStats.incNumMapTasksFailed();
            jobStats.incFailedMapTime(timeSpent);
          } else {
            jobStats.incNumMapTasksKilled();
            jobStats.incKilledMapTime(timeSpent);
            if (isSpeculative) {
              jobStats.incNumSpeculativeWasteMaps();
              jobStats.incSpeculativeMapTimeWaste(timeSpent);
            }
          }
        } else {
          runningReduceTasks -= 1;
          if (wasFailed) {
            jobStats.incNumReduceTasksFailed();
            jobStats.incFailedReduceTime(timeSpent);
          } else {
            jobStats.incNumReduceTasksKilled();
            jobStats.incKilledReduceTime(timeSpent);
            if (isSpeculative) {
              jobStats.incNumSpeculativeWasteReduces();
              jobStats.incSpeculativeReduceTimeWaste(timeSpent);
            }
          }
        }
      }

      // Metering
      meterTaskAttemptUnprotected(tip, status);
    }

    //update running  count on task failure.
    if (wasRunning && !isRunning) {
      if (tip.isJobCleanupTask()) {
        launchedCleanup = false;
      } else if (tip.isJobSetupTask()) {
        launchedSetup = false;
      } else if (tip.isMapTask()) {
        // remove from the running queue and put it in the non-running cache
        // if the tip is not complete i.e if the tip still needs to be run
        if (!isComplete) {
          failMapUnprotected(tip);
        }
      } else {
        // remove from the running queue and put in the failed queue if the tip
        // is not complete
        if (!isComplete) {
          failReduceUnprotected(tip);
        }
      }
    }

    // The case when the map was complete but the task tracker went down.
    // However, we don't need to do any metering here...
    if (wasComplete && !isComplete) {
      if (tip.isMapTask()) {
        // Put the task back in the cache. This will help locality for cases
        // where we have a different TaskTracker from the same rack/switch
        // asking for a task.
        // We bother about only those TIPs that were successful
        // earlier (wasComplete and !isComplete)
        // (since they might have been removed from the cache of other
        // racks/switches, if the input split blocks were present there too)
        failMapUnprotected(tip);
        finishedMapTasks -= 1;
      }
    }

    // update job history
    // get taskStatus from tip
    TaskStatus taskStatus = tip.getTaskStatus(taskid);
    String taskTrackerName = taskStatus.getTaskTracker();
    String taskTrackerHostName = convertTrackerNameToHostName(taskTrackerName);
    int taskTrackerPort = -1;
    if (taskTrackerStatus != null) {
      taskTrackerPort = taskTrackerStatus.getHttpPort();
    }
    long startTime = taskStatus.getStartTime();
    long finishTime = taskStatus.getFinishTime();
    List<String> taskDiagnosticInfo = tip.getDiagnosticInfo(taskid);
    String diagInfo = taskDiagnosticInfo == null ? "" :
      StringUtils.arrayToString(taskDiagnosticInfo.toArray(new String[0]));
    String taskType = getTaskType(tip);
    if (taskStatus.getIsMap()) {
      jobHistory.logMapTaskStarted(taskid, startTime,
        taskTrackerName, taskTrackerPort, taskType);
      if (taskStatus.getRunState() == TaskStatus.State.FAILED) {
        jobHistory.logMapTaskFailed(taskid, finishTime,
          taskTrackerHostName, diagInfo, taskType);
      } else {
        jobHistory.logMapTaskKilled(taskid, finishTime,
          taskTrackerHostName, diagInfo, taskType);
      }
    } else {
      jobHistory.logReduceTaskStarted(taskid, startTime,
        taskTrackerName, taskTrackerPort, taskType);
      if (taskStatus.getRunState() == TaskStatus.State.FAILED) {
        jobHistory.logReduceTaskFailed(taskid, finishTime,
          taskTrackerHostName, diagInfo, taskType);
      } else {
        jobHistory.logReduceTaskKilled(taskid, finishTime,
          taskTrackerHostName, diagInfo, taskType);
      }
    }

    // After this, try to assign tasks with the one after this, so that
    // the failed task goes to the end of the list.
    if (!tip.isJobCleanupTask() && !tip.isJobSetupTask()) {
      if (tip.isMapTask()) {
        failedMapTasks++;
        if (taskStatus.getRunState() != TaskStatus.State.FAILED) {
          killedMapTasks++;
        }
      } else {
        failedReduceTasks++;
        if (taskStatus.getRunState() != TaskStatus.State.FAILED) {
          killedReduceTasks++;
        }
      }
    }


    //
    // Check if we need to kill the job because of too many failures or
    // if the job is complete since all component tasks have completed

    // We do it once per TIP and that too for the task that fails the TIP
    if (!wasFailed && tip.isFailed()) {
      //
      // Allow upto 'mapFailuresPercent' of map tasks to fail or
      // 'reduceFailuresPercent' of reduce tasks to fail
      //
      boolean killJob = tip.isJobCleanupTask() || tip.isJobSetupTask() ? true :
                        tip.isMapTask() ?
            ((++failedMapTIPs*100) > (mapFailuresPercent*numMapTasks)) :
            ((++failedReduceTIPs*100) > (reduceFailuresPercent*numReduceTasks));

      if (killJob) {
        LOG.info("Aborting job " + profile.getJobID());
        jobHistory.logTaskFailed(tip.getTIPId(),
                                  taskType,
                                  finishTime,
                                  diagInfo);
        if (tip.isJobCleanupTask()) {
          // kill the other tip
          if (tip.isMapTask()) {
            cleanup[1].kill();
          } else {
            cleanup[0].kill();
          }
          terminateJob(JobStatus.FAILED);
        } else {
          if (tip.isJobSetupTask()) {
            // kill the other tip
            killSetupTipUnprotected(!tip.isMapTask());
          }
          fail();
        }
      }

      //
      // Update the counters
      //
      if (!tip.isJobCleanupTask() && !tip.isJobSetupTask()) {
        if (tip.isMapTask()) {
          jobCounters.incrCounter(Counter.NUM_FAILED_MAPS, 1);
        } else {
          jobCounters.incrCounter(Counter.NUM_FAILED_REDUCES, 1);
        }
      }
    }
  }

  /**
   * Get the task type for logging it to {@link JobHistory}.
   */
  private String getTaskType(TaskInProgress tip) {
    if (tip.isJobCleanupTask()) {
      return Values.CLEANUP.name();
    } else if (tip.isJobSetupTask()) {
      return Values.SETUP.name();
    } else if (tip.isMapTask()) {
      return Values.MAP.name();
    } else {
      return Values.REDUCE.name();
    }
  }

  /**
   * Adds the failed TIP in the front of the list for non-running maps
   * @param tip the tip that needs to be failed
   */
  private void failMapUnprotected(TaskInProgress tip) {
    if (nonRunningMaps == null) {
      LOG.warn("Non-running cache for maps missing!! "
               + "Job details are missing.");
      return;
    }
    nonRunningMaps.add(tip);
  }

  /**
   * Adds a failed TIP in the front of the list for non-running reduces
   * @param tip the tip that needs to be failed
   */
  private void failReduceUnprotected(TaskInProgress tip) {
    if (nonRunningReduces == null) {
      LOG.warn("Failed cache for reducers missing!! "
               + "Job details are missing.");
      return;
    }
    nonRunningReduces.add(0, tip);
  }

  @SuppressWarnings("deprecation")
  private void clearUncleanTasksUnprotected() {
    TaskAttemptID taskid = null;
    TaskInProgress tip = null;
    while (!mapCleanupTasks.isEmpty()) {
      taskid = mapCleanupTasks.remove(0);
      tip = maps[taskid.getTaskID().getId()];
      updateTaskStatus(tip, tip.getTaskStatus(taskid), null);
    }
    while (!reduceCleanupTasks.isEmpty()) {
      taskid = reduceCleanupTasks.remove(0);
      tip = reduces[taskid.getTaskID().getId()];
      updateTaskStatus(tip, tip.getTaskStatus(taskid), null);
    }
  }

  private void killSetupTipUnprotected(boolean isMap) {
    if (isMap) {
      setup[0].kill();
    } else {
      setup[1].kill();
    }
  }

  private void setupCompleteUnprotected() {
    status.setSetupProgress(1.0f);
    if (this.status.getRunState() == JobStatus.PREP) {
      changeStateTo(JobStatus.RUNNING);
      jobHistory.logStarted();
    }
  }

  private void jobComplete() {
    synchronized (lockObject) {
      jobCompleteUnprotected();
    }
  }
  /**
   * The job is done since all it's component tasks are either
   * successful or have failed.
   */
  @SuppressWarnings("deprecation")
  private void jobCompleteUnprotected() {
    //
    // All tasks are complete, then the job is done!
   
    if(this.terminated == JobStatus.FAILED
        || this.terminated == JobStatus.KILLED) {
      terminate(this.terminated);
    }
    if (this.status.getRunState() == JobStatus.RUNNING ||
        this.status.getRunState() == JobStatus.PREP) {
      changeStateTo(JobStatus.SUCCEEDED);
      this.status.setCleanupProgress(1.0f);
      if (maps.length == 0) {
        this.status.setMapProgress(1.0f);
      }
      if (reduces.length == 0) {
        this.status.setReduceProgress(1.0f);
      }
      this.finishTime = clock.getTime();
      LOG.info("Job " + this.status.getJobID() +
               " has completed successfully.");

      // Log the job summary (this should be done prior to logging to
      // job-history to ensure job-counters are in-sync
      JobSummary.logJobSummary(this);

      Counters counters = getCounters();
      // Log job-history
      jobHistory.logFinished(finishTime,
                             this.finishedMapTasks,
                             this.finishedReduceTasks, failedMapTasks,
                             failedReduceTasks, killedMapTasks,
                             killedReduceTasks, getMapCounters(),
                             getReduceCounters(), counters);
    }
  }

  /**
   * Job state change must happen thru this call
   */
  private void changeStateTo(int newState) {
    synchronized (lockObject) {
      int oldState = this.status.getRunState();
      if (oldState == newState) {
        return; //old and new states are same
      }
      this.status.setRunState(newState);
    }
  }

  /**
   * Metering: Occupied Slots * (Finish - Start)
   * @param tip {@link TaskInProgress} to be metered which just completed,
   *            cannot be <code>null</code>
   * @param status {@link TaskStatus} of the completed task, cannot be
   *               <code>null</code>
   */
  @SuppressWarnings("deprecation")
  private void meterTaskAttemptUnprotected(TaskInProgress tip, TaskStatus status) {
    Counter slotCounter =
      (tip.isMapTask()) ? Counter.SLOTS_MILLIS_MAPS :
                          Counter.SLOTS_MILLIS_REDUCES;
    jobCounters.incrCounter(slotCounter,
                            tip.getNumSlotsRequired() *
                            (status.getFinishTime() - status.getStartTime()));
    if (!tip.isMapTask()) {
      jobCounters.incrCounter(Counter.SLOTS_MILLIS_REDUCES_COPY,
                  tip.getNumSlotsRequired() *
                  (status.getShuffleFinishTime() - status.getStartTime()));
      jobCounters.incrCounter(Counter.SLOTS_MILLIS_REDUCES_SORT,
                  tip.getNumSlotsRequired() *
                  (status.getSortFinishTime() - status.getShuffleFinishTime()));
      jobCounters.incrCounter(Counter.SLOTS_MILLIS_REDUCES_REDUCE,
                  tip.getNumSlotsRequired() *
                  (status.getFinishTime() - status.getSortFinishTime()));
    }
  }

  /**
   * Populate the data structures as a task is scheduled.
   *
   * Assuming {@link JobTracker} is locked on entry.
   *
   * @param tip The tip for which the task is added
   * @param id The attempt-id for the task
   * @param taskTracker task tracker name
   * @param hostName host name for the task tracker
   * @param isScheduled Whether this task is scheduled from the JT or has
   *        joined back upon restart
   */
  @SuppressWarnings("deprecation")
  private void addRunningTaskToTIPUnprotected(
      TaskInProgress tip, TaskAttemptID id, String taskTracker,
      String hostName, boolean isScheduled) {
    // Make an entry in the tip if the attempt is not scheduled i.e externally
    // added
    if (!isScheduled) {
      tip.addRunningTask(id, taskTracker);
    }

    // keeping the earlier ordering intact
    String name;
    String splits = "";
    Enum<Counter> counter = null;
    if (tip.isJobSetupTask()) {
      launchedSetup = true;
      name = Values.SETUP.name();
    } else if (tip.isJobCleanupTask()) {
      launchedCleanup = true;
      name = Values.CLEANUP.name();
    } else if (tip.isMapTask()) {
      ++runningMapTasks;
      name = Values.MAP.name();
      counter = Counter.TOTAL_LAUNCHED_MAPS;
      splits = tip.getSplitNodes();
      if (tip.getActiveTasks().size() > 1) {
        speculativeMapTasks++;
        jobStats.incNumSpeculativeMaps();
      }
      jobStats.incNumMapTasksLaunched();
    } else {
      ++runningReduceTasks;
      name = Values.REDUCE.name();
      counter = Counter.TOTAL_LAUNCHED_REDUCES;
      if (tip.getActiveTasks().size() > 1) {
        speculativeReduceTasks++;
        jobStats.incNumSpeculativeReduces();
      }
      jobStats.incNumReduceTasksLaunched();
    }
    // Note that the logs are for the scheduled tasks only. Tasks that join on
    // restart has already their logs in place.
    if (tip.isFirstAttempt(id)) {
      jobHistory.logTaskStarted(tip.getTIPId(), name,
                                 tip.getExecStartTime(), splits);
    }
    if (!tip.isJobSetupTask() && !tip.isJobCleanupTask()) {
      jobCounters.incrCounter(counter, 1);
    }

    if (tip.isMapTask() && !tip.isJobSetupTask() && !tip.isJobCleanupTask()) {
      localityStats.record(tip, hostName, -1);
    }
  }

  /**
   * Kill the job and all its component tasks.
   */
  public void kill() {
    terminate(JobStatus.KILLED);
  }

  void fail() {
    terminate(JobStatus.FAILED);
  }

  private void terminate(int jobTerminationState) {
    this.terminated = jobTerminationState;
    synchronized (lockObject) {
      terminateUnprotected(jobTerminationState);
    }
  }

  /**
   * Terminate the job and all its component tasks.
   * Calling this will lead to marking the job as failed/killed. Cleanup
   * tip will be launched. If the job has not inited, it will directly call
   * terminateJob as there is no need to launch cleanup tip.
   * This method is reentrant.
   * @param jobTerminationState job termination state
   */
  private void terminateUnprotected(int jobTerminationState) {
    this.terminated = jobTerminationState;
    if(!tasksInited) {
      //init could not be done, we just terminate directly.
      terminateJob(jobTerminationState);
      return;
    }

    if ((status.getRunState() == JobStatus.RUNNING) ||
         (status.getRunState() == JobStatus.PREP)) {
      LOG.info("Killing job '" + this.status.getJobID() + "'");
      if (jobTerminationState == JobStatus.FAILED) {
        if(jobFailed) {//reentrant
          return;
        }
        jobFailed = true;
      } else if (jobTerminationState == JobStatus.KILLED) {
        if(jobKilled) {//reentrant
          return;
        }
        jobKilled = true;
      }
      // clear all unclean tasks
      clearUncleanTasksUnprotected();
      //
      // kill all TIPs.
      //
      for (int i = 0; i < setup.length; i++) {
        setup[i].kill();
      }
      for (int i = 0; i < maps.length; i++) {
        maps[i].kill();
        TreeMap<TaskAttemptID, String> activeTasks = maps[i].getActiveTasksCopy();
        for (TaskAttemptID attempt : activeTasks.keySet()) {
          TaskStatus status = maps[i].getTaskStatus(attempt);
          if (status != null) {
            failedTask(maps[i], attempt, JOB_KILLED_REASON,
              status.getPhase(), false, status.getTaskTracker(), null);
          } else {
            failedTask(maps[i], attempt, JOB_KILLED_REASON,
              Phase.MAP, false, EMPTY_TRACKER_NAME, null);
          }
        }
      }
      for (int i = 0; i < reduces.length; i++) {
        reduces[i].kill();
        TreeMap<TaskAttemptID, String> activeTasks = reduces[i].getActiveTasksCopy();
        for (TaskAttemptID attempt : activeTasks.keySet()) {
          TaskStatus status = reduces[i].getTaskStatus(attempt);
          if (status != null) {
            failedTask(reduces[i], attempt, JOB_KILLED_REASON,
              status.getPhase(), false, status.getTaskTracker(), null);
          } else {
            failedTask(reduces[i], attempt, JOB_KILLED_REASON,
              Phase.REDUCE, false, EMPTY_TRACKER_NAME, null);
          }
        }
      }

      // Moved job to a terminal state if no job cleanup is needed. In case the
      // job is killed, we do not perform cleanup. This is because cleanup
      // cannot be guaranteed - the process running Corona JT could just be killed.
      if (!jobSetupCleanupNeeded || jobTerminationState == JobStatus.KILLED) {
        terminateJobUnprotected(jobTerminationState);
      }
    }
  }

  private void terminateJob(int jobTerminationState) {
    synchronized (lockObject) {
      terminateJobUnprotected(jobTerminationState);
    }
  }

  @SuppressWarnings("deprecation")
  private void terminateJobUnprotected(int jobTerminationState) {
    if ((status.getRunState() == JobStatus.RUNNING) ||
        (status.getRunState() == JobStatus.PREP)) {
      this.finishTime = clock.getTime();
      this.status.setMapProgress(1.0f);
      this.status.setReduceProgress(1.0f);
      this.status.setCleanupProgress(1.0f);

      Counters counters = getCounters();
      setExtendedMetricsCountersUnprotected(counters);
      if (jobTerminationState == JobStatus.FAILED) {
        changeStateTo(JobStatus.FAILED);

        // Log the job summary
        JobSummary.logJobSummary(this);

        // Log to job-history
        jobHistory.logFailed(finishTime,
                             this.finishedMapTasks,
                             this.finishedReduceTasks, counters);
      } else {
        changeStateTo(JobStatus.KILLED);

        // Log the job summary
        JobSummary.logJobSummary(this);

        // Log to job-history
        jobHistory.logKilled(finishTime,
                             this.finishedMapTasks,
                             this.finishedReduceTasks, counters);
      }
    }
  }

  @SuppressWarnings("deprecation")
  private void setExtendedMetricsCountersUnprotected(Counters counters) {
    counters.incrCounter("extMet", "submit_time",
        getLaunchTime() - getStartTime());
    for (int i = 0; i < setup.length; i++) {
      if (setup[i].isComplete()) {
        counters.incrCounter("extMet", "setup_time",
            setup[i].getExecFinishTime() - setup[i].getStartTime());
        break;
      }
    }
    for (int i = cleanup.length - 1; i >= 0; i--) {
      if (cleanup[i].isComplete()) {
        counters.incrCounter("extMet", "cleanup_time",
            cleanup[i].getExecFinishTime() - cleanup[i].getStartTime());
        break;
      }
    }
    long totalMapWaitTime = 0;
    long maxMapWaitTime = 0;
    long totalMaps = 0;
    for (int i = 0; i < maps.length; i++) {
      if (maps[i].isComplete()) {
        long waitTime = maps[i].getExecStartTime() - getLaunchTime();
        if (waitTime > maxMapWaitTime)
          maxMapWaitTime = waitTime;
        totalMapWaitTime += waitTime;
        ++totalMaps;
      }
    }
    counters.incrCounter("extMet", "avg_map_wait_time",
        totalMaps > 0 ? (totalMapWaitTime / totalMaps) : 0);
    counters.incrCounter("extMet", "max_map_wait_time",
        maxMapWaitTime);
  }

  /**
   * Remove a map TIP from the lists for running maps.
   * Called when a map fails/completes (note if a map is killed,
   * it won't be present in the list since it was completed earlier)
   * @param tip the tip that needs to be retired
   */
  private void retireMapUnprotected(TaskInProgress tip) {
    if (runningMaps == null) {
      LOG.warn("Running cache for maps missing!! "
               + "Job details are missing.");
      return;
    }
    runningMaps.remove(tip);
  }

  /**
   * Remove a reduce TIP from the list for running-reduces
   * Called when a reduce fails/completes
   * @param tip the tip that needs to be retired
   */
  private void retireReduceUnprotected(TaskInProgress tip) {
    if (runningReduces == null) {
      LOG.warn("Running list for reducers missing!! "
               + "Job details are missing.");
      return;
    }
    runningReduces.remove(tip);
  }

  protected void scheduleMapUnprotected(TaskInProgress tip) {
    runningMapTaskStats.add(0.0f);
    runningMaps.add(tip);
  }

  protected void scheduleReduceUnprotected(TaskInProgress tip) {
    runningReduceTaskStats.add(0.0f);
    runningReduces.add(tip);
  }

  /**
   * Check if the job needs the JobCleanup task launched.
   * @return true if the job needs a cleanup task launched, false otherwise
   */
  public boolean canLaunchJobCleanupTask() {
    synchronized (lockObject) {
      return canLaunchJobCleanupTaskUnprotected();
    }
  }

  /**
   * Check whether cleanup task can be launched for the job.
   *
   * Cleanup task can be launched if it is not already launched
   * or job is Killed
   * or all maps and reduces are complete
   * @return true/false
   */
  private boolean canLaunchJobCleanupTaskUnprotected() {
    // check if the job is running
    if (status.getRunState() != JobStatus.RUNNING &&
        status.getRunState() != JobStatus.PREP) {
      return false;
    }
    // check if cleanup task has been launched already or if setup isn't
    // launched already. The later check is useful when number of maps is
    // zero.
    if (launchedCleanup || !isSetupFinishedUnprotected()) {
      return false;
    }
    // check if job has failed or killed
    if (jobKilled || jobFailed) {
      return true;
    }

    boolean mapsDone = ((finishedMapTasks + failedMapTIPs) == (numMapTasks));
    boolean reducesDone = ((finishedReduceTasks + failedReduceTIPs) == numReduceTasks);
    boolean mapOnlyJob = (numReduceTasks == 0);

    if (mapOnlyJob) {
      return mapsDone;
    }
    if (jobFinishWhenReducesDone) {
      return reducesDone;
    }
    return mapsDone && reducesDone;
  }

  boolean isSetupFinishedUnprotected() {
    // if there is no setup to be launched, consider setup is finished.
    if ((tasksInited && setup.length == 0) ||
        setup[0].isComplete() || setup[0].isFailed() || setup[1].isComplete()
        || setup[1].isFailed()) {
      return true;
    }
    return false;
  }


  /**
   *  Returns the total job counters, by adding together the job,
   *  the map and the reduce counters.
   */
  @SuppressWarnings("deprecation")
  public Counters getCounters() {
    synchronized (lockObject) {
      Counters result = new Counters();
      result.incrAllCounters(getJobCounters());

      incrementTaskCountersUnprotected(result, maps);
      return incrementTaskCountersUnprotected(result, reduces);
    }
  }

  public Counters getErrorCounters() {
    return taskErrorCollector.getErrorCountsCounters();
  }

  public Object getSchedulingInfo() {
    return null; // TODO
  }

  boolean isJobEmpty() {
    return maps.length == 0 && reduces.length == 0 && !jobSetupCleanupNeeded;
  }

  void completeEmptyJob() {
    jobComplete();
  }

  void completeSetup() {
    synchronized (lockObject) {
      setupCompleteUnprotected();
    }
  }

  /**
   * Increments the counters with the counters from each task.
   * @param counters the counters to increment
   * @param tips the tasks to add in to counters
   * @return counters the same object passed in as counters
   */
  @SuppressWarnings("deprecation")
  private Counters incrementTaskCountersUnprotected(Counters counters,
                                         TaskInProgress[] tips) {
    for (TaskInProgress tip : tips) {
      counters.incrAllCounters(tip.getCounters());
    }
    return counters;
  }

  static class JobSummary {
    static final Log LOG = LogFactory.getLog(JobSummary.class);

    // Escape sequences
    static final char EQUALS = '=';
    static final char[] charsToEscape =
      {StringUtils.COMMA, EQUALS, StringUtils.ESCAPE_CHAR};

    /**
     * Log a summary of the job's runtime.
     *
     * @param job {@link JobInProgress} whose summary is to be logged, cannot
     *            be <code>null</code>.
     */
    @SuppressWarnings("deprecation")
    public static void logJobSummary(CoronaJobInProgress job) {
      JobStatus status = job.getStatus();
      JobProfile profile = job.getProfile();
      String user = StringUtils.escapeString(profile.getUser(),
                                             StringUtils.ESCAPE_CHAR,
                                             charsToEscape);
      String queue = StringUtils.escapeString(profile.getQueueName(),
                                              StringUtils.ESCAPE_CHAR,
                                              charsToEscape);
      Counters jobCounters = job.getJobCounters();
      long mapSlotSeconds =
        (jobCounters.getCounter(Counter.SLOTS_MILLIS_MAPS) +
         jobCounters.getCounter(Counter.FALLOW_SLOTS_MILLIS_MAPS)) / 1000;
      long reduceSlotSeconds =
        (jobCounters.getCounter(Counter.SLOTS_MILLIS_REDUCES) +
         jobCounters.getCounter(Counter.FALLOW_SLOTS_MILLIS_REDUCES)) / 1000;

      LOG.info("jobId=" + profile.getJobID() + StringUtils.COMMA +
               "submitTime" + EQUALS + job.getStartTime() + StringUtils.COMMA +
               "launchTime" + EQUALS + job.getLaunchTime() + StringUtils.COMMA +
               "finishTime" + EQUALS + job.getFinishTime() + StringUtils.COMMA +
               "numMaps" + EQUALS + job.getTasks(TaskType.MAP).length +
                           StringUtils.COMMA +
               "numSlotsPerMap" + EQUALS + NUM_SLOTS_PER_MAP +
                                  StringUtils.COMMA +
               "numReduces" + EQUALS + job.getTasks(TaskType.REDUCE).length +
                              StringUtils.COMMA +
               "numSlotsPerReduce" + EQUALS + NUM_SLOTS_PER_REDUCE +
                                     StringUtils.COMMA +
               "user" + EQUALS + user + StringUtils.COMMA +
               "queue" + EQUALS + queue + StringUtils.COMMA +
               "status" + EQUALS +
                          JobStatus.getJobRunState(status.getRunState()) +
                          StringUtils.COMMA +
               "mapSlotSeconds" + EQUALS + mapSlotSeconds + StringUtils.COMMA +
               "reduceSlotsSeconds" + EQUALS + reduceSlotSeconds  +
                                      StringUtils.COMMA
      );
    }
  }

  @Override
  public boolean inited() {
    return tasksInited;
  }

  private void updateTaskTrackerStats(TaskInProgress tip, TaskTrackerInfo ttStatus,
      Map<String,DataStatistics> trackerStats, DataStatistics overallStats) {
    synchronized (lockObject) {
      float tipDuration = tip.getExecFinishTime() -
      tip.getDispatchTime(tip.getSuccessfulTaskid());
      DataStatistics ttStats =
        trackerStats.get(ttStatus.getTrackerName());
      double oldMean = 0.0d;
      //We maintain the mean of TaskTrackers' means. That way, we get a single
      //data-point for every tracker (used in the evaluation in isSlowTracker)
      if (ttStats != null) {
        oldMean = ttStats.mean();
        ttStats.add(tipDuration);
        overallStats.updateStatistics(oldMean, ttStats.mean());
      } else {
        trackerStats.put(ttStatus.getTrackerName(),
            (ttStats = new DataStatistics(tipDuration)));
        overallStats.add(tipDuration);
      }
      if (LOG.isDebugEnabled()) {
        LOG.debug("Added mean of " +ttStats.mean() + " to trackerStats of type "+
            (tip.isMapTask() ? "Map" : "Reduce") +
            " on "+ttStatus.getTrackerName()+". DataStatistics is now: " +
            trackerStats.get(ttStatus.getTrackerName()));
      }
    }
  }

  @Override
  public Vector<TaskInProgress> reportTasksInProgress(boolean shouldBeMap,
                                                      boolean shouldBeComplete) {
    synchronized (lockObject) {
      return super.reportTasksInProgress(shouldBeMap, shouldBeComplete);
    }
  }

  @Override
  public Vector<TaskInProgress> reportCleanupTIPs(boolean shouldBeComplete) {
    synchronized (lockObject) {
      return super.reportCleanupTIPs(shouldBeComplete);
    }
  }

  @Override
  public Vector<TaskInProgress> reportSetupTIPs(boolean shouldBeComplete) {
    synchronized (lockObject) {
      return super.reportSetupTIPs(shouldBeComplete);
    }
  }

  @Override
  public TaskInProgress getTaskInProgress(TaskID tipid) {
    synchronized (lockObject) {
      return super.getTaskInProgress(tipid);
    }
  }

  public Configuration getConf() {
    return jobConf;
  }

  @Override
  public boolean shouldLogCannotspeculativeMaps() {
    long now = clock.getTime();
    if ((now - lastTimeCannotspeculativeMapLog) <= logCannotspeculativeInterval)
        return false;
    int unfinished = numMapTasks - finishedMapTasks;
    if (unfinished <= numMapTasks * speculativeMapLogRateThreshold ||
        unfinished <= speculativeMapLogNumThreshold) {
        lastTimeCannotspeculativeMapLog = now;
        return true;
    }
    return false;
  }

  @Override
  public boolean shouldLogCannotspeculativeReduces() {
    long now = clock.getTime();
    if ((now - lastTimeCannotspeculativeReduceLog) <= logCannotspeculativeInterval)
      return false;
    int unfinished = numReduceTasks - finishedReduceTasks;
    if (unfinished <= numReduceTasks * speculativeReduceLogRateThreshold ||
        unfinished <= speculativeReduceLogNumThreshold) {
        lastTimeCannotspeculativeReduceLog = now;
        return true;
    }
    return false;
  }

  @Override
  public boolean shouldSpeculateAllRemainingMaps() {
    int unfinished = numMapTasks - finishedMapTasks;
    if (unfinished < numMapTasks * speculativeMapUnfininshedThreshold ||
        unfinished == 1) {
      return true;
    }
    return false;
  }

  @Override
  public boolean shouldSpeculateAllRemainingReduces() {
    int unfinished = numReduceTasks - finishedReduceTasks;
    if (unfinished < numReduceTasks * speculativeReduceUnfininshedThreshold ||
        unfinished == 1) {
      return true;
    }
    return false;
  }

  @Override
  DataStatistics getRunningTaskStatistics(Phase phase) {
    throw new RuntimeException("Not yet implemented.");
  }

  public static void uploadCachedSplits(JobID jobId, JobConf jobConf,
      String systemDir) throws IOException {
    Path jobDir = new Path(systemDir, jobId.toString());
    Path splitFile = new Path(jobDir, "job.split");
    LOG.info("Uploading splits file for " + jobId + " to " + splitFile);
    List<JobClient.RawSplit> splits = Arrays.asList(JobClient
        .getAndRemoveCachedSplits(jobId));
    JobClient.writeComputedSplits(jobConf, splits, splitFile);
  }
}
TOP

Related Classes of org.apache.hadoop.mapred.CoronaJobInProgress$JobSummary

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.