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 2015/11/05 03:15:48 UTC

tez git commit: TEZ-2893. Tez UI: Retain vertex info displayed in DAG details page even after completion (sree)

Repository: tez
Updated Branches:
  refs/heads/master baa506557 -> d7ba1098e


TEZ-2893. Tez UI: Retain vertex info displayed in DAG details page even after completion (sree)


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

Branch: refs/heads/master
Commit: d7ba1098e8d2c0050d6acf490906a4c86346f256
Parents: baa5065
Author: Sreenath Somarajapuram <sr...@apache.org>
Authored: Thu Nov 5 07:45:10 2015 +0530
Committer: Sreenath Somarajapuram <sr...@apache.org>
Committed: Thu Nov 5 07:45:10 2015 +0530

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 tez-ui/src/main/webapp/app/scripts/app.js       |  15 +-
 .../bs-progress-animated-component.js           |   4 +-
 .../app/scripts/controllers/dag_controller.js   | 151 +++--------
 .../scripts/controllers/dag_index_controller.js | 263 ++++++++++++-------
 .../app/scripts/controllers/dag_vertices.js     |   6 +-
 .../scripts/controllers/polling-controller.js   |   1 +
 .../controllers/table-page-controller.js        |   8 +
 .../main/webapp/app/scripts/helpers/em-data.js  |  59 +++++
 .../scripts/helpers/entity-array-pollster.js    |  51 +---
 .../app/scripts/models/TimelineRestAdapter.js   |  38 ++-
 .../src/main/webapp/app/scripts/models/dag.js   |  16 +-
 tez-ui/src/main/webapp/app/scripts/router.js    |  10 +-
 .../src/main/webapp/app/templates/dag/index.hbs |  20 +-
 14 files changed, 375 insertions(+), 268 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 4d63906..355a85c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -7,6 +7,7 @@ INCOMPATIBLE CHANGES
   TEZ-2679. Admin forms of launch env settings
 
 ALL CHANGES:
