Repository: tez Updated Branches: refs/heads/master 37cab1284 -> 9554c6455
TEZ-2780. Tez UI: Update All Tasks page while in progress (sree) Project: http://git-wip-us.apache.org/repos/asf/tez/repo Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/9554c645 Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/9554c645 Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/9554c645 Branch: refs/heads/master Commit: 9554c6455900b18c6a30779b2fcc1ced96299f6e Parents: 37cab12 Author: Sreenath Somarajapuram <[email protected]> Authored: Fri Sep 11 23:56:10 2015 +0530 Committer: Sreenath Somarajapuram <[email protected]> Committed: Fri Sep 11 23:56:10 2015 +0530 ---------------------------------------------------------------------- CHANGES.txt | 1 + tez-ui/src/main/webapp/app/scripts/app.js | 22 +++- .../basic-table/basic-table-component.js | 6 +- .../scripts/components/basic-table/cell-view.js | 39 ++++++- .../app/scripts/controllers/dag_controller.js | 2 +- .../webapp/app/scripts/controllers/dag_tasks.js | 59 +++++++++++ .../controllers/table-page-controller.js | 14 +++ .../scripts/helpers/entity-array-pollster.js | 103 +++++++++++++++++++ .../src/main/webapp/app/scripts/helpers/misc.js | 9 ++ .../main/webapp/app/scripts/helpers/pollster.js | 2 +- .../app/scripts/models/TimelineRestAdapter.js | 13 +++ .../src/main/webapp/app/scripts/models/dag.js | 9 +- tez-ui/src/main/webapp/app/scripts/router.js | 10 +- .../main/webapp/app/templates/common/table.hbs | 2 + 14 files changed, 282 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index de6ad13..d2de18e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -175,6 +175,7 @@ Release 0.7.1: Unreleased INCOMPATIBLE CHANGES ALL CHANGES: + TEZ-2780. Tez UI: Update All Tasks page while in progress TEZ-2792. Add AM web service API for tasks TEZ-2807. Log data in the finish event instead of the start event TEZ-2766. Tez UI: Add vertex in-progress info in DAG details http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/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 8e32874..13c08cb 100644 --- a/tez-ui/src/main/webapp/app/scripts/app.js +++ b/tez-ui/src/main/webapp/app/scripts/app.js @@ -218,6 +218,25 @@ App.ready = function () { return 'verticesInfo?dagID=%@'; } }); + + App.TaskInfoAdapter = 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.taskID, Em.Object.create(record)), 'GET', { data: query}); + }, + buildURL: function(type, id, record) { + var url = this._super(type, undefined, record); + return url.replace('__app_id__', record.get('appID')) + .fmt(record.get('dagID'), id); + }, + pathForType: function(typeName) { + return 'tasksInfo?dagID=%@&taskID=%@'; + } + }); }; $.ajaxPrefilter(function(options, originalOptions, jqXHR) { @@ -232,8 +251,9 @@ $.ajaxSetup({ require('scripts/default-configs'); require('scripts/translations'); -require('scripts/mixins/*'); +require('scripts/helpers/pollster'); require('scripts/helpers/*'); +require('scripts/mixins/*'); require('scripts/router'); require('scripts/views/**/*'); http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/tez-ui/src/main/webapp/app/scripts/components/basic-table/basic-table-component.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/components/basic-table/basic-table-component.js b/tez-ui/src/main/webapp/app/scripts/components/basic-table/basic-table-component.js index e56b5d8..2ea9cc5 100644 --- a/tez-ui/src/main/webapp/app/scripts/components/basic-table/basic-table-component.js +++ b/tez-ui/src/main/webapp/app/scripts/components/basic-table/basic-table-component.js @@ -138,8 +138,10 @@ App.BasicTableComponent = Em.Component.extend({ }.property('columns'), _rows: function () { - var startIndex = (this.get('pageNum') - 1) * this.get('rowCount'); - return this.get('_searchedRows').slice(startIndex, startIndex + this.get('rowCount')); + var startIndex = (this.get('pageNum') - 1) * this.get('rowCount'), + rows = this.get('_searchedRows').slice(startIndex, startIndex + this.get('rowCount')); + this.sendAction('rowsChanged', rows); + return rows; }.property('_searchedRows.@each', 'rowCount', 'pageNum'), _searchObserver: function () { http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/tez-ui/src/main/webapp/app/scripts/components/basic-table/cell-view.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/components/basic-table/cell-view.js b/tez-ui/src/main/webapp/app/scripts/components/basic-table/cell-view.js index 80a4c56..fa8d1f7 100644 --- a/tez-ui/src/main/webapp/app/scripts/components/basic-table/cell-view.js +++ b/tez-ui/src/main/webapp/app/scripts/components/basic-table/cell-view.js @@ -27,16 +27,47 @@ function stringifyNumbers(content) { } App.BasicTableComponent.CellView = Ember.View.extend({ - templateName: 'components/basic-table/basic-cell', + templateName: function () { + var template = this.get('column.observePath') ? 'bounded-basic-cell' : 'basic-cell'; + return 'components/basic-table/' + template; + }.property('column.observePath'), classNames: ['cell-content'], + value: null, + observedPath: null, + + _addObserver: function (path) { + this._removeObserver(); + this.get('row').addObserver(path, this, this._onValueChange); + this.set('observedPath', path); + }, + + _removeObserver: function (path) { + var path = this.get('observedPath'); + if(path) { + this.get('row').removeObserver(path, this, this._onValueChange); + this.set('observedPath', null); + } + }, + _normalizeContent: function (content) { return stringifyNumbers(content && typeof content == 'object' ? content : { displayText: content }); }, + _pathObserver: function () { + var path = this.get('column.contentPath'); + if(path && this.get('column.observePath')) { + this._addObserver(path); + } + }.observes('row', 'column.contentPath', 'column.observePath').on('init'), + + _onValueChange: function (row, path) { + this.set('value', row.get(path)); + }, + cellContent: function () { var cellContent = this.get('column').getCellContent(this.get('row')); @@ -47,5 +78,9 @@ App.BasicTableComponent.CellView = Ember.View.extend({ } return this._normalizeContent(cellContent); - }.property('row', 'column') + }.property('row', 'column', 'value'), + + willDestroy: function () { + this._removeObserver(); + } }); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/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 af22918..6260e06 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 @@ -143,7 +143,7 @@ App.DagController = Em.ObjectController.extend(App.Helpers.DisplayHelper, { that = this; if (Em.isNone(amInfoUpdateService)) { - amInfoUpdateService = App.Helpers.pollster.create({ + amInfoUpdateService = App.Helpers.Pollster.create({ onPoll: function() { that.updateAMDagInfo(); that.updateAMVerticesInfo(); http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js b/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js index 81e4277..0bd9df0 100644 --- a/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js +++ b/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js @@ -27,6 +27,57 @@ App.DagTasksController = App.TablePageController.extend({ cacheDomain: Ember.computed.alias('controllers.dag.id'), + pollster: App.Helpers.EntityArrayPollster.create(), + + init: function () { + this._super(); + this.get('pollster').setProperties({ + entityType: 'taskInfo', + mergeProperties: ['status', 'progress'], + store: this.get('store') + }); + }, + + pollsterControl: function () { + if(this.get('controllers.dag.status') == 'RUNNING' && + this.get('controllers.dag.amWebServiceVersion') != '1' && + !this.get('loading') && + this.get('isActive') && + this. get('rowsDisplayed.length') > 0) { + this.get('pollster').start(); + } + else { + this.get('pollster').stop(); + } + }.observes('controllers.dag.status', + 'controllers.dag.amWebServiceVersion', + 'rowsDisplayed', + 'loading', + 'isActive'), + + reset: function () { + this.set('pollster.options', null); + }, + + pollsterOptionsObserver: function () { + var rows = this.get('rowsDisplayed'); + this.set('pollster.targetRecords', rows); + + rows = rows.filter(function (row) { + return row.get('status') != 'SUCCEEDED'; + }); + + this.set('pollster.options', (rows && rows.length) ? { + appID: this.get('controllers.dag.model.applicationId'), + dagID: this.get('controllers.dag.model.idx'), + taskID: rows.map(function (row) { + var taskIndex = App.Helpers.misc.getIndexFromId(row.get('id')), + vertexIndex = App.Helpers.misc.getIndexFromId(row.get('vertexID')); + return '%@_%@'.fmt(vertexIndex, taskIndex); + }).join(',') + } : null); + }.observes('controllers.dag.model.applicationId', 'controllers.dag.model.idx', 'rowsDisplayed'), + beforeLoad: function () { var dagController = this.get('controllers.dag'), model = dagController.get('model'); @@ -113,6 +164,7 @@ App.DagTasksController = App.TablePageController.extend({ headerCellName: 'Status', templateName: 'components/basic-table/status-cell', contentPath: 'status', + observePath: true, getCellContent: function(row) { var status = row.get('status'); return { @@ -123,6 +175,13 @@ App.DagTasksController = App.TablePageController.extend({ } }, { + id: 'progress', + headerCellName: 'Progress', + contentPath: 'progress', + observePath: true, + templateName: 'components/basic-table/progress-cell' + }, + { id: 'startTime', headerCellName: 'Start Time', contentPath: 'startTime', http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/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 7e17c0e..dc66d7d 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 @@ -28,9 +28,20 @@ App.TablePageController = Em.ObjectController.extend( rowCount: 25, searchText: '', + rowsDisplayed: [], isRefreshable: true, + // -- TODO: TEZ-2785 : Following 3 must be moved to a parent class + isActive: false, + + setup: function () { + this.set('isActive', true); + }, + reset: function () { + this.set('isActive', false); + }, + statusMessage: function () { return this.get('loading') ? "Loading all records..." : null; }.property('loading'), @@ -38,6 +49,9 @@ App.TablePageController = Em.ObjectController.extend( actions: { refresh: function () { this.loadData(true); + }, + tableRowsChanged: function (rows) { + this.set('rowsDisplayed', rows); } } } http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/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 new file mode 100644 index 0000000..040ec8f --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/helpers/entity-array-pollster.js @@ -0,0 +1,103 @@ +/** + * 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.EntityArrayPollster = App.Helpers.Pollster.extend({ + entityType: null, // Entity type to be polled + store: null, + mergeProperties: [], + options: null, + + isRunning: false, + isWaiting: false, + + polledRecords: null, + targetRecords: [], + + _ready: function () { + return this.get('entityType') && this.get('store') && this.get('options'); + }.property('entityType', 'store', 'options'), + + start: function(interval) { + if(!this.get('isRunning')) { + if (!!interval && interval > 1000) { + this.set('_interval', 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.set('isRunning', false); + } + }, + + onPoll: function(){ + if(!this.get('isWaiting') && this.get('_ready')) { + this.set('isWaiting', true); + + return this.store.findQuery(this.get('entityType'), { + metadata: this.get('options') + }).then(this._callIfRunning(this, 'onResponse')). + catch(this._callIfRunning(this, 'onFailure')). + finally(this._final.bind(this)); + } + }, + + _callIfRunning: function (that, funName) { + return function (data) { + var fun = that.get(funName); + if(fun && that.get('isRunning')) { + fun.call(that, data); + } + }; + }, + + onResponse: function (data) { + this.set('polledRecords', data); + this.mergeToTarget(); + }, + + onFailure: function (err) { + // Implement based on requirement + }, + + _final: function () { + this.set('isWaiting', false); + }, + + 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')); + if(info) { + row.setProperties(info.getProperties.apply(info, mergeProperties)); + } + }); + } + }.observes('targetRecords').on('init') +}); http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/tez-ui/src/main/webapp/app/scripts/helpers/misc.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/helpers/misc.js b/tez-ui/src/main/webapp/app/scripts/helpers/misc.js index 639c9e8..223b686 100644 --- a/tez-ui/src/main/webapp/app/scripts/helpers/misc.js +++ b/tez-ui/src/main/webapp/app/scripts/helpers/misc.js @@ -360,6 +360,15 @@ App.Helpers.misc = { return dagId.split('_').splice(-1).pop(); }, + /* + * Return index for the given id + * @param id {string} + * @return index {Number} + */ + getIndexFromId: function (id) { + return parseInt(id.split('_').splice(-1).pop()); + }, + /** * Remove the specific record from store * @param store {DS.Store} http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/tez-ui/src/main/webapp/app/scripts/helpers/pollster.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/helpers/pollster.js b/tez-ui/src/main/webapp/app/scripts/helpers/pollster.js index 5728b81..af8dd70 100644 --- a/tez-ui/src/main/webapp/app/scripts/helpers/pollster.js +++ b/tez-ui/src/main/webapp/app/scripts/helpers/pollster.js @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -App.Helpers.pollster = Ember.Object.extend({ +App.Helpers.Pollster = Ember.Object.extend({ interval: function() { return this.get('_interval') || 10000; // Time between polls (in ms) }.property().readOnly(), http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/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 a21fd67..051a984 100644 --- a/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js +++ b/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js @@ -192,6 +192,11 @@ var timelineJsonToTaskMap = { vertexID: 'primaryfilters.TEZ_VERTEX_ID.0', endTime: 'otherinfo.endTime', status: 'otherinfo.status', + progress: { + custom: function(source) { + return Em.get(source, 'otherinfo.status') == 'SUCCEEDED' ? 1 : null; + } + }, numFailedTaskAttempts: 'otherinfo.numFailedTaskAttempts', diagnostics: 'otherinfo.diagnostics', counterGroups: 'otherinfo.counters.counterGroups', @@ -543,3 +548,11 @@ App.VertexInfoSerializer = DS.RESTSerializer.extend({ } } }); + +App.TaskInfoSerializer = DS.RESTSerializer.extend({ + normalizePayload: function(rawPayload) { + return { + taskInfo : rawPayload.tasks + } + } +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/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 011c181..1cbee7c 100644 --- a/tez-ui/src/main/webapp/app/scripts/models/dag.js +++ b/tez-ui/src/main/webapp/app/scripts/models/dag.js @@ -337,7 +337,7 @@ App.TezApp = App.AbstractEntity.extend({ App.Task = App.AbstractEntity.extend({ - status: DS.attr('status'), + status: DS.attr('string'), index: function () { var id = this.get('id'), @@ -347,6 +347,8 @@ App.Task = App.AbstractEntity.extend({ dagID: DS.attr('string'), + progress: DS.attr('number'), + successfulAttemptId: DS.attr('string'), attempts: DS.attr('array'), @@ -426,6 +428,11 @@ App.VertexInfo = DS.Model.extend({ }.property('totalTasks', 'runningTasks', 'succeededTasks') }); +App.TaskInfo = DS.Model.extend({ + progress: DS.attr('number'), + status: DS.attr('string'), +}); + App.KVDatum = DS.Model.extend({ key: DS.attr('string'), value: DS.attr('string'), http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/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 c5c4687..77fb6ef 100644 --- a/tez-ui/src/main/webapp/app/scripts/router.js +++ b/tez-ui/src/main/webapp/app/scripts/router.js @@ -106,6 +106,10 @@ function setupControllerFactory(format) { } this._super(controller, model); + if(controller.setup) { + controller.setup(); + } + if(controller.loadData) { controller.loadData(); } @@ -274,13 +278,17 @@ App.TezAppConfigsRoute = Em.Route.extend({ }); /* --- Shared routes --- */ - App.DagTasksRoute = App.DagVerticesRoute = App.DagTaskAttemptsRoute = App.VertexTasksRoute = App.VertexTaskAttemptsRoute = Em.Route.extend({ + resetController: function () { + if(this.controller.reset) { + this.controller.reset(); + } + }, renderTemplate: renderTable, setupController: setupControllerFactory() }); http://git-wip-us.apache.org/repos/asf/tez/blob/9554c645/tez-ui/src/main/webapp/app/templates/common/table.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/common/table.hbs b/tez-ui/src/main/webapp/app/templates/common/table.hbs index 462089d..0b1ab8d 100644 --- a/tez-ui/src/main/webapp/app/templates/common/table.hbs +++ b/tez-ui/src/main/webapp/app/templates/common/table.hbs @@ -34,6 +34,8 @@ enablePagination=true enableSort=true + rowsChanged='tableRowsChanged' + pageNumBinding='pageNum' rowCountBinding='rowCount'
