Browse Source

AMBARI-14829 Memory leak on Alerts page. (ababiichuk)

ababiichuk 9 years ago
parent
commit
f10d41ccbc

+ 41 - 0
ambari-web/app/controllers/main/alert_definitions_controller.js

@@ -28,12 +28,53 @@ App.MainAlertDefinitionsController = Em.ArrayController.extend({
    */
   showFilterConditionsFirstLoad: false,
 
+  contentUpdater: null,
+
   /**
    * List of all <code>App.AlertDefinition</code>
    * @type {App.AlertDefinition[]}
    */
   content: App.AlertDefinition.find(),
 
+  /**
+   * Generates key for alert summary that represents current state
+   */
+  getSummaryCache: function () {
+    var res = '';
+    this.get('content').forEach(function(o) {
+      var summary = o.get('summary');
+      o.get('order').forEach(function (state) {
+        res += summary[state] ? summary[state].count + summary[state].maintenanceCount : 0;
+      });
+    });
+
+    return res;
+   },
+
+  generateCacheByKey: function(key) {
+    if (key === 'summary') {
+      return this.getSummaryCache();
+    }
+
+    return this.get('content').mapProperty(key).join('');
+  },
+
+  contentWasChanged: function(key) {
+    var updatedCache = this.generateCacheByKey(key);
+    if (this.get('cache.' + key) !== updatedCache) {
+      this.set('cache.' + key, updatedCache);
+      this.propertyDidChange('contentUpdater');
+    }
+  },
+
+  cache: {
+    'label': '',
+    'summary': '',
+    'serviceName': '',
+    'lastTriggered': '',
+    'enabled': ''
+  },
+
   /**
    * Enable/disable alertDefinition confirmation popup
    * @param {object} event

+ 7 - 1
ambari-web/app/mappers/alert_definition_summary_mapper.js

@@ -56,6 +56,9 @@ App.alertDefinitionSummaryMapper = App.QuickDataMapper.create({
 
     alertDefinitions.forEach(function (d) {
       var id = d.get('id');
+      if ((alertDefinitionsMap[id].get('stateManager.currentState.name') !== 'saved')) {
+        alertDefinitionsMap[id].get('stateManager').transitionTo('saved');
+      }
       alertDefinitionsMap[id].setProperties(summaryMap[id]);
       if (!alertDefinitionsMap[id].get('enabled')) {
         // clear summary for disabled alert definitions
@@ -89,7 +92,10 @@ App.alertDefinitionSummaryMapper = App.QuickDataMapper.create({
         });
       }
     });
-
+    if (!$.mocho) {
+      //for some reasons this causing error in unit test
+      App.store.commit();
+    }
     console.timeEnd('App.alertDefinitionSummaryMapper execution time');
 
   }

+ 1 - 0
ambari-web/app/models/alerts/alert_definition.js

@@ -345,3 +345,4 @@ App.AlertDefinition.FIXTURES = [];
 App.AlertReportDefinition.FIXTURES = [];
 App.AlertMetricsSourceDefinition.FIXTURES = [];
 App.AlertMetricsUriDefinition.FIXTURES = [];
+App.AlertDefinitionParameter.FIXTURES = [];

+ 4 - 28
ambari-web/app/templates/main/alerts.hbs

@@ -57,39 +57,15 @@
               <span {{bindAttr title="alertDefinition.type"}} {{bindAttr class=":type-icon  alertDefinition.typeIconClass"}}></span>
               <a href="#" {{action "gotoAlertDetails" alertDefinition}}>{{alertDefinition.label}}</a>
             </td>
-            <td class="alert-status">{{{alertDefinition.status}}}</td>
+            <td class="alert-status">
+              {{view App.AlertDefinitionSummary contentBinding="alertDefinition"}}
+            </td>
             <td class="alert-service">{{alertDefinition.serviceDisplayName}}</td>
             <td class="alert-time">
               <time class="timeago" {{bindAttr data-original-title="alertDefinition.lastTriggeredFormatted"}}>{{alertDefinition.lastTriggeredAgoFormatted}}</time>
             </td>
             <td class="last toggle-state-button alert-state">
-              {{#if alertDefinition.enabled not=true}}
-                {{#isAuthorized "CLUSTER.TOGGLE_ALERTS"}}
-                  <a href="#" {{action "toggleState" alertDefinition target="controller"}} {{bindAttr class="alertDefinition.enabled:alert-definition-enable:alert-definition-disable"}}>
-                  <span class="enable-disable-button" {{bindAttr data-original-title="view.enabledTooltip"}}>
-                    {{view.enabledDisplay}}
-                  </span>
-                  </a>
-                {{/isAuthorized}}
-                {{#isNotAuthorized "CLUSTER.TOGGLE_ALERTS"}}
-                    <span {{bindAttr class="alertDefinition.enabled:alert-definition-enable:alert-definition-disable"}}>
-                      {{view.enabledDisplay}}
-                    </span>
-                {{/isNotAuthorized}}
-              {{else}}
-                {{#isAuthorized "CLUSTER.TOGGLE_ALERTS"}}
-                  <a href="#" {{action "toggleState" alertDefinition target="controller"}} {{bindAttr class="alertDefinition.enabled:alert-definition-enable:alert-definition-disable"}}>
-                  <span class="enable-disable-button" {{bindAttr data-original-title="view.disabledTooltip"}}>
-                    {{view.disabledDisplay}}
-                  </span>
-                  </a>
-                {{/isAuthorized}}
-                {{#isNotAuthorized "CLUSTER.TOGGLE_ALERTS"}}
-                  <span {{bindAttr class="alertDefinition.enabled:alert-definition-enable:alert-definition-disable"}}>
-                    {{view.disabledDisplay}}
-                  </span>
-                {{/isNotAuthorized}}
-              {{/if}}
+              {{view App.AlertDefinitionState contentBinding="alertDefinition"}}
             </td>
           </tr>
         {{/each}}

+ 31 - 0
ambari-web/app/templates/main/alerts/alert_definition/alert_definition_state.hbs

@@ -0,0 +1,31 @@
+{{!
+* 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.
+}}
+
+{{#isAuthorized "CLUSTER.TOGGLE_ALERTS"}}
+  <a href="#" {{action "toggleState" view.content target="controller"}}
+    {{bindAttr class="view.content.enabled:alert-definition-enable:alert-definition-disable"}}>
+      <span class="enable-disable-button" {{bindAttr data-original-title="view.tooltipText"}}>
+        {{view.labelText}}
+      </span>
+  </a>
+{{/isAuthorized}}
+{{#isNotAuthorized "CLUSTER.TOGGLE_ALERTS"}}
+  <span {{bindAttr class="view.content.enabled:alert-definition-enable:alert-definition-disable"}}>
+    {{view.labelText}}
+  </span>
+{{/isNotAuthorized}}

+ 28 - 0
ambari-web/app/templates/main/alerts/alert_definition/alert_definition_summary.hbs

@@ -0,0 +1,28 @@
+{{!
+* 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.
+}}
+
+{{#if view.hostCount}}
+  {{#each state in view.states}}
+    <span {{bindAttr class=":alert-state-single-host :label state.stateClass"}}>
+      {{#if state.isMaintenance}}<span class="icon-medkit"></span>{{/if}}
+      {{state.shortStateWithCounter}}
+    </span>
+  {{/each}}
+{{else}}
+  <span class="alert-state-single-host label alert-state-PENDING">NONE</span>
+{{/if}}

+ 14 - 0
ambari-web/app/utils/ember_reopen.js

@@ -224,6 +224,20 @@ Em.View.reopen({
   }
 });
 
+Ember._HandlebarsBoundView.reopen({
+  /**
+   * overwritten set method of Ember._HandlebarsBoundView to avoid uncaught errors
+   * when trying to set property of destroyed view
+   */
+  render: function(buffer){
+    if(!this.get('isDestroyed') && !this.get('isDestroying')){
+      this._super(buffer);
+    } else {
+      console.debug('Calling set on destroyed view');
+    }
+  }
+});
+
 Ember.TextArea.reopen({
   attributeBindings: ['readonly']
 });