+  TEZ-2893. Tez UI: Retain vertex info displayed in DAG details page even after completion
   TEZ-2878. Tez UI: AM error handling - Make the UI handle cases in which AM returns unexpected/no data
   TEZ-2922. Tez Live UI gives access denied for admins
   TEZ-2849. Implement Specific Workaround for JDK-8026049 & JDK-8073093.

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/app.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/app.js b/tez-ui/src/main/webapp/app/scripts/app.js
index 5d7e28d..c1c2c45 100644
--- a/tez-ui/src/main/webapp/app/scripts/app.js
+++ b/tez-ui/src/main/webapp/app/scripts/app.js
@@ -194,16 +194,23 @@ App.ready = function () {
   // v2 version of am web services
   App.DagInfoAdapter = App.AMInfoAdapter.extend({
     namespace: App.Configs.restNamespace.aminfoV2,
+    findQuery: function(store, type, query) {
+      var record = query.metadata;
+      delete query.metadata;
+      return this.ajax(
+        this.buildURL(Ember.String.pluralize(type.typeKey),
+          record.dagID, Em.Object.create(record)), 'GET', { data: query});
+    },
     buildURL: function(type, id, record) {
-      var url = this._super(type, null, record);
-      return url.replace('__app_id__', record.get('appId'))
-        .fmt(record.get('dagIdx'));
+      var url = this._super(type, undefined, record);
+      return url.replace('__app_id__', record.get('appID')).fmt(id);
     },
-    pathForType: function() {
+    pathForType: function(typeName) {
       return 'dagInfo?dagID=%@';
     }
   });
 
+
   App.VertexInfoAdapter = App.AMInfoAdapter.extend({
     namespace: App.Configs.restNamespace.aminfoV2,
     findQuery: function(store, type, query) {

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/components/bs-progress-animated-component.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/components/bs-progress-animated-component.js b/tez-ui/src/main/webapp/app/scripts/components/bs-progress-animated-component.js
index 22f380d..6a260ea 100644
--- a/tez-ui/src/main/webapp/app/scripts/components/bs-progress-animated-component.js
+++ b/tez-ui/src/main/webapp/app/scripts/components/bs-progress-animated-component.js
@@ -25,7 +25,9 @@ Bootstrap.BsProgressAnimatedComponent = Bootstrap.BsProgressComponent.extend({
  },
 
  progressDecimalObserver: function () {
-   this.set('progress', parseInt(this.get('progressDecimal') * 100).toString());
+   var progress = parseFloat(this.get('progressDecimal'));
+   progress = (typeof progress == 'number' && !isNaN(progress)) ? progress : 0;
+   this.set('progress', parseInt(progress * 100).toString());
  }.observes('progressDecimal'),
 
  progressObserver: function () {

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/controllers/dag_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dag_controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/dag_controller.js
index 9643f64..95c6a8f 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/dag_controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/dag_controller.js
@@ -16,10 +16,47 @@
  * limitations under the License.
  */
 
-App.DagController = Em.ObjectController.extend(App.Helpers.DisplayHelper, {
+App.DagController = App.PollingController.extend(App.Helpers.DisplayHelper, {
   controllerName: 'DagController',
   pageTitle: 'Dag',
+
   loading: true,
+  isActive: false,
+
+  pollingType: 'dagInfo',
+
+  setup: function () {
+    this.set('isActive', true);
+  },
+  reset: function () {
+    this.set('isActive', false);
+  },
+
+  pollsterControl: function () {
+    if(this.get('status') == 'RUNNING' &&
+        this.get('amWebServiceVersion') != '1' &&
+        this.get('isActive')) {
+      this.get('pollster').start();
+    }
+    else {
+      this.get('pollster').stop();
+    }
+  }.observes('status', 'amWebServiceVersion', 'isActive'),
+
+  pollsterOptionsObserver: function () {
+    var model = this.get('model');
+
+    this.get('pollster').setProperties( (model && model.get('status') != 'SUCCEEDED') ? {
+      targetRecords: [model],
+      options: {
+        appID: this.get('applicationId'),
+        dagID: App.Helpers.misc.getIndexFromId(this.get('id')),
+      }
+    } : {
+      targetRecords: [],
+      options: null
+    });
+  }.observes('applicationId', 'model', 'model.status', 'id'),
 
   loadAdditional: function(dag) {
     var that = this;
@@ -68,19 +105,10 @@ App.DagController = Em.ObjectController.extend(App.Helpers.DisplayHelper, {
 
     var allLoaders = Em.RSVP.all(loaders);
     allLoaders.then(function(){
-      ['dagProgress', 'dagInfo', 'vertexInfo'].forEach(function(itemType){
-        that.store.unloadAll(itemType);
-      });
       if (dag.get('status') === 'RUNNING') {
         // update the progress info if available. this need not block the UI
         if (dag.get('amWebServiceVersion') == '1') {
           that.updateInfoFromAM(dag);
-        } else {
-          // if AM version is v2 we keep updating the status, progress etc live.
-          ["loading", "id", "model.status"].forEach(function(item) {
-            Em.addObserver(that, item, that.startAMInfoUpdateService);
-          });
-          that.startAMInfoUpdateService();
         }
       }
     });
@@ -91,116 +119,17 @@ App.DagController = Em.ObjectController.extend(App.Helpers.DisplayHelper, {
   // called only for v1 version of am api.
   updateInfoFromAM: function(dag) {
     var that = this;
+    App.Helpers.misc.removeRecord(this.get('store'), 'dagProgress', dag.get('id'));
     var aminfoLoader = this.store.find('dagProgress', dag.get('id'), {
       appId: dag.get('applicationId'),
       dagIdx: dag.get('idx')
     }).then(function(dagProgressInfo) {
-      that.set('amDagInfo', dagProgressInfo);
+      that.set('dag.progress', dagProgressInfo.get('progress'));
     }).catch(function (error) {
       Em.Logger.error("Failed to fetch dagProgress" + e);
     });
   },
 
-  updateAMDagInfo: function() {
-    var dagId = this.get('id')
-        that = this,
-        dagInfoLoader = null;
-
-    if (!dagId) return;
-
-    if (this.store.recordIsLoaded("dagInfo", dagId)) {
-      var dagInfoRecord = this.store.recordForId("dagInfo", dagId);
-      if (dagInfoRecord.get('isLoading')) return;
-      dagInfoLoader = dagInfoRecord.reload();
-    } else {
-      dagInfoLoader = this.store.find("dagInfo", dagId, {
-        appId: that.get('applicationId'),
-        dagIdx: that.get('idx')
-      })
-    }
-
-    dagInfoLoader.then(function(dagInfo){
-      that.set('amDagInfo', dagInfo);
-      //TODO: find another way to trigger notification
-      that.set('amDagInfo._amInfoLastUpdatedTime', moment());
-      that.set('amProgressInfoSucceededOnce', true);
-    }).catch(function(e){
-      if (that.get('amProgressInfoSucceededOnce') === true) {
-        that.set('amProgressInfoSucceededOnce', false);
-        App.Helpers.ErrorBar.getInstance().show(
-          "Failed to get in-progress status. Manually refresh to get the updated status",
-          "Application Manager either exited or is not running.");
-      }
-    });
-  },
-
-  updateAMVerticesInfo: function() {
-    var dagId = this.get('id')
-        that = this,
-        verticesInfoLoader = null;
-
-    if (!dagId) return;
-
-    verticesInfoLoader = this.store.findQuery('vertexInfo', {
-      metadata: {
-        appID: that.get('applicationId'),
-        dagID: that.get('idx'),
-        counters: App.get('vertexCounters')
-      }
-    });
-
-    verticesInfoLoader.then(function(verticesInfo) {
-      that.set('amVertexInfo', verticesInfo);
-    }).catch(function(e){
-      // do nothing
-    });
-
-  },
-
-  startAMInfoUpdateService: function() {
-    if (this.get('loading') || !this.get('model.id') || this.get('model.status') != 'RUNNING') {
-      return;
-    }
-
-    var amInfoUpdateService = this.get('amInfoUpdateService')
-        that = this;
-
-    if (Em.isNone(amInfoUpdateService)) {
-      amInfoUpdateService = App.Helpers.Pollster.create({
-        onPoll: function() {
-          that.updateAMDagInfo();
-          that.updateAMVerticesInfo();
-        }
-      });
-      that.set('amInfoUpdateService', amInfoUpdateService);
-      amInfoUpdateService.start(true);
-
-      ["loading", "id", "model.status"].forEach(function(item) {
-        Em.addObserver(that, item, that.stopAMInfoUpdateService);
-      });
-    }
-    else {
-      that.updateAMDagInfo();
-      that.updateAMVerticesInfo();
-    }
-  },
-
-  dostopAMInfoUpdateService: function() {
-      var amInfoUpdateService = this.get('amInfoUpdateService');
-      if (!Em.isNone(amInfoUpdateService)) {
-        amInfoUpdateService.stop();
-        this.set('amInfoUpdateService', undefined);
-      }
-  },
-
-  // stop the update service if the status changes. see startAMInfoUpdateService
-  stopAMInfoUpdateService: function() {
-    that.set('amProgressInfoSucceededOnce', false);
-    if (this.get('loading') || this.get('model.status') != 'RUNNING') {
-      this.dostopAMInfoUpdateService();
-    }
-  },
-
   enableAppIdLink: function() {
     return !!this.get('tezApp');
   }.property('applicationId', 'tezApp'),

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/controllers/dag_index_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dag_index_controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/dag_index_controller.js
index a85c707..ad3c454 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/dag_index_controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/dag_index_controller.js
@@ -16,21 +16,59 @@
  * limitations under the License.
  */
 
- //TODO: watch individual counters.
-App.DagIndexController = Em.ObjectController.extend(App.ModelRefreshMixin, {
+App.DagIndexController = App.TablePageController.extend({
   controllerName: 'DagIndexController',
+  needs: "dag",
 
-  needs: 'dag',
+  entityType: 'dagVertex',
+  filterEntityType: 'dag',
+  filterEntityId: Ember.computed.alias('controllers.dag.id'),
 
-  liveData: null,
+  cacheDomain: Ember.computed.alias('controllers.dag.id'),
 
-  succeededTasks: null,
-  totalTasks: null,
-  completedVertices: null,
+  pollingType: 'vertexInfo',
 
-  liveDataObserver: function () {
-    var vertexInfoContent = this.get('amVertexInfo.content'),
-        liveData = null,
+  init: function () {
+    this._super();
+    this.set('pollster.mergeProperties', ['progress', 'status', 'runningTasks', 'pendingTasks',
+      'sucessfulTasks', 'failedTaskAttempts', 'killedTaskAttempts']);
+  },
+
+  reset: function () {
+    this._super();
+    this.set('data', null);
+  },
+
+  pollsterControl: function () {
+    if(this.get('status') == 'RUNNING' &&
+        this.get('amWebServiceVersion') != '1' &&
+        !this.get('loading') &&
+        this.get('isActive') &&
+        this.get('rowsDisplayed.length') > 0) {
+      this.get('pollster').start();
+    }
+    else {
+      this.get('pollster').stop();
+    }
+  }.observes('status', 'amWebServiceVersion', 'loading', 'isActive'),
+
+  applicationComplete: function () {
+    this._super();
+    this.set('controllers.dag.progress', 1);
+  },
+
+  pollsterOptionsObserver: function () {
+    this.set('pollster.options', {
+      appID: this.get('applicationId'),
+      dagID: this.get('idx')
+    });
+  }.observes('applicationId', 'idx').on('init'),
+
+  progressDetails: null,
+
+  progressObserver: function () {
+    var vertexInfoContent = this.get('pollster.polledRecords.content'),
+        progressDetails = null,
         succeededTasks = null,
         totalTasks = null,
         completedVertices = null;
@@ -42,65 +80,86 @@ App.DagIndexController = Em.ObjectController.extend(App.ModelRefreshMixin, {
       completedVertices = 0;
 
       liveData.forEach(function (vertex) {
-        succeededTasks += parseInt(vertex.get('succeededTasks'));
-        totalTasks += parseInt(vertex.get('totalTasks'));
+        succeededTasks += parseInt(vertex.get('sucessfulTasks'));
+        totalTasks += parseInt(vertex.get('numTasks'));
         if(vertex.get('progress') >= 1) {
           completedVertices++;
         }
       });
+
+      progressDetails = {
+        succeededTasks: succeededTasks,
+        completedVertices: completedVertices,
+        totalTasks: totalTasks
+      };
     }
 
-    this.setProperties({
-      liveData: liveData,
-      succeededTasks: succeededTasks,
-      totalTasks: totalTasks,
-      completedVertices: completedVertices
+    this.set('progressDetails', progressDetails);
+  }.observes('pollster.polledRecords'),
+
+  dataObserver: function () {
+    var data = this.get('data.content');
+    this.set('rowsDisplayed', data ? data.slice(0) : null);
+  }.observes('data'),
+
+  beforeLoad: function () {
+    var dagController = this.get('controllers.dag'),
+        model = dagController.get('model');
+    return model.reload().then(function () {
+      return dagController.loadAdditional(model);
     });
-  }.observes('amVertexInfo'),
+  },
 
-  dagRunning: function () {
-    var progress = this.get('dagProgress');
-    return progress != null && progress < 1;
-  }.property('dagProgress'),
+  afterLoad: function () {
+    var data = this.get('data'),
+        runningVerticesIdx,
+        isUnsuccessfulDag = App.Helpers.misc.isStatusInUnsuccessful(
+          this.get('controllers.dag.status')
+        );
 
-  actions: {
-    downloadDagJson: function() {
-      var dagID = this.get('id');
-      var downloader = App.Helpers.misc.downloadDAG(this.get('id'), {
-        batchSize: 500,
-        onSuccess: function() {
-          Bootstrap.ModalManager.close('downloadModal');
-        },
-        onFailure: function() {
-          $('#modalMessage').html('<i class="fa fa-lg fa-exclamation-circle margin-small-horizontal" ' +
-          'style="color:red"></i>&nbsp;Error downloading data');
-        }
+    if(isUnsuccessfulDag) {
+      data.filterBy('status', 'RUNNING').forEach(function (vertex) {
+        vertex.set('status', 'KILLED');
       });
-      this.set('tmpDownloader', downloader);
-      var modalDialogView = Ember.View.extend({
-        template: Em.Handlebars.compile(
-          '<p id="modalMessage"><i class="fa fa-lg fa-spinner fa-spin margin-small-horizontal" ' + 
-          'style="color:green"></i>Downloading data for dag %@</p>'.fmt(dagID)
-        )
-      });
-      var buttons = [
-        Ember.Object.create({title: 'Cancel', dismiss: 'modal', clicked: 'cancelDownload'})
-      ];
-      Bootstrap.ModalManager.open('downloadModal', 'Download data',
-        modalDialogView, buttons, this);
-    },
+    }
 
-    cancelDownload: function() {
-      var currentDownloader = this.get('tmpDownloader');
-      if (!!currentDownloader) {
-        currentDownloader.cancel();
-      }
-      this.set('tmpDownloader', undefined);
+    if (this.get('controllers.dag.amWebServiceVersion') == '1') {
+      this._loadProgress(data);
     }
 
+    return this._super();
+  },
+
+  // Load progress in parallel for v1 version of the api
+  _loadProgress: function (vertices) {
+    var that = this,
+        runningVerticesIdx = vertices
+      .filterBy('status', 'RUNNING')
+      .map(function(item) {
+        return item.get('id').split('_').splice(-1).pop();
+      });
+
+    if (runningVerticesIdx.length > 0) {
+      this.store.unloadAll('vertexProgress');
+      this.store.findQuery('vertexProgress', {
+        metadata: {
+          appId: that.get('applicationId'),
+          dagIdx: that.get('idx'),
+          vertexIds: runningVerticesIdx.join(',')
+        }
+      }).then(function(vertexProgressInfo) {
+          App.Helpers.emData.mergeRecords(
+            that.get('rowsDisplayed'),
+            vertexProgressInfo,
+            ['progress']
+          );
+      }).catch(function(error) {
+        Em.Logger.debug("failed to fetch vertex progress")
+      });
+    }
   },
 
-  liveColumns: function () {
+  defaultColumnConfigs: function() {
     var vertexIdToNameMap = this.get('vertexIdToNameMap');
 
     return App.Helpers.misc.createColumnDescription([
@@ -118,16 +177,11 @@ App.DagIndexController = Em.ObjectController.extend(App.ModelRefreshMixin, {
         }
       },
       {
-        id: 'progress',
-        headerCellName: 'Progress',
-        contentPath: 'progress',
-        templateName: 'components/basic-table/progress-cell'
-      },
-      {
         id: 'status',
         headerCellName: 'Status',
         templateName: 'components/basic-table/status-cell',
         contentPath: 'status',
+        observePath: true,
         getCellContent: function(row) {
           var status = row.get('status');
           return {
@@ -138,53 +192,92 @@ App.DagIndexController = Em.ObjectController.extend(App.ModelRefreshMixin, {
         }
       },
       {
+        id: 'progress',
+        headerCellName: 'Progress',
+        contentPath: 'progress',
+        observePath: true,
+        templateName: 'components/basic-table/progress-cell'
+      },
+      {
         id: 'totalTasks',
         headerCellName: 'Total Tasks',
-        contentPath: 'totalTasks',
+        contentPath: 'numTasks',
+        observePath: true,
       },
       {
         id: 'succeededTasks',
         headerCellName: 'Succeeded Tasks',
-        contentPath: 'succeededTasks',
+        contentPath: 'sucessfulTasks',
+        observePath: true,
       },
       {
         id: 'runningTasks',
         headerCellName: 'Running Tasks',
         contentPath: 'runningTasks',
+        observePath: true,
       },
       {
         id: 'pendingTasks',
         headerCellName: 'Pending Tasks',
         contentPath: 'pendingTasks',
+        observePath: true,
       },
       {
         id: 'failedTasks',
         headerCellName: 'Failed Task Attempts',
         contentPath: 'failedTaskAttempts',
+        observePath: true,
       },
       {
         id: 'killedTasks',
         headerCellName: 'Killed Task Attempts',
         contentPath: 'killedTaskAttempts',
+        observePath: true,
       }
     ]);
-  }.property('id'),
+  }.property('vertexIdToNameMap'),
 
-  load: function () {
-    var dag = this.get('controllers.dag.model'),
-        controller = this.get('controllers.dag'),
-        t = this;
-    t.set('loading', true);
-    dag.reload().then(function () {
-      return controller.loadAdditional(dag);
-    }).catch(function(error){
-      Em.Logger.error(error);
-      var err = App.Helpers.misc.formatError(error, defaultErrMsg);
-      var msg = 'error code: %@, message: %@'.fmt(err.errCode, err.msg);
-      App.Helpers.ErrorBar.getInstance().show(msg, err.details);
-    });
+  actions: {
+    downloadDagJson: function() {
+      var dagID = this.get('id');
+      var downloader = App.Helpers.misc.downloadDAG(this.get('id'), {
+        batchSize: 500,
+        onSuccess: function() {
+          Bootstrap.ModalManager.close('downloadModal');
+        },
+        onFailure: function() {
+          $('#modalMessage').html('<i class="fa fa-lg fa-exclamation-circle margin-small-horizontal" ' +
+          'style="color:red"></i>&nbsp;Error downloading data');
+        }
+      });
+      this.set('tmpDownloader', downloader);
+      var modalDialogView = Ember.View.extend({
+        template: Em.Handlebars.compile(
+          '<p id="modalMessage"><i class="fa fa-lg fa-spinner fa-spin margin-small-horizontal" ' +
+          'style="color:green"></i>Downloading data for dag %@</p>'.fmt(dagID)
+        )
+      });
+      var buttons = [
+        Ember.Object.create({title: 'Cancel', dismiss: 'modal', clicked: 'cancelDownload'})
+      ];
+      Bootstrap.ModalManager.open('downloadModal', 'Download data',
+        modalDialogView, buttons, this);
+    },
+
+    cancelDownload: function() {
+      var currentDownloader = this.get('tmpDownloader');
+      if (!!currentDownloader) {
+        currentDownloader.cancel();
+      }
+      this.set('tmpDownloader', undefined);
+    }
   },
 
+  dagRunning: function () {
+    var progress = this.get('dagProgress');
+    return progress != null && progress < 1;
+  }.property('dagProgress'),
+
   taskIconStatus: function() {
     return App.Helpers.misc.getStatusClassForEntity(this.get('model.status'),
       this.get('hasFailedTaskAttempts'));
@@ -246,22 +339,4 @@ App.DagIndexController = Em.ObjectController.extend(App.ModelRefreshMixin, {
     }
   }.property('appContextInfo.appType'),
 
-  updateAMInfo: function() {
-    var status = this.get('amDagInfo.status');
-        progress = this.get('amDagInfo.progress'),
-        infoUpdated = false;
-    if (!Em.isNone(status)) {
-      this.set('status', status);
-      infoUpdated = true;
-    }
-
-    if (!Em.isNone(progress)) {
-      this.set('progress', progress);
-      infoUpdated = true;
-    }
-
-    if (infoUpdated) {
-      Em.tryInvoke(this.get('model'), 'didLoad');
-    }
-  }.observes('amDagInfo', 'amDagInfo._amInfoLastUpdatedTime')
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js b/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js
index f25c59e..44d29da 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js
@@ -103,7 +103,11 @@ App.DagVerticesController = App.TablePageController.extend({
           vertexIds: runningVerticesIdx.join(',')
         }
       }).then(function(vertexProgressInfo) {
-        that.set('controllers.dag.amVertexInfo', vertexProgressInfo);
+          App.Helpers.emData.mergeRecords(
+            that.get('displayedRecords'),
+            vertexProgressInfo,
+            ['progress']
+          );
       }).catch(function(error) {
         Em.Logger.debug("failed to fetch vertex progress")
       });

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/controllers/polling-controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/polling-controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/polling-controller.js
index c15db57..507ee49 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/polling-controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/polling-controller.js
@@ -61,6 +61,7 @@ App.PollingController = Em.ObjectController.extend({
   },
 
   applicationComplete: function () {
+    this.set('pollster.polledRecords', null);
     if(this.load) {
       this.load();
     }

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/controllers/table-page-controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/table-page-controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/table-page-controller.js
index a91dafc..285cc69 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/table-page-controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/table-page-controller.js
@@ -46,6 +46,14 @@ App.TablePageController = App.PollingController.extend(
         this.set('pollster.targetRecords', this.get('rowsDisplayed'));
       }.observes('rowsDisplayed', 'pollster'),
 
+      parentStatusObserver: function () {
+        var parentStatus = this.get('status');
+        if(parentStatus && parentStatus != 'RUNNING') {
+          this.get('pollster').stop();
+          this.loadData(true);
+        }
+      }.observes('status'),
+
       applicationComplete: function () {
         this.loadData(true);
       },

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/helpers/em-data.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/helpers/em-data.js b/tez-ui/src/main/webapp/app/scripts/helpers/em-data.js
new file mode 100644
index 0000000..5c4dadc
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/scripts/helpers/em-data.js
@@ -0,0 +1,59 @@
+/**
+ * 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.
+ */
+
+App.Helpers.emData = {
+  /**
+   * Merge data from an array of records to another
+   * @param target {Array} Target record array
+   * @param source {Array} Source record array
+   * @param mergeProps {Array} Array of strings of property names to be merged
+   * @return true if merge was success, else false
+   */
+  mergeRecords: function (target, source, mergeProps) {
+
+    if(source && target && mergeProps) {
+      target.forEach(function (row) {
+        var info = source.findBy('id', row.get('id')),
+            merge = !!info;
+
+        if(merge && row.get('progress') && info.get('progress')) {
+          if(row.get('progress') >= info.get('progress')) {
+            merge = false;
+          }
+        }
+
+        if(merge) {
+          row.setProperties(info.getProperties.apply(info, mergeProps));
+
+          if(info.get('counters')) {
+            row.set('counterGroups',
+              App.Helpers.misc.mergeCounterInfo(
+                row.get('counterGroups'),
+                info.get('counters')
+              ).slice(0)
+            );
+          }
+
+          row.didLoad();// To update the record time stamp
+        }
+      });
+      return true;
+    }
+    return false;
+  }
+};

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/helpers/entity-array-pollster.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/helpers/entity-array-pollster.js b/tez-ui/src/main/webapp/app/scripts/helpers/entity-array-pollster.js
index 31181be..4cf97cf 100644
--- a/tez-ui/src/main/webapp/app/scripts/helpers/entity-array-pollster.js
+++ b/tez-ui/src/main/webapp/app/scripts/helpers/entity-array-pollster.js
@@ -37,20 +37,14 @@ App.Helpers.EntityArrayPollster = App.Helpers.Pollster.extend({
 
   start: function(interval) {
     if(!this.get('isRunning')) {
-      if (!!interval && interval > 1000) {
-        this.set('_interval', interval)
-      }
-
+      this._super(true, interval);
       this.set('isRunning', true);
-
-      this.onPoll();
-      this.set('timer', this.schedule(this.onPoll, true));
     }
   },
 
   stop: function() {
     if(this.get('isRunning')) {
-      Ember.run.cancel(this.get('timer'));
+      this._super();
       this.set('isRunning', false);
     }
   },
@@ -68,7 +62,9 @@ App.Helpers.EntityArrayPollster = App.Helpers.Pollster.extend({
   },
 
   _optionObserver: function () {
-    Em.run.later(this, this.onPoll, 10);
+    if(this.get('options')) {
+      Em.run.later(this, this.onPoll, 10);
+    }
   }.observes('options'),
 
   _callIfRunning: function (that, funName) {
@@ -94,37 +90,10 @@ App.Helpers.EntityArrayPollster = App.Helpers.Pollster.extend({
   },
 
   mergeToTarget: function () {
-    var polledRecords = this.get('polledRecords'),
-        targetRecords = this.get('targetRecords'),
-        mergeProperties = this.get('mergeProperties') || [];
-
-    if(polledRecords && targetRecords) {
-      targetRecords.forEach(function (row) {
-        var info = polledRecords.findBy('id', row.get('id')),
-             merge = !!info;
-
-        row.didLoad();
-
-        if(merge && row.get('progress') && info.get('progress')) {
-          if(row.get('progress') >= info.get('progress')) {
-            merge = false;
-          }
-        }
-
-        if(merge) {
-          row.setProperties(info.getProperties.apply(info, mergeProperties));
-
-          if(info.get('counters')) {
-            row.set('counterGroups',
-              App.Helpers.misc.mergeCounterInfo(
-                row.get('counterGroups'),
-                info.get('counters')
-              ).slice(0)
-            );
-          }
-
-        }
-      });
-    }
+    App.Helpers.emData.mergeRecords(
+      this.get('targetRecords'),
+      this.get('polledRecords'),
+      this.get('mergeProperties') || []
+    );
   }.observes('targetRecords').on('init')
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js b/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
index e7fe876..d54c8b9 100644
--- a/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
+++ b/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
@@ -94,6 +94,13 @@ var timelineJsonToDagMap = {
   name: 'primaryfilters.dagName.0',
   user: 'primaryfilters.user.0',
   status: 'otherinfo.status',
+
+  progress: {
+    custom: function(source) {
+      return Em.get(source, 'otherinfo.status') == 'SUCCEEDED' ? 1 : null;
+    }
+  },
+
   containerLogs: {
     custom: function(source) {
 
@@ -254,6 +261,16 @@ var timelineJsonToVertexMap = {
       return Em.get(source, 'otherinfo.status') == 'SUCCEEDED' ? 1 : null;
     }
   },
+  runningTasks: {
+    custom: function(source) {
+      return Em.get(source, 'otherinfo.status') == 'SUCCEEDED' ? 0 : null;
+    }
+  },
+  pendingTasks: {
+    custom: function(source) {
+      return Em.get(source, 'otherinfo.status') == 'SUCCEEDED' ? 0 : null;
+    }
+  },
 
   status: 'otherinfo.status',
   hasFailedTaskAttempts: {
@@ -266,6 +283,9 @@ var timelineJsonToVertexMap = {
   },
   diagnostics: 'otherinfo.diagnostics',
 
+  failedTaskAttempts: 'otherinfo.numFailedTaskAttempts',
+  killedTaskAttempts: 'otherinfo.numKilledTaskAttempts',
+
   failedTasks: 'otherinfo.numFailedTasks',
   sucessfulTasks: 'otherinfo.numSucceededTasks',
   numTasks: 'otherinfo.numTasks',
@@ -570,16 +590,30 @@ App.VertexProgressSerializer = App.DagProgressSerializer = DS.RESTSerializer.ext
 App.DagInfoSerializer = DS.RESTSerializer.extend({
   normalizePayload: function(rawPayload) {
     return {
-      dagInfo : rawPayload.dag
+      dagInfo : [rawPayload.dag]
     }
   }
 });
 
 App.VertexInfoSerializer = DS.RESTSerializer.extend({
+  map: {
+    id: 'id',
+    progress: 'progress',
+    status: 'status',
+    numTasks: 'totalTasks',
+    runningTasks: 'runningTasks',
+    sucessfulTasks: 'succeededTasks',
+    failedTaskAttempts: 'failedTaskAttempts',
+    killedTaskAttempts: 'killedTaskAttempts',
+    counters: 'counters'
+  },
   normalizePayload: function(rawPayload) {
     return {
       vertexInfo : rawPayload.vertices
     }
+  },
+  normalize: function(type, hash, prop) {
+    return Em.JsonMapper.map(hash, this.get('map'));
   }
 });
 
@@ -620,4 +654,4 @@ App.ClusterAppSerializer = App.TimelineSerializer.extend({
   normalize: function(type, hash, prop) {
     return Em.JsonMapper.map(hash, this.get('map'));
   }
-});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/models/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/models/dag.js b/tez-ui/src/main/webapp/app/scripts/models/dag.js
index a21b983..0457157 100644
--- a/tez-ui/src/main/webapp/app/scripts/models/dag.js
+++ b/tez-ui/src/main/webapp/app/scripts/models/dag.js
@@ -50,6 +50,8 @@ App.Dag = App.AbstractEntity.extend({
   tezApp: DS.belongsTo('tezApp'),
   appDetail: DS.belongsTo('appDetail'),
 
+  progress: DS.attr('number'),
+
   // status
   status: DS.attr('string'),
   hasFailedTaskAttempts: DS.attr('boolean'),
@@ -215,9 +217,14 @@ App.Vertex = App.AbstractEntity.extend({
 
   failedTasks: DS.attr('number'),
   sucessfulTasks: DS.attr('number'),
+  runningTasks: DS.attr('number'),
+  pendingTasks: DS.attr('number'),
   numTasks: DS.attr('number'),
   killedTasks: DS.attr('number'),
 
+  failedTaskAttempts: DS.attr('number'),
+  killedTaskAttempts: DS.attr('number'),
+
   diagnostics: DS.attr('string'),
 
   counterGroups: DS.attr('array'),
@@ -434,14 +441,15 @@ App.VertexInfo = DS.Model.extend({
 
   progress: DS.attr('number'),
   status: DS.attr('string'),
-  totalTasks: DS.attr('number'),
+  numTasks: DS.attr('number'),
   runningTasks: DS.attr('number'),
-  succeededTasks: DS.attr('number'),
+  sucessfulTasks: DS.attr('number'),
   failedTaskAttempts: DS.attr('number'),
   killedTaskAttempts: DS.attr('number'),
+
   pendingTasks: function() {
-    return this.get('totalTasks') - this.get('runningTasks') - this.get('succeededTasks');
-  }.property('totalTasks', 'runningTasks', 'succeededTasks'),
+    return this.get('numTasks') - this.get('runningTasks') - this.get('sucessfulTasks');
+  }.property('numTasks', 'runningTasks', 'sucessfulTasks'),
 
   counters: DS.attr('object')
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/scripts/router.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/router.js b/tez-ui/src/main/webapp/app/scripts/router.js
index 1f7f646..ab551a1 100644
--- a/tez-ui/src/main/webapp/app/scripts/router.js
+++ b/tez-ui/src/main/webapp/app/scripts/router.js
@@ -155,7 +155,6 @@ App.DagRoute = Em.Route.extend({
   },
   setupController: setupControllerFactory('Dag: %@ (%@)', 'name', 'id'),
   resetController: function() {
-    this.controller.dostopAMInfoUpdateService();
     if(this.controller.reset) {
       this.controller.reset();
     }
@@ -314,6 +313,15 @@ App.TezAppConfigsRoute = Em.Route.extend({
 });
 
 /* --- Shared routes --- */
+App.DagIndexRoute = Em.Route.extend({
+  resetController: function () {
+    if(this.controller.reset) {
+      this.controller.reset();
+    }
+  },
+  setupController: setupControllerFactory()
+});
+
 App.DagTasksRoute =
     App.DagVerticesRoute =
     App.DagTaskAttemptsRoute =

http://git-wip-us.apache.org/repos/asf/tez/blob/d7ba1098/tez-ui/src/main/webapp/app/templates/dag/index.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/dag/index.hbs b/tez-ui/src/main/webapp/app/templates/dag/index.hbs
index f7ec99f..c287584 100644
--- a/tez-ui/src/main/webapp/app/templates/dag/index.hbs
+++ b/tez-ui/src/main/webapp/app/templates/dag/index.hbs
@@ -95,22 +95,24 @@
   </div>
 </div>
 
-{{#if liveData}}
+{{#if data.content}}
   <br/>
   <h4>DAG Progress
-    ( Vertices {{completedVertices}}/{{liveData.length}} )
-    {{#if totalTasks}}
-      ( Tasks {{succeededTasks}}/{{totalTasks}} )
+    {{#if progressDetails}}
+      ( Vertices {{progressDetails.completedVertices}}/{{data.length}} )
+      {{#if progressDetails.totalTasks}}
+        ( Tasks {{progressDetails.succeededTasks}}/{{progressDetails.totalTasks}} )
+      {{/if}}
+      :
+      {{progressStr}}
     {{/if}}
-    :
-    {{progressStr}}
   </h4>
   {{bs-progress-animated progressDecimal=progress}}
 
   {{basic-table-component
-    columns=liveColumns
-    rows=liveData
-    rowCount=liveData.length
+    columns=defaultColumnConfigs
+    rows=data.content
+    rowCount=data.content.length
 
     enableStatus=false
     enableSort=true