浏览代码

AMBARI-5203. Jobs page becomes extremely slow and unresponsive around 500 queries (alexantonenko)

Alex Antonenko 11 年之前
父节点
当前提交
4f226737f9

+ 73 - 11
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', 'content.@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);
         }

+ 4 - 1
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 : {}
 });

+ 1 - 0
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',

+ 8 - 4
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>

+ 1 - 3
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: [],
 
 
   /**