Repository: ambari Updated Branches: refs/heads/trunk b51bfea4e -> 6625ae5dd
AMBARI-5203. Jobs page becomes extremely slow and unresponsive around 500 queries (alexantonenko) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/4f226737 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/4f226737 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/4f226737 Branch: refs/heads/trunk Commit: 4f226737f99d2caadbd53b27945c14d7a745d43d Parents: b51bfea Author: Alex Antonenko <hiv...@gmail.com> Authored: Tue Mar 25 17:21:40 2014 +0200 Committer: Alex Antonenko <hiv...@gmail.com> Committed: Tue Mar 25 17:21:40 2014 +0200 ---------------------------------------------------------------------- .../app/controllers/main/jobs_controller.js | 84 +++++++++++++++++--- ambari-web/app/mappers/jobs/hive_jobs_mapper.js | 5 +- ambari-web/app/messages.js | 1 + ambari-web/app/templates/main/jobs.hbs | 12 ++- ambari-web/app/views/main/jobs_view.js | 4 +- 5 files changed, 87 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/4f226737/ambari-web/app/controllers/main/jobs_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/jobs_controller.js b/ambari-web/app/controllers/main/jobs_controller.js index d77a9ed..454210b 100644 --- a/ambari-web/app/controllers/main/jobs_controller.js +++ b/ambari-web/app/controllers/main/jobs_controller.js @@ -18,11 +18,72 @@ var App = require('app'); -App.MainJobsController = Em.ArrayController.extend({ - +App.MainJobsController = Em.Controller.extend({ +/* + * https://github.com/emberjs/ember.js/issues/1221 prevents this controller + * from being an Ember.ArrayController. Doing so will keep the UI flashing + * whenever any of the 'sortProperties' or 'sortAscending' properties are set. + * + * To bypass this issue this controller will be a regular controller. Also, + * for memory-leak issues and sorting purposes, we are decoupling the backend + * model and the UI model. There will be simple Ember POJOs for the UI which + * will be periodically updated from backend Jobs model. + */ + name:'mainJobsController', - content: [], + /** + * Unsorted ArrayProxy + */ + content: App.HiveJob.find(), + + /** + * Sorted ArrayProxy + */ + sortedContent: [], + + contentAndSortObserver : function() { + Ember.run.once(this, 'contentAndSortUpdater'); + }.observes('content.length', 'conte...@each.id', 'content.@each.startTime', 'content.@each.endTime', 'sortProperties', 'sortAscending'), + + contentAndSortUpdater: function() { + var content = this.get('content'); + var sortedContent = content.toArray(); + var sortProperty = this.get('sortProperty'); + var sortAscending = this.get('sortAscending'); + sortedContent.sort(function(r1, r2) { + var r1id = r1.get(sortProperty); + var r2id = r2.get(sortProperty); + if (r1id < r2id) + return sortAscending ? -1 : 1; + if (r1id > r2id) + return sortAscending ? 1 : -1; + return 0; + }); + var sortedArray = this.get('sortedContent'); + var count = 0; + sortedContent.forEach(function(sortedJob){ + if(sortedArray.length <= count) { + sortedArray.pushObject(Ember.Object.create()); + } + sortedArray[count].set('failed', sortedJob.get('failed')); + sortedArray[count].set('hasTezDag', sortedJob.get('hasTezDag')); + sortedArray[count].set('queryText', sortedJob.get('queryText')); + sortedArray[count].set('name', sortedJob.get('name')); + sortedArray[count].set('user', sortedJob.get('user')); + sortedArray[count].set('id', sortedJob.get('id')); + sortedArray[count].set('startTimeDisplay', sortedJob.get('startTimeDisplay')); + sortedArray[count].set('endTimeDisplay', sortedJob.get('endTimeDisplay')); + sortedArray[count].set('durationDisplay', sortedJob.get('durationDisplay')); + count ++; + }); + if(sortedArray.length > count) { + for(var c = sortedArray.length-1; c >= count; c--){ + sortedArray.removeObject(sortedArray[c]); + } + } + sortedContent.length = 0; + }, loaded : false, loading : false, @@ -31,6 +92,15 @@ App.MainJobsController = Em.ArrayController.extend({ jobsUpdateInterval: 6000, jobsUpdate: null, sortingColumn: null, + sortProperty: 'id', + sortAscending: true, + + sortingColumnObserver: function () { + if(this.get('sortingColumn')){ + this.set('sortProperty', this.get('sortingColumn').get('name')); + this.set('sortAscending', this.get('sortingColumn').get('status') == "sorting_desc" ? false : true ); + } + }.observes('sortingColumn.name','sortingColumn.status'), updateJobs: function (controllerName, funcName) { clearInterval(this.get('jobsUpdate')); @@ -255,14 +325,6 @@ App.MainJobsController = Em.ArrayController.extend({ + ":" + yarnService.get('ahsWebPort') + "/ws/v1/timeline/HIVE_QUERY_ID" + filtersLink; App.HttpClient.get(hiveQueriesUrl, App.hiveJobsMapper, { complete : function(jqXHR, textStatus) { - var sortColumn = self.get('sortingColumn'); - if(sortColumn && sortColumn.get('status')){ - var sortColumnStatus = sortColumn.get('status'); - sortColumn.get('parentView').set('content', self.get('content')); - sortColumn.get('parentView').sort(sortColumn, sortColumnStatus === "sorting_desc"); - sortColumn.set('status', sortColumnStatus); - self.set('content',sortColumn.get('parentView').get('content')); - } self.set('loading', false); self.set('loaded', true); } http://git-wip-us.apache.org/repos/asf/ambari/blob/4f226737/ambari-web/app/mappers/jobs/hive_jobs_mapper.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mappers/jobs/hive_jobs_mapper.js b/ambari-web/app/mappers/jobs/hive_jobs_mapper.js index 7ebf8a7..f810bbb 100644 --- a/ambari-web/app/mappers/jobs/hive_jobs_mapper.js +++ b/ambari-web/app/mappers/jobs/hive_jobs_mapper.js @@ -72,6 +72,8 @@ App.hiveJobsMapper = App.QuickDataMapper.create({ hiveJob.end_time = entity.endtime; } hiveJobs.push(hiveJob); + hiveJob = null; + entity = null; }); // Delete IDs not seen from server var hiveJobsModel = model.find().toArray(); @@ -82,7 +84,8 @@ App.hiveJobsMapper = App.QuickDataMapper.create({ }, this); } App.store.loadMany(model, hiveJobs); - App.router.get('mainJobsController').set('content', App.HiveJob.find().toArray()); + json = null; + hiveJobs = null; }, config : {} }); http://git-wip-us.apache.org/repos/asf/ambari/blob/4f226737/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index cc15d9f..9c3ae98 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -1964,6 +1964,7 @@ Em.I18n.translations = { 'menu.item.admin':'Admin', 'jobs.nothingToShow': 'No jobs to display', + 'jobs.loadingTasks': 'Loading...', 'jobs.table.custom.date.am':'AM', 'jobs.table.custom.date.pm':'PM', 'jobs.table.custom.date.header':'Select Custom Dates', http://git-wip-us.apache.org/repos/asf/ambari/blob/4f226737/ambari-web/app/templates/main/jobs.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/jobs.hbs b/ambari-web/app/templates/main/jobs.hbs index a03cc96..81391b6 100644 --- a/ambari-web/app/templates/main/jobs.hbs +++ b/ambari-web/app/templates/main/jobs.hbs @@ -26,7 +26,7 @@ </div> <table id="jobs-table" class="table table-bordered table-striped"> <thead> - {{#view view.sortView classNames="label-row" contentBinding="controller.content"}} + {{#view view.sortView classNames="label-row" contentBinding="view.content"}} <th></th> {{view view.parentView.idSort}} {{view view.parentView.userSort}} @@ -47,10 +47,14 @@ <tbody> {{#if view.noDataToShow}} <tr> + {{#if controller.loaded}} <td class="no-data" {{bindAttr colspan="controller.columnsName.content.length"}}>{{t jobs.nothingToShow}}</td> + {{else}} + <td class="no-data" {{bindAttr colspan="controller.columnsName.content.length"}}>{{t jobs.loadingTasks}}</td> + {{/if}} </tr> {{else}} - {{#each job in controller.content}} + {{#each job in controller.sortedContent}} <tr> <td> {{#if job.failed}} @@ -60,9 +64,9 @@ <td> <div class="id"> {{#if job.hasTezDag}} - <a rel="tooltip" class="job-link" title="{{unbound job.queryText}}" href="#" {{action "showJobDetails" job}}>{{unbound job.name}}</a> + <a rel="tooltip" class="job-link" {{bindAttr title="job.queryText"}} href="#" {{action "showJobDetails" job}}>{{job.name}}</a> {{else}} - <span rel="tooltip" class="job-link" title="{{unbound job.queryText}}">{{unbound job.name}}</span> + <span rel="tooltip" class="job-link" {{bindAttr title="job.queryText"}}>{{job.name}}</span> {{/if}} </div> </td> http://git-wip-us.apache.org/repos/asf/ambari/blob/4f226737/ambari-web/app/views/main/jobs_view.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/jobs_view.js b/ambari-web/app/views/main/jobs_view.js index 4ee2d84..25fbe6b 100644 --- a/ambari-web/app/views/main/jobs_view.js +++ b/ambari-web/app/views/main/jobs_view.js @@ -23,9 +23,7 @@ var sort = require('views/common/sort_view'); App.MainJobsView = App.TableView.extend({ templateName: require('templates/main/jobs'), - content: function () { - return this.get('controller.content'); - }.property('controller.content.length'), + content: [], /**