From 769178f3c0e94f3285446634acff001fe09691a3 Mon Sep 17 00:00:00 2001 From: Siddharth Seth Date: Wed, 21 Dec 2011 23:36:14 +0000 Subject: [PATCH] MAPREDUCE-3349. Log rack-name in JobHistory for unsuccessful tasks. (Contributed by Amar Kamat and Devaraj K) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-0.23@1221939 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-mapreduce-project/CHANGES.txt | 3 + .../v2/app/job/impl/TaskAttemptImpl.java | 2 + .../src/main/avro/Events.avpr | 2 + .../jobhistory/JobHistoryParser.java | 3 +- .../jobhistory/MapAttemptFinishedEvent.java | 11 ++- .../ReduceAttemptFinishedEvent.java | 10 +- .../jobhistory/TaskAttemptFinishedEvent.java | 11 ++- ...askAttemptUnsuccessfulCompletionEvent.java | 18 +++- .../v2/hs/TestJobHistoryParsing.java | 89 ++++++++++++++++- .../apache/hadoop/mapred/JobInProgress.java | 10 +- .../jobhistory/TestJobHistoryEvents.java | 5 +- .../tools/rumen/TestRumenJobTraces.java | 33 ++++--- .../counters-test-trace.json.gz | Bin 2985 -> 3013 bytes .../dispatch-trace-output.json.gz | Bin 27850 -> 28317 bytes .../job-tracker-logs-topology-output | 91 ++++++++++++++++-- .../tools/rumen/HadoopLogsAnalyzer.java | 12 ++- .../apache/hadoop/tools/rumen/JobBuilder.java | 42 ++++++-- .../apache/hadoop/tools/rumen/ParsedHost.java | 14 ++- .../rumen/TaskAttempt20LineEventEmitter.java | 16 ++- .../hadoop/tools/rumen/TopologyBuilder.java | 39 +++++++- 20 files changed, 348 insertions(+), 63 deletions(-) diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index d3273af0992..979af1c701e 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -300,6 +300,9 @@ Release 0.23.1 - Unreleased MAPREDUCE-3588. Fixed bin/yarn which was broken by MAPREDUCE-3366 so that yarn daemons can start. (Arun C Murthy via vinodkv) + MAPREDUCE-3349. Log rack-name in JobHistory for unsuccessful tasks. (Amar + Kamat and Devaraj K via sseth) + Release 0.23.0 - 2011-11-01 INCOMPATIBLE CHANGES diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java index 3a52c11d2a5..46479ee142c 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/job/impl/TaskAttemptImpl.java @@ -926,6 +926,8 @@ private static JobCounterUpdateEvent createJobCounterUpdateEventTAFailed( : taskAttempt.containerNodeId.getHost(), taskAttempt.containerNodeId == null ? -1 : taskAttempt.containerNodeId.getPort(), + taskAttempt.nodeRackName == null ? "UNKNOWN" + : taskAttempt.nodeRackName, StringUtils.join( LINE_SEPARATOR, taskAttempt.getDiagnostics()), taskAttempt .getProgressSplitBlock().burst()); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/avro/Events.avpr b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/avro/Events.avpr index 34c40900e6b..22864a6fc2f 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/avro/Events.avpr +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/avro/Events.avpr @@ -175,6 +175,7 @@ {"name": "taskType", "type": "string"}, {"name": "taskStatus", "type": "string"}, {"name": "finishTime", "type": "long"}, + {"name": "rackname", "type": "string"}, {"name": "hostname", "type": "string"}, {"name": "state", "type": "string"}, {"name": "counters", "type": "JhCounters"} @@ -202,6 +203,7 @@ {"name": "finishTime", "type": "long"}, {"name": "hostname", "type": "string"}, {"name": "port", "type": "int"}, + {"name": "rackname", "type": "string"}, {"name": "status", "type": "string"}, {"name": "error", "type": "string"}, {"name": "clockSplits", "type": { "type": "array", "items": "int"}}, diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryParser.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryParser.java index a12db54afc5..c9be77c29d8 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryParser.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryParser.java @@ -224,7 +224,7 @@ private void handleMapAttemptFinishedEvent(MapAttemptFinishedEvent event) { attemptInfo.counters = event.getCounters(); attemptInfo.hostname = event.getHostname(); attemptInfo.port = event.getPort(); - attemptInfo.rackname = event.getRackname(); + attemptInfo.rackname = event.getRackName(); } private void handleTaskAttemptFailedEvent( @@ -237,6 +237,7 @@ private void handleTaskAttemptFailedEvent( attemptInfo.status = event.getTaskStatus(); attemptInfo.hostname = event.getHostname(); attemptInfo.port = event.getPort(); + attemptInfo.rackname = event.getRackName(); attemptInfo.shuffleFinishTime = event.getFinishTime(); attemptInfo.sortFinishTime = event.getFinishTime(); attemptInfo.mapFinishTime = event.getFinishTime(); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/MapAttemptFinishedEvent.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/MapAttemptFinishedEvent.java index cdeecb0b987..5838c971f37 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/MapAttemptFinishedEvent.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/MapAttemptFinishedEvent.java @@ -68,7 +68,10 @@ public class MapAttemptFinishedEvent implements HistoryEvent { datum.finishTime = finishTime; datum.hostname = new Utf8(hostname); datum.port = port; - datum.rackname = new Utf8(rackName); + // This is needed for reading old jh files + if (rackName != null) { + datum.rackname = new Utf8(rackName); + } datum.state = new Utf8(state); datum.counters = EventWriter.toAvro(counters); @@ -139,8 +142,12 @@ public TaskType getTaskType() { public String getHostname() { return datum.hostname.toString(); } /** Get the tracker rpc port */ public int getPort() { return datum.port; } + /** Get the rack name */ - public String getRackname() { return datum.rackname.toString(); } + public String getRackName() { + return datum.rackname == null ? null : datum.rackname.toString(); + } + /** Get the state string */ public String getState() { return datum.state.toString(); } /** Get the counters */ diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/ReduceAttemptFinishedEvent.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/ReduceAttemptFinishedEvent.java index 1eadb274cc9..99b2212316c 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/ReduceAttemptFinishedEvent.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/ReduceAttemptFinishedEvent.java @@ -69,7 +69,9 @@ public class ReduceAttemptFinishedEvent implements HistoryEvent { datum.finishTime = finishTime; datum.hostname = new Utf8(hostname); datum.port = port; - datum.rackname = new Utf8(rackName); + if (rackName != null) { + datum.rackname = new Utf8(rackName); + } datum.state = new Utf8(state); datum.counters = EventWriter.toAvro(counters); @@ -142,8 +144,12 @@ public TaskType getTaskType() { public String getHostname() { return datum.hostname.toString(); } /** Get the tracker rpc port */ public int getPort() { return datum.port; } + /** Get the rack name of the node where the attempt ran */ - public String getRackName() { return datum.rackname.toString(); } + public String getRackName() { + return datum.rackname == null ? null : datum.rackname.toString(); + } + /** Get the state string */ public String getState() { return datum.state.toString(); } /** Get the counters for the attempt */ diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptFinishedEvent.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptFinishedEvent.java index fff9232e5a7..9938182fc6c 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptFinishedEvent.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptFinishedEvent.java @@ -51,13 +51,16 @@ public class TaskAttemptFinishedEvent implements HistoryEvent { */ public TaskAttemptFinishedEvent(TaskAttemptID id, TaskType taskType, String taskStatus, - long finishTime, + long finishTime, String rackName, String hostname, String state, Counters counters) { datum.taskid = new Utf8(id.getTaskID().toString()); datum.attemptId = new Utf8(id.toString()); datum.taskType = new Utf8(taskType.name()); datum.taskStatus = new Utf8(taskStatus); datum.finishTime = finishTime; + if (rackName != null) { + datum.rackname = new Utf8(rackName); + } datum.hostname = new Utf8(hostname); datum.state = new Utf8(state); datum.counters = EventWriter.toAvro(counters); @@ -86,6 +89,12 @@ public TaskType getTaskType() { public long getFinishTime() { return datum.finishTime; } /** Get the host where the attempt executed */ public String getHostname() { return datum.hostname.toString(); } + + /** Get the rackname where the attempt executed */ + public String getRackName() { + return datum.rackname == null ? null : datum.rackname.toString(); + } + /** Get the state string */ public String getState() { return datum.state.toString(); } /** Get the counters for the attempt */ diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptUnsuccessfulCompletionEvent.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptUnsuccessfulCompletionEvent.java index 56cb0e34633..066f0af6466 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptUnsuccessfulCompletionEvent.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/jobhistory/TaskAttemptUnsuccessfulCompletionEvent.java @@ -47,6 +47,7 @@ public class TaskAttemptUnsuccessfulCompletionEvent implements HistoryEvent { * @param finishTime Finish time of the attempt * @param hostname Name of the host where the attempt executed * @param port rpc port for for the tracker + * @param rackName Name of the rack where the attempt executed * @param error Error string * @param allSplits the "splits", or a pixelated graph of various * measurable worker node state variables against progress. @@ -55,14 +56,17 @@ public class TaskAttemptUnsuccessfulCompletionEvent implements HistoryEvent { */ public TaskAttemptUnsuccessfulCompletionEvent (TaskAttemptID id, TaskType taskType, - String status, long finishTime, - String hostname, int port, String error, - int[][] allSplits) { + String status, long finishTime, + String hostname, int port, String rackName, + String error, int[][] allSplits) { datum.taskid = new Utf8(id.getTaskID().toString()); datum.taskType = new Utf8(taskType.name()); datum.attemptId = new Utf8(id.toString()); datum.finishTime = finishTime; datum.hostname = new Utf8(hostname); + if (rackName != null) { + datum.rackname = new Utf8(rackName); + } datum.port = port; datum.error = new Utf8(error); datum.status = new Utf8(status); @@ -99,7 +103,7 @@ public class TaskAttemptUnsuccessfulCompletionEvent implements HistoryEvent { (TaskAttemptID id, TaskType taskType, String status, long finishTime, String hostname, String error) { - this(id, taskType, status, finishTime, hostname, -1, error, null); + this(id, taskType, status, finishTime, hostname, -1, null, error, null); } TaskAttemptUnsuccessfulCompletionEvent() {} @@ -125,6 +129,12 @@ public TaskAttemptID getTaskAttemptId() { public String getHostname() { return datum.hostname.toString(); } /** Get the rpc port for the host where the attempt executed */ public int getPort() { return datum.port; } + + /** Get the rack name of the node where the attempt ran */ + public String getRackName() { + return datum.rackname == null ? null : datum.rackname.toString(); + } + /** Get the error string */ public String getError() { return datum.error.toString(); } /** Get the task status */ diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryParsing.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryParsing.java index 08563f0091c..3755eba9466 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryParsing.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryParsing.java @@ -44,10 +44,13 @@ import org.apache.hadoop.mapreduce.jobhistory.JobHistoryParser.TaskInfo; import org.apache.hadoop.mapreduce.v2.api.records.JobId; import org.apache.hadoop.mapreduce.v2.api.records.JobState; +import org.apache.hadoop.mapreduce.v2.api.records.TaskAttemptId; import org.apache.hadoop.mapreduce.v2.app.MRApp; import org.apache.hadoop.mapreduce.v2.app.job.Job; import org.apache.hadoop.mapreduce.v2.app.job.Task; import org.apache.hadoop.mapreduce.v2.app.job.TaskAttempt; +import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEvent; +import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEventType; import org.apache.hadoop.mapreduce.v2.hs.TestJobHistoryEvents.MRAppWithHistory; import org.apache.hadoop.mapreduce.v2.jobhistory.FileNameIndexUtils; import org.apache.hadoop.mapreduce.v2.jobhistory.JobHistoryUtils; @@ -62,10 +65,12 @@ public class TestJobHistoryParsing { private static final Log LOG = LogFactory.getLog(TestJobHistoryParsing.class); + private static final String RACK_NAME = "/MyRackName"; + public static class MyResolver implements DNSToSwitchMapping { @Override public List resolve(List names) { - return Arrays.asList(new String[]{"/MyRackName"}); + return Arrays.asList(new String[]{RACK_NAME}); } } @@ -172,7 +177,7 @@ public void testHistoryParsing() throws Exception { // Verify rack-name Assert.assertEquals("rack-name is incorrect", taskAttemptInfo - .getRackname(), "/MyRackName"); + .getRackname(), RACK_NAME); } } @@ -217,9 +222,89 @@ public void testHistoryParsing() throws Exception { Assert.assertEquals("Status does not match", "SUCCEEDED", jobSummaryElements.get("status")); } + + @Test + public void testHistoryParsingForFailedAttempts() throws Exception { + Configuration conf = new Configuration(); + conf + .setClass( + CommonConfigurationKeysPublic.NET_TOPOLOGY_NODE_SWITCH_MAPPING_IMPL_KEY, + MyResolver.class, DNSToSwitchMapping.class); + RackResolver.init(conf); + MRApp app = new MRAppWithHistoryWithFailedAttempt(2, 1, true, this.getClass().getName(), + true); + app.submit(conf); + Job job = app.getContext().getAllJobs().values().iterator().next(); + JobId jobId = job.getID(); + app.waitForState(job, JobState.SUCCEEDED); + + // make sure all events are flushed + app.waitForState(Service.STATE.STOPPED); + + String jobhistoryDir = JobHistoryUtils + .getHistoryIntermediateDoneDirForUser(conf); + JobHistory jobHistory = new JobHistory(); + jobHistory.init(conf); + + JobIndexInfo jobIndexInfo = jobHistory.getJobMetaInfo(jobId) + .getJobIndexInfo(); + String jobhistoryFileName = FileNameIndexUtils + .getDoneFileName(jobIndexInfo); + + Path historyFilePath = new Path(jobhistoryDir, jobhistoryFileName); + FSDataInputStream in = null; + FileContext fc = null; + try { + fc = FileContext.getFileContext(conf); + in = fc.open(fc.makeQualified(historyFilePath)); + } catch (IOException ioe) { + LOG.info("Can not open history file: " + historyFilePath, ioe); + throw (new Exception("Can not open History File")); + } + + JobHistoryParser parser = new JobHistoryParser(in); + JobInfo jobInfo = parser.parse(); + int noOffailedAttempts = 0; + Map allTasks = jobInfo.getAllTasks(); + for (Task task : job.getTasks().values()) { + TaskInfo taskInfo = allTasks.get(TypeConverter.fromYarn(task.getID())); + for (TaskAttempt taskAttempt : task.getAttempts().values()) { + TaskAttemptInfo taskAttemptInfo = taskInfo.getAllTaskAttempts().get( + TypeConverter.fromYarn((taskAttempt.getID()))); + // Verify rack-name for all task attempts + Assert.assertEquals("rack-name is incorrect", taskAttemptInfo + .getRackname(), RACK_NAME); + if (taskAttemptInfo.getTaskStatus().equals("FAILED")) { + noOffailedAttempts++; + } + } + } + Assert.assertEquals("No of Failed tasks doesn't match.", 2, noOffailedAttempts); + } + + static class MRAppWithHistoryWithFailedAttempt extends MRAppWithHistory { + + public MRAppWithHistoryWithFailedAttempt(int maps, int reduces, boolean autoComplete, + String testName, boolean cleanOnStart) { + super(maps, reduces, autoComplete, testName, cleanOnStart); + } + + @SuppressWarnings("unchecked") + @Override + protected void attemptLaunched(TaskAttemptId attemptID) { + if (attemptID.getTaskId().getId() == 0 && attemptID.getId() == 0) { + getContext().getEventHandler().handle( + new TaskAttemptEvent(attemptID, TaskAttemptEventType.TA_FAILMSG)); + } else { + getContext().getEventHandler().handle( + new TaskAttemptEvent(attemptID, TaskAttemptEventType.TA_DONE)); + } + } + } public static void main(String[] args) throws Exception { TestJobHistoryParsing t = new TestJobHistoryParsing(); t.testHistoryParsing(); + t.testHistoryParsingForFailedAttempts(); } } diff --git a/hadoop-mapreduce-project/src/java/org/apache/hadoop/mapred/JobInProgress.java b/hadoop-mapreduce-project/src/java/org/apache/hadoop/mapred/JobInProgress.java index fc8bdc624b7..791b92c82f7 100644 --- a/hadoop-mapreduce-project/src/java/org/apache/hadoop/mapred/JobInProgress.java +++ b/hadoop-mapreduce-project/src/java/org/apache/hadoop/mapred/JobInProgress.java @@ -2671,7 +2671,9 @@ public synchronized boolean completedTask(TaskInProgress tip, // Update jobhistory TaskTrackerStatus ttStatus = this.jobtracker.getTaskTrackerStatus(status.getTaskTracker()); - String trackerHostname = jobtracker.getNode(ttStatus.getHost()).toString(); + Node node = jobtracker.getNode(ttStatus.getHost()); + String trackerHostname = node.getName(); + String trackerRackName = node.getParent().getName(); TaskType taskType = getTaskType(tip); TaskAttemptStartedEvent tse = new TaskAttemptStartedEvent( @@ -2685,7 +2687,7 @@ public synchronized boolean completedTask(TaskInProgress tip, MapAttemptFinishedEvent mfe = new MapAttemptFinishedEvent( statusAttemptID, taskType, TaskStatus.State.SUCCEEDED.toString(), status.getMapFinishTime(), - status.getFinishTime(), trackerHostname, -1, "", + status.getFinishTime(), trackerHostname, -1, trackerRackName, status.getStateString(), new org.apache.hadoop.mapreduce.Counters(status.getCounters()), tip.getSplits(statusAttemptID).burst() @@ -2698,7 +2700,7 @@ public synchronized boolean completedTask(TaskInProgress tip, statusAttemptID, taskType, TaskStatus.State.SUCCEEDED.toString(), status.getShuffleFinishTime(), status.getSortFinishTime(), status.getFinishTime(), - trackerHostname, -1, "", status.getStateString(), + trackerHostname, -1, trackerRackName, status.getStateString(), new org.apache.hadoop.mapreduce.Counters(status.getCounters()), tip.getSplits(statusAttemptID).burst() ); @@ -3208,7 +3210,7 @@ private void failedTask(TaskInProgress tip, TaskAttemptID taskid, (taskid, taskType, taskStatus.getRunState().toString(), finishTime, - taskTrackerHostName, -1, diagInfo, + taskTrackerHostName, -1, null, diagInfo, splits.burst()); jobHistory.logEvent(tue, taskid.getJobID()); diff --git a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/mapreduce/jobhistory/TestJobHistoryEvents.java b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/mapreduce/jobhistory/TestJobHistoryEvents.java index 27c149de462..c297ac06e3f 100644 --- a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/mapreduce/jobhistory/TestJobHistoryEvents.java +++ b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/mapreduce/jobhistory/TestJobHistoryEvents.java @@ -83,7 +83,7 @@ private static void testFailedKilledEventsForTypes(EventType expected, for (TaskType t : types) { TaskAttemptUnsuccessfulCompletionEvent tauce = new TaskAttemptUnsuccessfulCompletionEvent - (id, t, state, 0L, "", -1, "", NULL_SPLITS_ARRAY); + (id, t, state, 0L, "", -1, "", "", NULL_SPLITS_ARRAY); assertEquals(expected, tauce.getEventType()); } } @@ -132,7 +132,8 @@ private static void testFinishedEventsForTypes(EventType expected, for (TaskType t : types) { TaskAttemptFinishedEvent tafe = new TaskAttemptFinishedEvent(id, t, - TaskStatus.State.SUCCEEDED.toString(), 0L, "", "", new Counters()); + TaskStatus.State.SUCCEEDED.toString(), 0L, "", "", "", + new Counters()); assertEquals(expected, tafe.getEventType()); } } diff --git a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java index 0f2149125a8..c93414d3674 100644 --- a/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java +++ b/hadoop-mapreduce-project/src/test/mapred/org/apache/hadoop/tools/rumen/TestRumenJobTraces.java @@ -42,6 +42,7 @@ import org.apache.hadoop.mapreduce.TaskCounter; import org.apache.hadoop.mapreduce.TaskID; import org.apache.hadoop.mapreduce.TaskType; +import org.apache.hadoop.mapreduce.TypeConverter; import org.apache.hadoop.mapreduce.TestNoJobSetupCleanup.MyOutputFormat; import org.apache.hadoop.mapreduce.jobhistory.HistoryEvent; import org.apache.hadoop.mapreduce.jobhistory.JobHistory; @@ -49,6 +50,9 @@ import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptUnsuccessfulCompletionEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskStartedEvent; import org.apache.hadoop.mapreduce.server.tasktracker.TTConfig; +import org.apache.hadoop.mapreduce.v2.api.records.JobId; +import org.apache.hadoop.mapreduce.v2.jobhistory.FileNameIndexUtils; +import org.apache.hadoop.mapreduce.v2.jobhistory.JobIndexInfo; import org.apache.hadoop.tools.rumen.TraceBuilder.MyOptions; import org.apache.hadoop.util.Tool; import org.apache.hadoop.util.ToolRunner; @@ -246,8 +250,10 @@ public void testHadoop20JHParser() throws Exception { } /** - * Validate the parsing of given history file name. Also validate the history - * file name suffixed with old/stale file suffix. + * Validate the parsing of given history file name. + * + * TODO: Also validate the history file name suffixed with old/stale file + * suffix. * @param jhFileName job history file path * @param jid JobID */ @@ -257,13 +263,7 @@ private void validateHistoryFileNameParsing(Path jhFileName, JobID.forName(JobHistoryUtils.extractJobID(jhFileName.getName())); assertEquals("TraceBuilder failed to parse the current JH filename" + jhFileName, jid, extractedJID); - // test jobhistory filename with old/stale file suffix - jhFileName = jhFileName.suffix(JobHistory.getOldFileSuffix("123")); - extractedJID = - JobID.forName(JobHistoryUtils.extractJobID(jhFileName.getName())); - assertEquals("TraceBuilder failed to parse the current JH filename" - + "(old-suffix):" + jhFileName, - jid, extractedJID); + //TODO test jobhistory filename with old/stale file suffix } /** @@ -318,8 +318,9 @@ public void testJobHistoryFilenameParsing() throws IOException { .makeQualified(lfs.getUri(), lfs.getWorkingDirectory()); // Check if current jobhistory filenames are detected properly - Path jhFilename = org.apache.hadoop.mapreduce.v2.jobhistory.JobHistoryUtils - .getStagingJobHistoryFile(rootInputDir, jid.toString(), 1); + JobId jobId = TypeConverter.toYarn(jid); + JobIndexInfo info = new JobIndexInfo(0L, 0L, "", "", jobId, 0, 0, ""); + Path jhFilename = new Path(FileNameIndexUtils.getDoneFileName(info)); validateHistoryFileNameParsing(jhFilename, jid); // Check if Pre21 V1 jophistory file names are detected properly @@ -932,18 +933,18 @@ public void testTopologyBuilder() throws Exception { subject.process(new TaskAttemptFinishedEvent(TaskAttemptID .forName("attempt_200904211745_0003_m_000004_0"), TaskType .valueOf("MAP"), "STATUS", 1234567890L, - "/194\\.6\\.134\\.64/cluster50261\\.secondleveldomain\\.com", + "/194\\.6\\.134\\.64", "cluster50261\\.secondleveldomain\\.com", "SUCCESS", null)); subject.process(new TaskAttemptUnsuccessfulCompletionEvent (TaskAttemptID.forName("attempt_200904211745_0003_m_000004_1"), TaskType.valueOf("MAP"), "STATUS", 1234567890L, - "/194\\.6\\.134\\.80/cluster50262\\.secondleveldomain\\.com", - -1, "MACHINE_EXPLODED", splits)); + "cluster50262\\.secondleveldomain\\.com", + -1, "/194\\.6\\.134\\.80", "MACHINE_EXPLODED", splits)); subject.process(new TaskAttemptUnsuccessfulCompletionEvent (TaskAttemptID.forName("attempt_200904211745_0003_m_000004_2"), TaskType.valueOf("MAP"), "STATUS", 1234567890L, - "/194\\.6\\.134\\.80/cluster50263\\.secondleveldomain\\.com", - -1, "MACHINE_EXPLODED", splits)); + "cluster50263\\.secondleveldomain\\.com", + -1, "/194\\.6\\.134\\.80", "MACHINE_EXPLODED", splits)); subject.process(new TaskStartedEvent(TaskID .forName("task_200904211745_0003_m_000004"), 1234567890L, TaskType .valueOf("MAP"), diff --git a/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/counters-test-trace.json.gz b/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/counters-test-trace.json.gz index c1eb1b3d070a23df9749008ffa71d06c6e0f7174..47ee42869198b842622e9b35cf1811f46f9a6f5e 100644 GIT binary patch literal 3013 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 HK864QJBaGc delta 2984 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 ew_rzWhW${#VGS?qH~b$=(-9KIg#ZATH`|2( diff --git a/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/dispatch-trace-output.json.gz b/hadoop-mapreduce-project/src/test/tools/data/rumen/small-trace-test/dispatch-trace-output.json.gz index 98cfbdb844b80a04f4d3ec21d0540552c8839bd8..355e143bb76b80d5e48901603e59bb3ce0e539b9 100644 GIT binary patch literal 28317 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%0Am<*d&e58>inI}*)O2t|=cioWL#}l$ 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%0D}ZQ*{`I*95*? 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 locs = task.getPreferredLocations(); @@ -1491,9 +1493,13 @@ private void processReduceAttemptLine(ParsedLine line) { } } - ParsedHost host = getAndRecordParsedHost(hostName); - if (host != null) { - attempt.setHostName(host.getNodeName(), host.getRackName()); + if (hostName != null) { + ParsedHost host = getAndRecordParsedHost(hostName); + if (host != null) { + attempt.setHostName(host.getNodeName(), host.getRackName()); + } else { + attempt.setHostName(hostName, null); + } } if (attemptID != null) { diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java index 381a46b0c6b..dfced10b0f0 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/JobBuilder.java @@ -474,10 +474,11 @@ private void processTaskAttemptUnsuccessfulCompletionEvent( } attempt.setResult(getPre21Value(event.getTaskStatus())); - ParsedHost parsedHost = getAndRecordParsedHost(event.getHostname()); - - if (parsedHost != null) { - attempt.setLocation(parsedHost.makeLoggedLocation()); + attempt.setHostName(event.getHostname(), event.getRackName()); + ParsedHost pHost = + getAndRecordParsedHost(event.getRackName(), event.getHostname()); + if (pHost != null) { + attempt.setLocation(pHost.makeLoggedLocation()); } attempt.setFinishTime(event.getFinishTime()); @@ -506,8 +507,10 @@ private void processTaskAttemptFinishedEvent(TaskAttemptFinishedEvent event) { return; } attempt.setResult(getPre21Value(event.getTaskStatus())); - attempt.setLocation(getAndRecordParsedHost(event.getHostname()) - .makeLoggedLocation()); + ParsedHost pHost = getAndRecordParsedHost(event.getRackName(), event.getHostname()); + if (pHost != null) { + attempt.setLocation(pHost.makeLoggedLocation()); + } attempt.setFinishTime(event.getFinishTime()); attempt .incorporateCounters(((TaskAttemptFinished) event.getDatum()).counters); @@ -523,6 +526,11 @@ private void processReduceAttemptFinishedEvent( } attempt.setResult(getPre21Value(event.getTaskStatus())); attempt.setHostName(event.getHostname(), event.getRackName()); + ParsedHost pHost = + getAndRecordParsedHost(event.getRackName(), event.getHostname()); + if (pHost != null) { + attempt.setLocation(pHost.makeLoggedLocation()); + } // XXX There may be redundant location info available in the event. // We might consider extracting it from this event. Currently this @@ -546,8 +554,14 @@ private void processMapAttemptFinishedEvent(MapAttemptFinishedEvent event) { return; } attempt.setResult(getPre21Value(event.getTaskStatus())); - attempt.setHostName(event.getHostname(), event.getRackname()); + attempt.setHostName(event.getHostname(), event.getRackName()); + ParsedHost pHost = + getAndRecordParsedHost(event.getRackName(), event.getHostname()); + if (pHost != null) { + attempt.setLocation(pHost.makeLoggedLocation()); + } + // XXX There may be redundant location info available in the event. // We might consider extracting it from this event. Currently this // is redundant, but making this will add future-proofing. @@ -676,7 +690,19 @@ private LoggedTaskAttempt getOrMakeTaskAttempt(TaskType type, } private ParsedHost getAndRecordParsedHost(String hostName) { - ParsedHost result = ParsedHost.parse(hostName); + return getAndRecordParsedHost(null, hostName); + } + + private ParsedHost getAndRecordParsedHost(String rackName, String hostName) { + ParsedHost result = null; + if (rackName == null) { + // for old (pre-23) job history files where hostname was represented as + // /rackname/hostname + result = ParsedHost.parse(hostName); + } else { + // for new (post-23) job history files + result = new ParsedHost(rackName, hostName); + } if (result != null) { ParsedHost canonicalResult = allHosts.get(result); diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedHost.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedHost.java index 1e75a6aafb1..7262b4171fa 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedHost.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/ParsedHost.java @@ -69,11 +69,17 @@ public static ParsedHost parse(String name) { return new ParsedHost(matcher.group(1), matcher.group(2)); } + private String process(String name) { + return name == null + ? null + : name.startsWith("/") ? name.substring(1) : name; + } + public ParsedHost(LoggedLocation loc) { List coordinates = loc.getLayers(); - rackName = coordinates.get(0); - nodeName = coordinates.get(1); + rackName = process(coordinates.get(0)); + nodeName = process(coordinates.get(1)); } LoggedLocation makeLoggedLocation() { @@ -99,8 +105,8 @@ String getRackName() { // expects the broadest name first ParsedHost(String rackName, String nodeName) { - this.rackName = rackName; - this.nodeName = nodeName; + this.rackName = process(rackName); + this.nodeName = process(nodeName); } @Override diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TaskAttempt20LineEventEmitter.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TaskAttempt20LineEventEmitter.java index 4bf25ae1f2b..e4da6ccc624 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TaskAttempt20LineEventEmitter.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TaskAttempt20LineEventEmitter.java @@ -108,9 +108,12 @@ HistoryEvent maybeEmitEvent(ParsedLine line, String taskAttemptIDName, TaskAttempt20LineEventEmitter that = (TaskAttempt20LineEventEmitter) thatg; + ParsedHost pHost = ParsedHost.parse(hostName); + return new TaskAttemptFinishedEvent(taskAttemptID, that.originalTaskType, status, Long.parseLong(finishTime), - hostName, state, maybeParseCounters(counters)); + pHost.getRackName(), pHost.getNodeName(), state, + maybeParseCounters(counters)); } return null; @@ -138,10 +141,19 @@ HistoryEvent maybeEmitEvent(ParsedLine line, String taskAttemptIDName, TaskAttempt20LineEventEmitter that = (TaskAttempt20LineEventEmitter) thatg; + ParsedHost pHost = ParsedHost.parse(hostName); + String rackName = null; + + // Earlier versions of MR logged on hostnames (without rackname) for + // unsuccessful attempts + if (pHost != null) { + rackName = pHost.getRackName(); + hostName = pHost.getNodeName(); + } return new TaskAttemptUnsuccessfulCompletionEvent (taskAttemptID, that.originalTaskType, status, Long.parseLong(finishTime), - hostName, -1, error, null); + hostName, -1, rackName, error, null); } return null; diff --git a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TopologyBuilder.java b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TopologyBuilder.java index 4ab1fa6ad9d..7cef6d19092 100644 --- a/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TopologyBuilder.java +++ b/hadoop-mapreduce-project/src/tools/org/apache/hadoop/tools/rumen/TopologyBuilder.java @@ -25,6 +25,8 @@ import org.apache.hadoop.mapreduce.jobhistory.HistoryEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptFinishedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskAttemptUnsuccessfulCompletionEvent; +import org.apache.hadoop.mapreduce.jobhistory.MapAttemptFinishedEvent; +import org.apache.hadoop.mapreduce.jobhistory.ReduceAttemptFinishedEvent; import org.apache.hadoop.mapreduce.jobhistory.TaskStartedEvent; /** @@ -46,6 +48,10 @@ public void process(HistoryEvent event) { processTaskAttemptUnsuccessfulCompletionEvent((TaskAttemptUnsuccessfulCompletionEvent) event); } else if (event instanceof TaskStartedEvent) { processTaskStartedEvent((TaskStartedEvent) event); + } else if (event instanceof MapAttemptFinishedEvent) { + processMapAttemptFinishedEvent((MapAttemptFinishedEvent) event); + } else if (event instanceof ReduceAttemptFinishedEvent) { + processReduceAttemptFinishedEvent((ReduceAttemptFinishedEvent) event); } // I do NOT expect these if statements to be exhaustive. @@ -78,15 +84,40 @@ private void processTaskStartedEvent(TaskStartedEvent event) { private void processTaskAttemptUnsuccessfulCompletionEvent( TaskAttemptUnsuccessfulCompletionEvent event) { - recordParsedHost(event.getHostname()); + recordParsedHost(event.getHostname(), event.getRackName()); } private void processTaskAttemptFinishedEvent(TaskAttemptFinishedEvent event) { - recordParsedHost(event.getHostname()); + recordParsedHost(event.getHostname(), event.getRackName()); } - private void recordParsedHost(String hostName) { - ParsedHost result = ParsedHost.parse(hostName); + private void processMapAttemptFinishedEvent(MapAttemptFinishedEvent event) { + recordParsedHost(event.getHostname(), event.getRackName()); + } + + private void processReduceAttemptFinishedEvent(ReduceAttemptFinishedEvent event) { + recordParsedHost(event.getHostname(), event.getRackName()); + } + + private void recordParsedHost(String hostName, String rackName) { + if (hostName == null) { + return; + } + ParsedHost result = null; + if (rackName == null) { + result = ParsedHost.parse(hostName); + } else { + result = new ParsedHost(rackName, hostName); + } + + + if (result != null && !allHosts.contains(result)) { + allHosts.add(result); + } + } + + private void recordParsedHost(String nodeName) { + ParsedHost result = ParsedHost.parse(nodeName); if (result != null && !allHosts.contains(result)) { allHosts.add(result);