+ 2 - 0
ambari-web/app/views.js

@@ -95,6 +95,8 @@ require('views/login');
 require('views/main');
 require('views/main/menu');
 require('views/main/alert_definitions_view');
+require('views/main/alerts/alert_definition/alert_definition_summary');
+require('views/main/alerts/alert_definition/alert_definition_state');
 require('views/main/alerts/definition_details_view');
 require('views/main/alerts/alert_definitions_actions_view');
 require('views/main/alerts/definition_configs_view');

+ 16 - 3
ambari-web/app/views/common/sort_view.js

@@ -119,7 +119,7 @@ var wrapperView = Em.View.extend({
         }
       }, this);
     }
-  }.observes('content.length'),
+  }.observes('controller.contentUpdater'),
 
   /**
    * reset all sorts fields
@@ -185,12 +185,25 @@ var wrapperView = Em.View.extend({
     return func;
   },
 
+  /**
+   * method that runs <code>contentWasChanged<code>
+   *
+   * @method onContentChangeOnce
+   */
+  onContentChangeOnce: function() {
+    var keys = arguments[1].match(/[a-zA-Z]+$/),
+      key = keys.length ? keys[0] : null;
+    if (key) {
+      Em.run.once(this.get('controller'), 'contentWasChanged', key);
+    }
+  },
+
   /**
    * Add observer for key to call  <code>onContentChange</code>
    * @param key
    */
   addSortingObserver: function (key) {
-    this.addObserver('content.@each.' + key, this, 'onContentChange');
+    this.addObserver('controller.content.@each.' + key, this, 'onContentChangeOnce');
   },
 
   /**
@@ -198,7 +211,7 @@ var wrapperView = Em.View.extend({
    * @param key
    */
   removeSortingObserver: function (key) {
-    this.removeObserver('content.@each.' + key, this, 'onContentChange');
+    this.removeObserver('controller.content.@each.' + key, this, 'onContentChangeOnce');
   },
 
   willDestroyElement: function () {

+ 2 - 21
ambari-web/app/views/main/alert_definitions_view.js

@@ -52,6 +52,7 @@ App.MainAlertDefinitionsView = App.TableView.extend({
   },
 
   willDestroyElement: function () {
+    $(".timeago").tooltip('destroy');
     this.removeObserver('pageContent.length', this, 'tooltipsUpdater');
   },
 
@@ -78,26 +79,6 @@ App.MainAlertDefinitionsView = App.TableView.extend({
 
   colPropAssoc: ['', 'label', 'summary', 'serviceName', 'type', 'lastTriggered', 'enabled', 'groups'],
 
-  /**
-   * @type {string}
-   */
-  enabledTooltip: Em.I18n.t('alerts.table.state.enabled.tooltip'),
-
-  /**
-   * @type {string}
-   */
-  disabledTooltip: Em.I18n.t('alerts.table.state.disabled.tooltip'),
-
-  /**
-   * @type {string}
-   */
-  enabledDisplay: Em.I18n.t('alerts.table.state.enabled'),
-
-  /**
-   * @type {string}
-   */
-  disabledDisplay: Em.I18n.t('alerts.table.state.disabled'),
-
   sortView: sort.wrapperView.extend({
     didInsertElement: function () {
       this._super();
@@ -500,7 +481,7 @@ App.MainAlertDefinitionsView = App.TableView.extend({
    */
   tooltipsUpdater: function () {
     Em.run.next(this, function () {
-      App.tooltip($(".enable-disable-button, .timeago"));
+      App.tooltip($(".timeago"));
     });
   },
 

+ 34 - 0
ambari-web/app/views/main/alerts/alert_definition/alert_definition_state.js

@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+var App = require('app');
+
+App.AlertDefinitionState = Em.View.extend({
+
+  templateName: require('templates/main/alerts/alert_definition/alert_definition_state'),
+
+  labelText: Em.computed.ifThenElse('content.enabled', Em.I18n.t('alerts.table.state.enabled'), Em.I18n.t('alerts.table.state.disabled')),
+
+  tooltipText: Em.computed.ifThenElse('content.enabled', Em.I18n.t('alerts.table.state.enabled.tooltip'), Em.I18n.t('alerts.table.state.disabled.tooltip')),
+
+  didInsertElement: function () {
+    App.tooltip(this.$(".enable-disable-button"));
+  },
+  willDestroyElement:function () {
+    this.$(".enable-disable-button").tooltip('destroy');
+  }
+});

+ 65 - 0
ambari-web/app/views/main/alerts/alert_definition/alert_definition_summary.js

@@ -0,0 +1,65 @@
+/**
+ * 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.
+ */
+var App = require('app');
+
+App.AlertDefinitionSummary = Em.View.extend({
+
+  templateName: require('templates/main/alerts/alert_definition/alert_definition_summary'),
+
+  didInsertElement: function() {
+    this.stateObserver();
+  },
+
+  hostCount: 0,
+  states: [],
+
+  stateObserver: function () {
+    var order = this.get('content.order'),
+      summary = this.get('content.summary'),
+      shortState = this.get('content.shortState');
+
+    var hostCnt = 0;
+    order.forEach(function (state) {
+      hostCnt += summary[state] ? summary[state].count + summary[state].maintenanceCount : 0;
+    });
+    var states = [];
+    if (hostCnt) {
+      order.forEach(function (state) {
+        if (summary[state]) {
+          if (summary[state].count) {
+            states.push({
+              'shortStateWithCounter': shortState[state] + (summary[state].count > 1 ? ' (' + summary[state].count + ')' : ''),
+              'isMaintenance': false,
+              'stateClass': 'alert-state-' + state
+            });
+          }
+          if (summary[state].maintenanceCount) {
+            states.push({
+              'shortStateWithCounter': shortState[state] + (summary[state].maintenanceCount > 1 ? ' (' + summary[state].maintenanceCount + ')' : ''),
+              'isMaintenance': true,
+              'stateClass': 'alert-state-PENDING'
+            });
+          }
+        }
+      }, this);
+    }
+    this.set('hostCount', hostCnt);
+    this.set('states', states);
+  }.observes('content.summary')
+
+});