You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by sr...@apache.org on 2017/01/18 14:05:18 UTC

tez git commit: TEZ-3571. Tez UI: Display a Total Timeline View for Hive Queries (sree)

Repository: tez
Updated Branches:
  refs/heads/master 051ed16ed -> 6fea794df


TEZ-3571. Tez UI: Display a Total Timeline View for Hive Queries (sree)


Project: http://git-wip-us.apache.org/repos/asf/tez/repo
Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/6fea794d
Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/6fea794d
Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/6fea794d

Branch: refs/heads/master
Commit: 6fea794df6c8bf809991abe06c83ce071fc37a42
Parents: 051ed16
Author: Sreenath Somarajapuram <sr...@apache.org>
Authored: Wed Jan 18 19:35:37 2017 +0530
Committer: Sreenath Somarajapuram <sr...@apache.org>
Committed: Wed Jan 18 19:35:37 2017 +0530

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../webapp/app/components/query-timeline.js     |  64 +++++++++
 tez-ui/src/main/webapp/app/controllers/query.js |   3 +
 .../webapp/app/controllers/query/timeline.js    |  69 ++++++++++
 tez-ui/src/main/webapp/app/models/hive-query.js |   2 +
 tez-ui/src/main/webapp/app/router.js            |   1 +
 .../main/webapp/app/routes/query/timeline.js    |  35 +++++
 .../main/webapp/app/serializers/hive-query.js   |  13 +-
 tez-ui/src/main/webapp/app/styles/app.less      |   1 +
 .../main/webapp/app/styles/query-timeline.less  | 113 +++++++++++++++
 .../app/templates/components/query-timeline.hbs |  40 ++++++
 .../webapp/app/templates/query/timeline.hbs     | 126 +++++++++++++++++
 .../components/query-timeline-test.js           | 136 +++++++++++++++++++
 .../webapp/tests/unit/controllers/query-test.js |   2 +-
 .../unit/controllers/query/timeline-test.js     |  63 +++++++++
 .../webapp/tests/unit/models/hive-query-test.js |   2 +
 .../tests/unit/routes/query/timeline-test.js    |  35 +++++
 .../tests/unit/serializers/hive-query-test.js   |   2 +-
 18 files changed, 705 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 67aaff9..f0cb52e 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -168,6 +168,7 @@ ALL CHANGES:
   TEZ-3531. Tez UI: All Queries table: Improve searchability
   TEZ-3556. Tez UI: Display query configurations
   TEZ-3496. Tez UI: Optimize display of all tasks table
