Browse Source

AMBARI-8100. Flume UI freezes in 1 minute on a 400 node cluster (atkach via srimanth)

Srimanth Gunturi 10 years ago
parent
commit
f93e73c4b1

+ 3 - 1
ambari-web/app/controllers/main/alerts_controller.js

@@ -142,13 +142,15 @@ App.MainAlertsController = Em.Controller.extend({
     if (json && json.legacy_alerts && json.legacy_alerts.detail) {
       json.legacy_alerts.detail.forEach(function (_alert) {
         alerts.pushObject(App.Alert.create({
+          id: _alert.description + "_" + _alert.host_name + "_" + _alert.serviceType,
           title: _alert.description,
           serviceType: _alert.service_name,
           lastTime: _alert.status_time,
           status: this.get('statusNumberMap')[_alert.status] || "4",
           message: _alert.output,
           hostName: _alert.host_name,
-          lastCheck: _alert.last_status_time
+          lastCheck: _alert.last_status_time,
+          isLoaded: true
         }));
       }, this);
     }

+ 2 - 2
ambari-web/app/data/service_graph_config.js

@@ -80,8 +80,8 @@ App.service_graph_config = {
 		'Flume_OutgoingSum',
 		'Flume_GarbageCollection',
 		'Flume_JVMHeapUsed',
-		'Flume_JVMThreadsRunnable',
-		'Flume_CPUUser'
+		'Flume_JVMThreadsRunnable'
+		//'Flume_CPUUser'
 	],
 
 	'storm': [

+ 3 - 2
ambari-web/app/models/alert.js

@@ -128,10 +128,11 @@ App.Alert = Em.Object.extend({
   }.property('date', 'status'),
   
   makeTimeAtleastMinuteAgo: function(d){
-    var diff = App.dateTime() - d.getTime();
+    var time = d.getTime();
+    var diff = App.dateTime() - time;
     if (diff < 60000) {
       diff = 60000 - diff;
-      return new Date(d.getTime() - diff );
+      return new Date(time - diff);
     }
     return d;
   },

+ 2 - 2
ambari-web/app/utils/helper.js

@@ -517,7 +517,7 @@ App.format = {
  */
 App.popover = function (self, options) {
   self.popover(options);
-  self.on("remove DOMNodeRemoved", function () {
+  self.on("remove", function () {
     $(this).trigger('mouseleave');
   });
 };
@@ -533,7 +533,7 @@ App.popover = function (self, options) {
 App.tooltip = function (self, options) {
   self.tooltip(options);
   /* istanbul ignore next */
-  self.on("remove DOMNodeRemoved", function () {
+  self.on("remove", function () {
     $(this).trigger('mouseleave');
   });
 };

+ 71 - 8
ambari-web/app/views/main/service/info/summary.js

@@ -37,6 +37,14 @@ App.AlertItemView = Em.View.extend({
 App.MainServiceInfoSummaryView = Em.View.extend({
   templateName: require('templates/main/service/info/summary'),
   attributes:null,
+  /**
+   * alerts collection
+   */
+  alerts: [],
+  /**
+   * associative collection of alerts
+   */
+  alertsMap: {},
   /**
    *  @property {String} templatePathPrefix - base path for custom templates
    *    if you want to add custom template, add <service_name>.hbs file to
@@ -82,9 +90,60 @@ App.MainServiceInfoSummaryView = Em.View.extend({
   }.property('App.services.hasClient'),
 
   alertsControllerBinding: 'App.router.mainAlertsController',
-  alerts: function () {
-    return this.get('alertsController.alerts');
-  }.property('alertsController.alerts'),
+  /**
+   * observes changes to alerts collection
+   */
+  observeAlerts: function () {
+    var newAlerts = this.get('alertsController.alerts'),
+      currentAlerts = this.get('alerts'),
+      alertsMap;
+
+    if (currentAlerts.length === 0) {
+      alertsMap = {};
+      newAlerts.forEach(function (alert) {
+        alertsMap[alert.id] = alert;
+        currentAlerts.pushObject(alert);
+      }, this);
+      this.set('alertsMap', alertsMap);
+    } else if (newAlerts.length > 0) {
+      this.updateAlerts(newAlerts, currentAlerts);
+    } else {
+      currentAlerts.clear();
+      this.set('alertsMap', {});
+    }
+  }.observes('alertsController.alerts'),
+  /**
+   * update existing alerts according to new data
+   * @param newAlerts
+   * @param currentAlerts
+   */
+  updateAlerts: function (newAlerts, currentAlerts) {
+    var alertsMap = this.get('alertsMap');
+    var mutableFields = ['status', 'message', 'lastCheck', 'lastTime'];
+    // minimal time difference to apply changes to "lastTime" property
+    var minTimeDiff = 60000;
+    var curTime = App.dateTime();
+    currentAlerts.setEach('isLoaded', false);
+
+    newAlerts.forEach(function (alert) {
+      var currentAlert = alertsMap[alert.get('id')];
+      if (currentAlert) {
+        mutableFields.forEach(function (field) {
+          if (currentAlert.get(field) !== alert.get(field)) {
+            currentAlert.set(field, alert.get(field));
+          }
+        });
+        currentAlert.set('isLoaded', true);
+      } else {
+        alertsMap[alert.get('id')] = alert;
+        currentAlerts.pushObject(alert);
+      }
+    });
+    currentAlerts.filterProperty('isLoaded', false).slice(0).forEach(function (alert) {
+      delete alertsMap[alert.get('id')];
+      currentAlerts.removeObject(alert);
+    }, this);
+  },
 
   noTemplateService: function () {
     var serviceName = this.get("service.serviceName");
@@ -116,11 +175,13 @@ App.MainServiceInfoSummaryView = Em.View.extend({
     if (service.get("id") == "ZOOKEEPER" || service.get("id") == "FLUME") {
       var servers = service.get('hostComponents').filterProperty('isMaster');
       if (servers.length > 0) {
-        result = [{
-          'host': servers[0].get('displayName'),
-          'isComma': false,
-          'isAnd': false
-        }];
+        result = [
+          {
+            'host': servers[0].get('displayName'),
+            'isComma': false,
+            'isAnd': false
+          }
+        ];
       }
       if (servers.length > 1) {
         result[0].isComma = true;
@@ -435,6 +496,8 @@ App.MainServiceInfoSummaryView = Em.View.extend({
   },
   willDestroyElement: function(){
     this.get('alertsController').set('isUpdating', false);
+    this.get('alerts').clear();
+    this.set('alertsMap', {});
   },
 
   setAlertsWindowSize: function() {

+ 216 - 24
ambari-web/test/views/main/service/info/summary_test.js

@@ -22,7 +22,7 @@ require('views/main/service/info/summary');
 
 describe('App.MainServiceInfoSummaryView', function() {
 
-  var mainServiceInfoSummaryView = App.MainServiceInfoSummaryView.create({
+  var view = App.MainServiceInfoSummaryView.create({
     monitorsLiveTextView: Em.View.create(),
     controller: Em.Object.create({
       content: Em.Object.create({
@@ -30,16 +30,17 @@ describe('App.MainServiceInfoSummaryView', function() {
         serviceName: 'HDFS',
         hostComponents: []
       })
-    })
+    }),
+    alertsController: Em.Object.create()
   });
 
   describe('#servers', function () {
     it('services shuldn\'t have servers except FLUME and ZOOKEEPER', function () {
-      expect(mainServiceInfoSummaryView.get('servers')).to.be.empty;
+      expect(view.get('servers')).to.be.empty;
     });
 
     it('if one server exists then first server should have isComma and isAnd property false', function () {
-      mainServiceInfoSummaryView.set('controller.content', Em.Object.create({
+      view.set('controller.content', Em.Object.create({
         id: 'ZOOKEEPER',
         serviceName: 'ZOOKEEPER',
         hostComponents: [
@@ -49,12 +50,12 @@ describe('App.MainServiceInfoSummaryView', function() {
           })
         ]
       }));
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(0).isComma).to.equal(false);
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(0).isAnd).to.equal(false);
+      expect(view.get('servers').objectAt(0).isComma).to.equal(false);
+      expect(view.get('servers').objectAt(0).isAnd).to.equal(false);
     });
 
     it('if more than one servers exist then first server should have isComma - true and isAnd - false', function () {
-      mainServiceInfoSummaryView.set('controller.content', Em.Object.create({
+      view.set('controller.content', Em.Object.create({
         id: 'ZOOKEEPER',
         serviceName: 'ZOOKEEPER',
         hostComponents: [
@@ -68,14 +69,14 @@ describe('App.MainServiceInfoSummaryView', function() {
           })
         ]
       }));
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(0).isComma).to.equal(true);
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(0).isAnd).to.equal(false);
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(1).isComma).to.equal(false);
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(1).isAnd).to.equal(false);
+      expect(view.get('servers').objectAt(0).isComma).to.equal(true);
+      expect(view.get('servers').objectAt(0).isAnd).to.equal(false);
+      expect(view.get('servers').objectAt(1).isComma).to.equal(false);
+      expect(view.get('servers').objectAt(1).isAnd).to.equal(false);
     });
 
     it('if more than two servers exist then second server should have isComma - false and isAnd - true', function () {
-      mainServiceInfoSummaryView.set('controller.content', Em.Object.create({
+      view.set('controller.content', Em.Object.create({
         id: 'ZOOKEEPER',
         serviceName: 'ZOOKEEPER',
         hostComponents: [
@@ -93,12 +94,12 @@ describe('App.MainServiceInfoSummaryView', function() {
           })
         ]
       }));
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(0).isComma).to.equal(true);
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(0).isAnd).to.equal(false);
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(1).isComma).to.equal(false);
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(1).isAnd).to.equal(true);
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(2).isComma).to.equal(false);
-      expect(mainServiceInfoSummaryView.get('servers').objectAt(2).isAnd).to.equal(false);
+      expect(view.get('servers').objectAt(0).isComma).to.equal(true);
+      expect(view.get('servers').objectAt(0).isAnd).to.equal(false);
+      expect(view.get('servers').objectAt(1).isComma).to.equal(false);
+      expect(view.get('servers').objectAt(1).isAnd).to.equal(true);
+      expect(view.get('servers').objectAt(2).isComma).to.equal(false);
+      expect(view.get('servers').objectAt(2).isAnd).to.equal(false);
     });
 
   });
@@ -107,21 +108,212 @@ describe('App.MainServiceInfoSummaryView', function() {
     it("should return a single array with the items in the fom of '<name>.extend()' when the number of items is less than 4", function() {
       var graphs = ['HDFS_SpaceUtilization'];
 
-      expect(mainServiceInfoSummaryView.constructGraphObjects(graphs).length).to.equal(1);
-      expect(mainServiceInfoSummaryView.constructGraphObjects(graphs)[0].length).to.equal(1);
+      expect(view.constructGraphObjects(graphs).length).to.equal(1);
+      expect(view.constructGraphObjects(graphs)[0].length).to.equal(1);
     });
 
     it("should return an array with arrays that are grouped into sizes of 4 or less when number of items is greater than 4", function() {
       var graphs = ['HDFS_SpaceUtilization', 'YARN_AllocatedMemory', 'MapReduce_JobsStatus', 
       'HBASE_ClusterRequests', 'Flume_ChannelSizeMMA'];
 
-      expect(mainServiceInfoSummaryView.constructGraphObjects(graphs).length).to.equal(2);
-      expect(mainServiceInfoSummaryView.constructGraphObjects(graphs)[0].length).to.equal(4);
-      expect(mainServiceInfoSummaryView.constructGraphObjects(graphs)[1].length).to.equal(1);
+      expect(view.constructGraphObjects(graphs).length).to.equal(2);
+      expect(view.constructGraphObjects(graphs)[0].length).to.equal(4);
+      expect(view.constructGraphObjects(graphs)[1].length).to.equal(1);
     });
 
     it("should return an empty array if the graphs array provided is empty", function() {
-      expect(mainServiceInfoSummaryView.constructGraphObjects([])).to.be.empty;
+      expect(view.constructGraphObjects([])).to.be.empty;
+    });
+  });
+
+  describe("#observeAlerts()", function() {
+    it("No alerts loaded", function() {
+      var alerts = [];
+      view.set('alertsController.alerts', []);
+      view.set('alerts', alerts)
+      view.observeAlerts();
+
+      expect(alerts).to.be.empty;
+      expect(view.get('alertsMap')).to.be.empty;
+    });
+    it("One alert loaded", function() {
+      var alerts = [];
+      view.set('alertsController.alerts', [{
+        id: 1
+      }]);
+      view.set('alerts', alerts)
+      view.observeAlerts();
+
+      expect(alerts[0]).to.be.eql({
+        "id": 1
+      });
+      expect(alerts.length).to.be.equal(1);
+      expect(view.get('alertsMap')).to.be.eql({"1": {
+        "id": 1
+      }});
+    });
+    it("No new alerts", function() {
+      var alerts = [{id: 1}];
+      view.set('alertsController.alerts', []);
+      view.set('alerts', alerts)
+      view.set('alertsMap', {'1': {id: '1'}});
+      view.observeAlerts();
+
+      expect(alerts).to.be.empty;
+      expect(view.get('alertsMap')).to.be.empty;
+    });
+    before(function () {
+      sinon.stub(view, 'updateAlerts', Em.K);
+    });
+    after(function () {
+      view.updateAlerts.restore();
+    });
+    it("Alerts already exist", function() {
+      var alerts = [{id: 1}];
+      view.set('alertsController.alerts', [{
+        id: 1
+      }]);
+      view.set('alerts', alerts)
+      view.set('alertsMap', {'1': {id: '1'}});
+      view.observeAlerts();
+
+      expect(view.updateAlerts.calledWith(
+        [{
+          id: 1
+        }],
+        [{id: 1}]
+      )).to.be.true;
+    });
+  });
+
+  describe("#updateAlerts()", function() {
+    var currentAlerts = [];
+    var alertsMap = {};
+    var newAlerts = [];
+    it("Add new alert", function() {
+      newAlerts.clear();
+      currentAlerts.clear();
+      alertsMap = {};
+
+      newAlerts.pushObjects([
+        Em.Object.create({
+          id: '1',
+          status: '1',
+          isLoaded: true
+        }),
+        Em.Object.create({
+          id: '2',
+          status: '2',
+          isLoaded: true
+        })
+      ]);
+      var currentAlert = Em.Object.create({
+          id: '1',
+          status: '1',
+          isLoaded: true
+        });
+      alertsMap['1'] = currentAlert;
+      view.set('alertsMap', alertsMap);
+      currentAlerts.pushObject(currentAlert);
+
+      view.updateAlerts(newAlerts, currentAlerts);
+      expect(currentAlerts.length).to.be.equal(2);
+      expect(currentAlerts[1]).to.be.eql(Em.Object.create({
+        id: '2',
+        status: '2',
+        isLoaded: true
+      }));
+      expect(alertsMap).to.be.eql({
+        "1": Em.Object.create({
+          id: '1',
+          status: '1',
+          isLoaded: true
+        }),
+        "2": Em.Object.create({
+          id: '2',
+          status: '2',
+          isLoaded: true
+        })
+      });
+    });
+    it("Update properties of existing alert", function() {
+      newAlerts.clear();
+      currentAlerts.clear();
+      alertsMap = {};
+
+      newAlerts.pushObjects([
+        Em.Object.create({
+          id: '1',
+          status: '2',
+          isLoaded: true
+        })
+      ]);
+      var currentAlert = Em.Object.create({
+        id: '1',
+        status: '1',
+        isLoaded: true
+      });
+      alertsMap['1'] = currentAlert;
+      view.set('alertsMap', alertsMap);
+      currentAlerts.pushObject(currentAlert);
+
+      view.updateAlerts(newAlerts, currentAlerts);
+      expect(currentAlerts.length).to.be.equal(1);
+      expect(currentAlerts[0]).to.be.eql(Em.Object.create({
+        id: '1',
+        status: '2',
+        isLoaded: true
+      }));
+      expect(alertsMap).to.be.eql({
+        "1": Em.Object.create({
+          id: '1',
+          status: '2',
+          isLoaded: true
+        })
+      });
+    });
+    it("delete old alert", function() {
+      newAlerts.clear();
+      currentAlerts.clear();
+      alertsMap = {};
+
+      newAlerts.pushObjects([
+        Em.Object.create({
+          id: '1',
+          status: '1',
+          isLoaded: true
+        })
+      ]);
+      var currentAlert1 = Em.Object.create({
+        id: '1',
+        status: '1',
+        isLoaded: true
+      });
+      var currentAlert2 = Em.Object.create({
+        id: '2',
+        status: '2',
+        isLoaded: true
+      });
+      alertsMap["1"] = currentAlert1;
+      alertsMap["2"] = currentAlert2;
+      view.set('alertsMap', alertsMap);
+      currentAlerts.pushObjects([currentAlert1, currentAlert2]);
+
+      view.updateAlerts(newAlerts, currentAlerts);
+
+      expect(currentAlerts.length).to.be.equal(1);
+      expect(currentAlerts[0]).to.be.eql(Em.Object.create({
+        id: '1',
+        status: '1',
+        isLoaded: true
+      }));
+      expect(alertsMap).to.be.eql({
+        "1": Em.Object.create({
+          id: '1',
+          status: '1',
+          isLoaded: true
+        })
+      });
     });
   });
 });