Browse Source

AMBARI-1519. Ambari Web goes back and forth between frozen and usable state peridocially on a large cluster. (yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/trunk@1450945 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 years ago
parent
commit
1d44649afe

+ 3 - 0
CHANGES.txt

@@ -387,6 +387,9 @@ Trunk (unreleased changes):
 
  BUG FIXES
 
+ AMBARI-1519. Ambari Web goes back and forth between frozen and usable state
+ peridocially on a large cluster. (yusaku)
+
  AMBARI-1499. Add hosts is broken. (yusaku)
 
  AMBARI-1501. Nagios alerts do not update automatically. (yusaku)

+ 4 - 0
ambari-web/app/mappers/status_mapper.js

@@ -58,6 +58,8 @@ App.statusMapper = App.QuickDataMapper.create({
         }, this)
       }, this);
 
+      // console.profile("App.statusMapper.map() profile");
+
       var hostComponents = App.HostComponent.find();
 
       hostComponents.forEach(function(hostComponent) {
@@ -67,6 +69,8 @@ App.statusMapper = App.QuickDataMapper.create({
         }
       });
 
+      // console.profileEnd();
+
       console.log('out status mapper.  Took ' + (new Date().getTime() - start) + 'ms');
     }
   }

+ 16 - 3
ambari-web/app/models/host.js

@@ -122,7 +122,19 @@ App.Host = DS.Model.extend({
     if (this.get('loadFifteen') != null) return this.get('loadFifteen').toFixed(2);
   }.property('loadOne', 'loadFive', 'loadFifteen'),
 
-  healthClass: function(){
+  // Instead of making healthStatus a computed property that listens on hostComponents.@each.workStatus,
+  // we are creating a separate observer _updateHealthStatus.  This is so that healthStatus is updated
+  // only once after the run loop.  This is because Ember invokes the computed property every time
+  // a property that it depends on changes.  For example, App.statusMapper's map function would invoke
+  // the computed property too many times and freezes the UI without this hack.
+  // See http://stackoverflow.com/questions/12467345/ember-js-collapsing-deferring-expensive-observers-or-computed-properties
+  healthClass: '',
+
+  _updateHealthClass: function(){
+    Ember.run.once(this, 'updateHealthClass');
+  }.observes('healthStatus', 'hostComponents.@each.workStatus'),
+
+  updateHealthClass: function(){
     var healthStatus = this.get('healthStatus');
     /**
      * Do nothing until load
@@ -147,8 +159,9 @@ App.Host = DS.Model.extend({
         healthStatus = status;
       }
     }
-    return 'health-status-' + healthStatus;
-  }.property('healthStatus', 'hostComponents.@each.workStatus'),
+    this.set('healthClass', 'health-status-' + healthStatus);
+  },
+
   healthToolTip: function(){
     var hostComponents = this.get('hostComponents').filter(function(item){
       if(item.get('workStatus') !== App.HostComponentStatus.started){

+ 41 - 19
ambari-web/app/models/service.js

@@ -35,24 +35,45 @@ App.Service = DS.Model.extend({
     return !(this.get('healthStatus') == 'green');
   }.property('healthStatus'),
 
-  healthStatus: function () {
+  // Instead of making healthStatus a computed property that listens on hostComponents.@each.workStatus,
+  // we are creating a separate observer _updateHealthStatus.  This is so that healthStatus is updated
+  // only once after the run loop.  This is because Ember invokes the computed property every time
+  // a property that it depends on changes.  For example, App.statusMapper's map function would invoke
+  // the computed property too many times and freezes the UI without this hack.
+  // See http://stackoverflow.com/questions/12467345/ember-js-collapsing-deferring-expensive-observers-or-computed-properties
+  healthStatus: '',
+
+  updateHealthStatus: function () {
+    // console.log('model:service.healthStatus ' + this.get('serviceName'));
     var components = this.get('hostComponents').filterProperty('isMaster', true);
     var isGreen = (this.get('serviceName') === 'HBASE' ?
       components.someProperty('workStatus', App.HostComponentStatus.started) :
       components.everyProperty('workStatus', App.HostComponentStatus.started)) ;
 
     if (isGreen) {
-      return 'green';
+      this.set('healthStatus', 'green');
     } else if (components.someProperty('workStatus', App.HostComponentStatus.starting)) {
-      return 'green-blinking';
+      this.set('healthStatus', 'green-blinking');
     } else if (components.someProperty('workStatus', App.HostComponentStatus.stopped)) {
-      return 'red';
+      this.set('healthStatus', 'red');
     } else {
-      return 'red-blinking';
+      this.set('healthStatus', 'red-blinking');
     }
-  }.property('hostComponents.@each.workStatus'),
+  },
+
+  /**
+   * Every time when changes workStatus of any component we schedule recalculating values related from them
+   */
+  _updateHealthStatus: (function() {
+    Ember.run.once(this, 'updateHealthStatus');
+    Ember.run.once(this, 'updateIsStopped');
+    Ember.run.once(this, 'updateIsStarted');
+  }).observes('hostComponents.@each.workStatus'),
 
-  isStopped: function () {
+  isStopped: false,
+  isStarted: false,
+
+  updateIsStopped: function () {
     var components = this.get('hostComponents');
     var flag = true;
     components.forEach(function (_component) {
@@ -60,13 +81,16 @@ App.Service = DS.Model.extend({
         flag = false;
       }
     }, this);
-    return flag;
-  }.property('hostComponents.@each.workStatus'),
+    this.set('isStopped', flag);
+  },
 
-  isStarted: function () {
+  updateIsStarted: function () {
     var components = this.get('hostComponents').filterProperty('isMaster', true);
-    return components.everyProperty('workStatus', App.HostComponentStatus.started);
-  }.property('hostComponents.@each.workStatus'),
+    this.set('isStarted',
+      components.everyProperty('workStatus', App.HostComponentStatus.started)
+    );
+  },
+
   isMaintained: function () {
     var maintainedServices = [
       "HDFS",
@@ -79,10 +103,9 @@ App.Service = DS.Model.extend({
       "PIG",
       "SQOOP"
     ];
-    for (var i in maintainedServices) {
-      if (this.get('serviceName') == maintainedServices[i]) return true;
-    }
+    return maintainedServices.contains(this.get('serviceName'));
   }.property('serviceName'),
+
   isConfigurable: function () {
     var configurableServices = [
       "HDFS",
@@ -96,10 +119,9 @@ App.Service = DS.Model.extend({
       "SQOOP",
       "NAGIOS"
     ];
-    for (var i in configurableServices) {
-      if (this.get('serviceName') == configurableServices[i]) return true;
-    }
+    return configurableServices.contains(this.get('serviceName'));
   }.property('serviceName'),
+
   displayName: function () {
     switch (this.get('serviceName').toLowerCase()) {
       case 'hdfs':
@@ -133,7 +155,7 @@ App.Service = DS.Model.extend({
 
 App.Service.Health = {
   live: "LIVE",
-  dead: "DEAD",
+  dead: "DEAD-RED",
   starting: "STARTING",
   stopping: "STOPPING",