+  TEZ-3571. Tez UI: Display a Total Timeline View for Hive Queries
 
 Release 0.8.5: Unreleased
 

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/components/query-timeline.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/components/query-timeline.js b/tez-ui/src/main/webapp/app/components/query-timeline.js
new file mode 100644
index 0000000..e743530
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/query-timeline.js
@@ -0,0 +1,64 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  classNames: ['query-timeline'],
+
+  perf: null,
+
+  getDisplayedPerfValues: function (perfHash) {
+    return [[
+      perfHash["compile"] || 0,
+      perfHash["parse"] || 0,
+      perfHash["semanticAnalyze"] || 0,
+      perfHash["TezBuildDag"] || 0,
+    ], [
+      perfHash["TezSubmitDag"] || 0,
+      perfHash["TezSubmitToRunningDag"] || 0,
+    ], [
+      perfHash["TezRunDag"] || 0,
+    ], [
+      perfHash["PostATSHook"] || 0,
+      perfHash["RemoveTempOrDuplicateFiles"] || 0,
+      perfHash["RenameOrMoveFiles"] || 0,
+    ]];
+  },
+
+  alignBars: function (bars, widthFactors) {
+    var totalValue = widthFactors.reduce((a, b) => a + b, 0);
+    bars.each(function (index, bar) {
+      var width = (widthFactors[index] / totalValue) * 100;
+      Ember.$(bar).css({
+        width: `${width}%`
+      });
+    });
+  },
+
+  didInsertElement: Ember.observer("perf", function () {
+    var perfs = this.getDisplayedPerfValues(this.get("perf"));
+
+    this.alignBars(this.$().find(".sub-groups").find(".bar"), [].concat.apply([], perfs));
+
+    this.alignBars(this.$().find(".groups").find(".bar"), perfs.map(function (subPerfs) {
+      return subPerfs.reduce((a, b) => a + b, 0);
+    }));
+  })
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/controllers/query.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/controllers/query.js b/tez-ui/src/main/webapp/app/controllers/query.js
index f9c6833..98a4303 100644
--- a/tez-ui/src/main/webapp/app/controllers/query.js
+++ b/tez-ui/src/main/webapp/app/controllers/query.js
@@ -35,6 +35,9 @@ export default ParentController.extend({
     text: "Query Details",
     routeName: "query.index"
   }, {
+    text: "Timeline",
+    routeName: "query.timeline"
+  }, {
     text: "Configurations",
     routeName: "query.configs"
   }]

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/controllers/query/timeline.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/controllers/query/timeline.js b/tez-ui/src/main/webapp/app/controllers/query/timeline.js
new file mode 100644
index 0000000..3b72efb
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/controllers/query/timeline.js
@@ -0,0 +1,69 @@
+/*global more*/
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import TableController from '../table';
+import ColumnDefinition from 'em-table/utils/column-definition';
+
+var MoreObject = more.Object;
+
+export default TableController.extend({
+
+  columns: ColumnDefinition.make([{
+    id: 'perfLogName',
+    headerTitle: 'Raw Perf Log Name',
+    contentPath: 'perfLogName',
+  }, {
+    id: 'perfLogValue',
+    headerTitle: 'Value',
+    contentPath: 'perfLogValue',
+    cellDefinition: {
+      type: 'duration'
+    }
+  }]),
+
+  phaseTimes: Ember.computed("model", function () {
+    var perf = this.get("model.perf");
+    return {
+      pre: perf.compile + perf.parse + perf.semanticAnalyze + perf.TezBuildDag,
+      submission: perf.TezSubmitDag + perf.TezSubmitToRunningDag,
+      run: perf.TezRunDag,
+      post: perf.RemoveTempOrDuplicateFiles +
+          perf.RemoveTempOrDuplicateFiles +
+          perf.RenameOrMoveFiles
+    };
+  }),
+
+  rows: Ember.computed("model.perf", function () {
+    var perf = this.get("model.perf"),
+        rows = [];
+
+    if(perf) {
+      MoreObject.forEach(perf, function (key, value) {
+        rows.push(Ember.Object.create({
+          perfLogName: key,
+          perfLogValue: value
+        }));
+      });
+    }
+
+    return Ember.A(rows);
+  })
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/models/hive-query.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/models/hive-query.js b/tez-ui/src/main/webapp/app/models/hive-query.js
index 7eac037..a640050 100644
--- a/tez-ui/src/main/webapp/app/models/hive-query.js
+++ b/tez-ui/src/main/webapp/app/models/hive-query.js
@@ -72,4 +72,6 @@ export default AbstractModel.extend({
     return duration > 0 ? duration : null;
   }),
 
+  perf: DS.attr("Object"),
+
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/router.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/router.js b/tez-ui/src/main/webapp/app/router.js
index 3444579..6aa0b2f 100644
--- a/tez-ui/src/main/webapp/app/router.js
+++ b/tez-ui/src/main/webapp/app/router.js
@@ -51,6 +51,7 @@ Router.map(function() {
   });
   this.route('query', {path: '/query/:query_id'}, function() {
     this.route('configs');
+    this.route('timeline');
   });
 
   // Alias for backward compatibility with Tez UI V1

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/routes/query/timeline.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/routes/query/timeline.js b/tez-ui/src/main/webapp/app/routes/query/timeline.js
new file mode 100644
index 0000000..e336762
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/routes/query/timeline.js
@@ -0,0 +1,35 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import SingleAmPollsterRoute from '../single-am-pollster';
+
+export default SingleAmPollsterRoute.extend({
+  title: "Query Timeline",
+
+  loaderNamespace: "query",
+
+  setupController: function (controller, model) {
+    this._super(controller, model);
+    Ember.run.later(this, "startCrumbBubble");
+  },
+
+  load: function (value, query, options) {
+    return this.get("loader").queryRecord('hive-query', this.modelFor("query").get("id"), options);
+  },
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/serializers/hive-query.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/serializers/hive-query.js b/tez-ui/src/main/webapp/app/serializers/hive-query.js
index bd14368..e00f9ef 100644
--- a/tez-ui/src/main/webapp/app/serializers/hive-query.js
+++ b/tez-ui/src/main/webapp/app/serializers/hive-query.js
@@ -79,11 +79,14 @@ export default TimelineSerializer.extend({
 
     startTime: 'starttime',
     endTime: getEndTime,
+
+    perf: "otherinfo.PERF"
   },
 
   extractAttributes: function (modelClass, resourceHash) {
     var data = resourceHash.data,
-        query = Ember.get(resourceHash, "data.otherinfo.QUERY");
+        query = Ember.get(resourceHash, "data.otherinfo.QUERY"),
+        perf = Ember.get(resourceHash, "data.otherinfo.PERF");
 
     if(query) {
       try{
@@ -95,6 +98,14 @@ export default TimelineSerializer.extend({
       data.otherinfo.CLIENT_IP_ADDRESS = data.otherinfo.HIVE_ADDRESS;
     }
 
+    if(perf) {
+      try{
+        let PERF = JSON.parse(perf);
+        PERF["PostATSHook"] = PERF["PostHook.org.apache.hadoop.hive.ql.hooks.ATSHook"];
+        data.otherinfo.PERF = PERF;
+      }catch(e){}
+    }
+
     return this._super(modelClass, resourceHash);
   },
 

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/styles/app.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/app.less b/tez-ui/src/main/webapp/app/styles/app.less
index 85b02bd..2ad6449 100644
--- a/tez-ui/src/main/webapp/app/styles/app.less
+++ b/tez-ui/src/main/webapp/app/styles/app.less
@@ -42,6 +42,7 @@
 @import "em-tooltip";
 @import "em-swimlane-vertex-name";
 @import "em-table-status-cell";
+@import "query-timeline";
 
 // Modals
 @import "column-selector";

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/styles/query-timeline.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/query-timeline.less b/tez-ui/src/main/webapp/app/styles/query-timeline.less
new file mode 100644
index 0000000..b5bfb54
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/query-timeline.less
@@ -0,0 +1,113 @@
+/**
+ * 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.
+ */
+
+.query-timeline {
+  font-size: 0;
+  margin: 20px 0 20px 0;
+
+  .no-select;
+
+  .bar {
+    display: inline-block;
+
+    font-size: 12px;
+    text-align: center;
+    white-space: nowrap;
+
+    overflow: hidden;
+    text-overflow: ellipsis;
+
+    vertical-align: top;
+  }
+
+  .groups {
+    height: 30px;
+
+    white-space: nowrap;
+
+    overflow: hidden;
+
+    .bar {
+      width: 25%;
+      height: 30px;
+      font-weight: bold;
+
+      padding-top: 5px;
+
+      border: solid @border-color;
+      border-width: 1px 1px 0px 1px;
+      border-radius: 10px 10px 0 0;
+
+      background-color: #F7F7F7;
+    }
+  }
+
+  .sub-groups {
+    height: 40px;
+    border: 1px solid @border-color;
+
+    white-space: nowrap;
+
+    border-radius: 0 0 10px 10px;
+    overflow: hidden;
+
+    .bar {
+      width: 10%;
+      height: 40px;
+
+      padding-top: 10px;
+
+      border-left: 1px solid @border-color;
+
+      &:first-child {
+        border: none;
+      }
+
+      &:nth-child(1) {
+        background-color: lighten(#FF355E, 20%);
+      }
+      &:nth-child(2) {
+        background-color: lighten(#FD5B78, 20%);
+      }
+      &:nth-child(3) {
+        background-color: lighten(#FF6037, 20%);
+      }
+      &:nth-child(4) {
+        background-color: lighten(#FF9966, 20%);
+      }
+      &:nth-child(5) {
+        background-color: lighten(#FF9933, 20%);
+      }
+      &:nth-child(6) {
+        background-color: lighten(#FFCC33, 20%);
+      }
+      &:nth-child(7) {
+        background-color: lighten(#FFFF66, 20%);
+      }
+      &:nth-child(8) {
+        background-color: lighten(#CCFF00, 20%);
+      }
+      &:nth-child(9) {
+        background-color: lighten(#AAF0D1, 10%);
+      }
+      &:nth-child(10) {
+        background-color: lighten(#FF6EFF, 20%);
+      }
+   }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/templates/components/query-timeline.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/components/query-timeline.hbs b/tez-ui/src/main/webapp/app/templates/components/query-timeline.hbs
new file mode 100644
index 0000000..681b7b5
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/query-timeline.hbs
@@ -0,0 +1,40 @@
+{{!
+ * 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.
+}}
+
+<div class="groups">
+  <div class="bar" title="Pre-Execution + DAG construction">Pre-Execution + DAG construction</div>
+  <div class="bar" title="DAG Submission">DAG Submission</div>
+  <div class="bar" title="DAG Runtime">DAG Runtime</div>
+  <div class="bar" title="Post Execution">Post Execution</div>
+</div>
+
+<div class="sub-groups">
+  <div class="bar" title="Compile">Compile</div>
+  <div class="bar" title="Parse">Parse</div>
+  <div class="bar" title="Semantic Analyze">Semantic Analyze</div>
+  <div class="bar" title="Build Dag">Build Dag</div>
+
+  <div class="bar" title="Submit Dag">Submit Dag</div>
+  <div class="bar" title="Submit To Running">Submit To Running</div>
+
+  <div class="bar" title="Run Dag">Run Dag</div>
+
+  <div class="bar" title="Post ATS Hook">Post ATS Hook</div>
+  <div class="bar" title="Remove Files">Remove Files</div>
+  <div class="bar" title="Rename Or Move Files">Rename Or Move Files</div>
+</div>

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/app/templates/query/timeline.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/query/timeline.hbs b/tez-ui/src/main/webapp/app/templates/query/timeline.hbs
new file mode 100644
index 0000000..9a728a7
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/query/timeline.hbs
@@ -0,0 +1,126 @@
+{{!
+ * 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.
+}}
+
+{{#if loaded}}
+
+  {{query-timeline perf=model.perf}}
+
+  <table class='detail-list'>
+    <thead>
+    <tr>
+      <th colspan=2>Pre-Execution + DAG construction : {{txt phaseTimes.pre type="duration"}}</th>
+    </tr>
+    </thead>
+
+    <tbody>
+    <tr>
+      <td>Compile</td>
+      <td>{{txt model.perf.compile type="duration"}}</td>
+    </tr>
+    <tr>
+      <td>Parse</td>
+      <td>{{txt model.perf.parse type="duration"}}</td>
+    </tr>
+    <tr>
+      <td>Semantic Analyze</td>
+      <td>{{txt model.perf.semanticAnalyze type="duration"}}</td>
+    </tr>
+    <tr>
+      <td>Build Dag</td>
+      <td>{{txt model.perf.TezBuildDag type="duration"}}</td>
+    </tr>
+    </tbody>
+  </table>
+
+  <table class='detail-list'>
+    <thead>
+    <tr>
+      <th colspan=2>DAG Submission : {{txt phaseTimes.submission type="duration"}}</th>
+    </tr>
+    </thead>
+    <tbody>
+    <tr>
+      <td>Submit Dag</td>
+      <td>{{txt model.perf.TezSubmitDag type="duration"}}</td>
+    </tr>
+    <tr>
+      <td>Submit To Running</td>
+      <td>{{txt model.perf.TezSubmitToRunningDag type="duration"}}</td>
+    </tr>
+    </tbody>
+  </table>
+
+  <table class='detail-list'>
+    <thead>
+    <tr>
+      <th colspan=2>DAG Runtime : {{txt phaseTimes.run type="duration"}}</th>
+    </tr>
+    </thead>
+    <tbody>
+    <tr>
+      <td>Run Dag</td>
+      <td>{{txt model.perf.TezRunDag type="duration"}}</td>
+    </tr>
+    </tbody>
+  </table>
+
+  <table class='detail-list'>
+    <thead>
+    <tr>
+      <th colspan=2>Post Execution : {{txt phaseTimes.post type="duration"}}</th>
+    </tr>
+    </thead>
+    <tbody>
+    <tr>
+      <td>Post ATS Hook</td>
+      <td>{{txt model.perf.PostATSHook type="duration"}}</td>
+    </tr>
+    {{#if model.perf.RemoveTempOrDuplicateFiles}}
+      <tr>
+        <td>Remove Files</td>
+        <td>{{txt model.perf.RemoveTempOrDuplicateFiles type="duration"}}</td>
+      </tr>
+    {{/if}}
+    {{#if model.perf.RenameOrMoveFiles}}
+      <tr>
+        <td>Rename Or Move Files</td>
+        <td>{{txt model.perf.RenameOrMoveFiles type="duration"}}</td>
+      </tr>
+    {{/if}}
+    </tbody>
+  </table>
+
+  <br/>
+
+  {{em-table
+  columns=columns
+  rows=rows
+
+  rowCount=configs.length
+  definition=definition
+
+  enablePagination=false
+
+  searchAction="searchChanged"
+  sortAction="sortChanged"
+  }}
+
+
+{{else}}
+  {{partial "loading"}}
+{{/if}}

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/tests/integration/components/query-timeline-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/integration/components/query-timeline-test.js b/tez-ui/src/main/webapp/tests/integration/components/query-timeline-test.js
new file mode 100644
index 0000000..907896d
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/query-timeline-test.js
@@ -0,0 +1,136 @@
+/**
+ * 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.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('query-timeline', 'Integration | Component | query timeline', {
+  integration: true
+});
+
+test('Basic creation test', function(assert) {
+  this.set("perf", {});
+  this.render(hbs`{{query-timeline perf=perf}}`);
+
+  assert.equal(this.$().find(".bar").length, 10 + 4);
+
+  this.render(hbs`
+    {{#query-timeline perf=perf}}
+      template block text
+    {{/query-timeline}}
+  `);
+
+  assert.equal(this.$().find(".bar").length, 10 + 4);
+});
+
+test('alignBars test', function(assert) {
+  var total = 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 90 + 100;
+  var bars;
+
+  this.set("perf", {
+    "compile": 10,
+    "parse": 20,
+    "semanticAnalyze": 30,
+    "TezBuildDag": 40,
+
+    "TezSubmitDag": 50,
+    "TezSubmitToRunningDag": 60,
+
+    "TezRunDag": 70,
+
+    "PostATSHook": 80,
+    "RemoveTempOrDuplicateFiles": 90,
+    "RenameOrMoveFiles": 100,
+  });
+  this.render(hbs`{{query-timeline perf=perf}}`);
+
+  function assertWidth(domElement, factor) {
+    var elementWidth = (parseFloat(domElement.style.width) / 100).toFixed(4),
+        expectedWidth = (factor / total).toFixed(4);
+
+    assert.equal(elementWidth, expectedWidth, `Unexpected value for factor ${factor}`);
+  }
+
+  bars = this.$().find(".groups").find(".bar");
+  assert.equal(bars.length, 4);
+  assertWidth(bars[0], 10 + 20 + 30 + 40);
+  assertWidth(bars[1], 50 + 60);
+  assertWidth(bars[2], 70);
+  assertWidth(bars[3], 80 + 90 + 100);
+
+  bars = this.$().find(".sub-groups").find(".bar");
+  assert.equal(bars.length, 10);
+  assertWidth(bars[0], 10);
+  assertWidth(bars[1], 20);
+  assertWidth(bars[2], 30);
+  assertWidth(bars[3], 40);
+  assertWidth(bars[4], 50);
+  assertWidth(bars[5], 60);
+  assertWidth(bars[6], 70);
+  assertWidth(bars[7], 80);
+  assertWidth(bars[8], 90);
+  assertWidth(bars[9], 100);
+});
+
+test('alignBars - without RenameOrMoveFiles test', function(assert) {
+  var total = 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 90;
+  var bars;
+
+  this.set("perf", {
+    "compile": 10,
+    "parse": 20,
+    "semanticAnalyze": 30,
+    "TezBuildDag": 40,
+
+    "TezSubmitDag": 50,
+    "TezSubmitToRunningDag": 60,
+
+    "TezRunDag": 70,
+
+    "PostATSHook": 80,
+    "RemoveTempOrDuplicateFiles": 90,
+  });
+  this.render(hbs`{{query-timeline perf=perf}}`);
+
+  function assertWidth(domElement, factor) {
+    var elementWidth = (parseFloat(domElement.style.width) / 100).toFixed(4),
+        expectedWidth = (factor / total).toFixed(4);
+
+    assert.equal(elementWidth, expectedWidth, `Unexpected value for factor ${factor}`);
+  }
+
+  bars = this.$().find(".groups").find(".bar");
+  assert.equal(bars.length, 4);
+  assertWidth(bars[0], 10 + 20 + 30 + 40);
+  assertWidth(bars[1], 50 + 60);
+  assertWidth(bars[2], 70);
+  assertWidth(bars[3], 80 + 90);
+
+  bars = this.$().find(".sub-groups").find(".bar");
+  assert.equal(bars.length, 10);
+  assertWidth(bars[0], 10);
+  assertWidth(bars[1], 20);
+  assertWidth(bars[2], 30);
+  assertWidth(bars[3], 40);
+  assertWidth(bars[4], 50);
+  assertWidth(bars[5], 60);
+  assertWidth(bars[6], 70);
+  assertWidth(bars[7], 80);
+  assertWidth(bars[8], 90);
+  assertWidth(bars[9], 0);
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/tests/unit/controllers/query-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/query-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/query-test.js
index de4452d..4913b85 100644
--- a/tez-ui/src/main/webapp/tests/unit/controllers/query-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/controllers/query-test.js
@@ -31,7 +31,7 @@ test('Basic creation test', function(assert) {
   });
 
   assert.ok(controller);
-  assert.equal(controller.get("tabs.length"), 2);
+  assert.equal(controller.get("tabs.length"), 3);
 });
 
 test('breadcrumbs test', function(assert) {

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/tests/unit/controllers/query/timeline-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/query/timeline-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/query/timeline-test.js
new file mode 100644
index 0000000..e3e14dd
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/controllers/query/timeline-test.js
@@ -0,0 +1,63 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:query/timeline', 'Unit | Controller | query/timeline', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+test('Basic creation test', function(assert) {
+  let controller = this.subject({
+    send: Ember.K,
+    initVisibleColumns: Ember.K
+  });
+
+  assert.ok(controller);
+
+  assert.ok(controller.columns);
+  assert.equal(controller.columns.length, 2);
+
+  assert.ok(controller.rows);
+});
+
+test('rows test', function(assert) {
+  let controller = this.subject({
+    send: Ember.K,
+    initVisibleColumns: Ember.K,
+    model: {
+      perf: {
+        x: 1,
+        y: 2,
+        z: 3
+      }
+    }
+  }),
+  rows = controller.get("rows");
+
+  assert.equal(rows[0].perfLogName, "x");
+  assert.equal(rows[0].perfLogValue, 1);
+
+  assert.equal(rows[1].perfLogName, "y");
+  assert.equal(rows[1].perfLogValue, 2);
+
+  assert.equal(rows[2].perfLogName, "z");
+  assert.equal(rows[2].perfLogValue, 3);
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/tests/unit/models/hive-query-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/models/hive-query-test.js b/tez-ui/src/main/webapp/tests/unit/models/hive-query-test.js
index 5c7b492..e44592c 100644
--- a/tez-ui/src/main/webapp/tests/unit/models/hive-query-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/models/hive-query-test.js
@@ -46,6 +46,8 @@ test('Basic creation test', function(assert) {
   assert.ok(model.startTime);
   assert.ok(model.endTime);
   assert.ok(model.duration);
+
+  assert.ok(model.perf);
 });
 
 test('duration test', function(assert) {

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/tests/unit/routes/query/timeline-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/routes/query/timeline-test.js b/tez-ui/src/main/webapp/tests/unit/routes/query/timeline-test.js
new file mode 100644
index 0000000..1cc360a
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/routes/query/timeline-test.js
@@ -0,0 +1,35 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:query/timeline', 'Unit | Route | query/timeline', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+test('Basic creation test', function(assert) {
+  let route = this.subject();
+
+  assert.ok(route);
+  assert.equal(route.get("title"), "Query Timeline");
+  assert.equal(route.get("loaderNamespace"), "query");
+
+  assert.ok(route.setupController);
+  assert.ok(route.load);
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/6fea794d/tez-ui/src/main/webapp/tests/unit/serializers/hive-query-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/serializers/hive-query-test.js b/tez-ui/src/main/webapp/tests/unit/serializers/hive-query-test.js
index c4cf130..cb61d0d 100644
--- a/tez-ui/src/main/webapp/tests/unit/serializers/hive-query-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/serializers/hive-query-test.js
@@ -26,7 +26,7 @@ moduleFor('serializer:hive-query', 'Unit | Serializer | hive query', {
 
 test('Basic creation test', function(assert) {
   let serializer = this.subject();
-  assert.equal(Object.keys(serializer.get("maps")).length, 6 + 20);
+  assert.equal(Object.keys(serializer.get("maps")).length, 6 + 21);
   assert.ok(serializer.get("extractAttributes"));
 });