changeset 270:4023b79e4d58

Load historical jvm-memory data at controller init Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-October/025371.html
author Andrew Azores <aazores@redhat.com>
date Wed, 11 Oct 2017 14:59:09 -0400
parents a0ae36ed4a9a
children 671cfc1d001d
files mock-api/endpoints/jvm-memory.endpoint.js src/app/components/jvm-info/jvm-memory/jvm-memory.controller.js src/app/components/jvm-info/jvm-memory/jvm-memory.controller.spec.js src/app/components/jvm-info/jvm-memory/jvm-memory.service.js src/app/components/jvm-info/jvm-memory/jvm-memory.service.spec.js
diffstat 5 files changed, 248 insertions(+), 145 deletions(-) [+]
line wrap: on
line diff
--- a/mock-api/endpoints/jvm-memory.endpoint.js	Wed Oct 11 14:59:09 2017 -0400
+++ b/mock-api/endpoints/jvm-memory.endpoint.js	Wed Oct 11 14:59:09 2017 -0400
@@ -1,96 +1,106 @@
+var _ = require('lodash');
+
 function jvmMemory (server) {
-  var _ = require('lodash');
   server.init('jvmMemory');
   server.app.get('/jvm-memory/0.0.3/jvms/:jvmId', function (req, res) {
     server.logRequest('jvm-memory', req);
 
     var jvmId = req.params.jvmId;
+    var query = req.query.query;
+
+    var now = Date.now();
+    var since = query.split('timeStamp>=')[1];
+    var elapsed = now - since;
+    var count = _.floor(elapsed / 1000);
+
+    var response = [];
+    for (var i = count; i >= 0; i--) {
+      response.push(makeSample(jvmId, now - (i * 1000)));
+    }
 
     res.setHeader('Content-Type', 'application/json');
-    res.send(JSON.stringify(
+    res.send(JSON.stringify({ response: response }));
+  });
+}
+
+function makeSample (jvmId, timestamp) {
+  return {
+    agentId: 'foo-agentId',
+    jvmId: jvmId,
+    timeStamp: { $numberLong: timestamp.toString() },
+    metaspaceMaxCapacity: { $numberLong: '0' },
+    metaspaceMinCapacity: { $numberLong: '0' },
+    metaspaceCapacity: { $numberLong: (4096 * 1024 * 1024).toString() },
+    metaspaceUsed: { $numberLong: _.round(4096 * 1024 * 1024 * Math.random()).toString() },
+    generations: [
       {
-        response: [
+        capacity: { $numberLong: (100 * 1024 * 1024).toString() },
+        collector: 'Shenandoah',
+        maxCapacity: { $numberLong: (200 * 1024 * 1024).toString() },
+        name: 'old',
+        spaces: [
+          {
+            capacity: { $numberLong: (50 * 1024 * 1024).toString() },
+            index: 0,
+            maxCapacity: { $numberLong: (100 * 1024 * 1024).toString() },
+            name: 'Gen 0 Space 0',
+            used: { $numberLong: _.round(50 * 1024 * 1024 * Math.random()).toString() }
+          },
           {
-            agentId: 'foo-agentId',
-            jvmId: jvmId,
-            timeStamp: { $numberLong: Date.now().toString() },
-            metaspaceMaxCapacity: { $numberLong: '0' },
-            metaspaceMinCapacity: { $numberLong: '0' },
-            metaspaceCapacity: { $numberLong: (4096 * 1024 * 1024).toString() },
-            metaspaceUsed: { $numberLong: _.round(4096 * 1024 * 1024 * Math.random()).toString() },
-            generations: [
-              {
-                capacity: { $numberLong: (100 * 1024 * 1024).toString() },
-                collector: 'Shenandoah',
-                maxCapacity: { $numberLong: (200 * 1024 * 1024).toString() },
-                name: 'old',
-                spaces: [
-                  {
-                    capacity: { $numberLong: (50 * 1024 * 1024).toString() },
-                    index: 0,
-                    maxCapacity: { $numberLong: (100 * 1024 * 1024).toString() },
-                    name: 'Gen 0 Space 0',
-                    used: { $numberLong: _.round(50 * 1024 * 1024 * Math.random()).toString() }
-                  },
-                  {
-                    capacity: { $numberLong: (50 * 1024 * 1024).toString() },
-                    index: 1,
-                    maxCapacity: { $numberLong: (100 * 1024 * 1024).toString() },
-                    name: 'Gen 0 Space 1',
-                    used: { $numberLong: _.round(50 * 1024 * 1024 * Math.random()).toString() }
-                  }
-                ]
-              },
-              {
-                capacity: { $numberLong: (200 * 1024 * 1024).toString() },
-                collector: 'Shenandoah',
-                maxCapacity: { $numberLong: (400 * 1024 * 1024).toString() },
-                name: 'new',
-                spaces: [
-                  {
-                    capacity: { $numberLong: (200 * 1024 * 1024).toString() },
-                    index: 0,
-                    maxCapacity: { $numberLong: (400 * 1024 * 1024).toString() },
-                    name: 'Gen 1 Space 0',
-                    used: { $numberLong: _.round(200 * 1024 * 1024 * Math.random()).toString() }
-                  }
-                ]
-              },
-              {
-                capacity: { $numberLong: (400 * 1024 * 1024).toString() },
-                collector: 'G1',
-                maxCapacity: { $numberLong: (1600 * 1024 * 1024).toString() },
-                name: 'newest',
-                spaces: [
-                  {
-                    capacity: { $numberLong: (50 * 1024 * 1024).toString() },
-                    index: 0,
-                    maxCapacity: { $numberLong: (400 * 1024 * 1024).toString() },
-                    name: 'Gen 2 Space 0',
-                    used: { $numberLong: _.round(50 * 1024 * 1024 * Math.random()).toString() }
-                  },
-                  {
-                    capacity: { $numberLong: (100 * 1024 * 1024).toString() },
-                    index: 1,
-                    maxCapacity: { $numberLong: (200 * 1024 * 1024).toString() },
-                    name: 'Gen 2 Space 1',
-                    used: { $numberLong: _.round(100 * 1024 * 1024 * Math.random()).toString() }
-                  },
-                  {
-                    capacity: { $numberLong: (250 * 1024 * 1024).toString() },
-                    index: 2,
-                    maxCapacity: { $numberLong: (1000 * 1024 * 1024).toString() },
-                    name: 'Gen 2 Space 2',
-                    used: { $numberLong: _.round(250 * 1024 * 1024 * Math.random()).toString() }
-                  }
-                ]
-              }
-            ]
+            capacity: { $numberLong: (50 * 1024 * 1024).toString() },
+            index: 1,
+            maxCapacity: { $numberLong: (100 * 1024 * 1024).toString() },
+            name: 'Gen 0 Space 1',
+            used: { $numberLong: _.round(50 * 1024 * 1024 * Math.random()).toString() }
+          }
+        ]
+      },
+      {
+        capacity: { $numberLong: (200 * 1024 * 1024).toString() },
+        collector: 'Shenandoah',
+        maxCapacity: { $numberLong: (400 * 1024 * 1024).toString() },
+        name: 'new',
+        spaces: [
+          {
+            capacity: { $numberLong: (200 * 1024 * 1024).toString() },
+            index: 0,
+            maxCapacity: { $numberLong: (400 * 1024 * 1024).toString() },
+            name: 'Gen 1 Space 0',
+            used: { $numberLong: _.round(200 * 1024 * 1024 * Math.random()).toString() }
+          }
+        ]
+      },
+      {
+        capacity: { $numberLong: (400 * 1024 * 1024).toString() },
+        collector: 'G1',
+        maxCapacity: { $numberLong: (1600 * 1024 * 1024).toString() },
+        name: 'newest',
+        spaces: [
+          {
+            capacity: { $numberLong: (50 * 1024 * 1024).toString() },
+            index: 0,
+            maxCapacity: { $numberLong: (400 * 1024 * 1024).toString() },
+            name: 'Gen 2 Space 0',
+            used: { $numberLong: _.round(50 * 1024 * 1024 * Math.random()).toString() }
+          },
+          {
+            capacity: { $numberLong: (100 * 1024 * 1024).toString() },
+            index: 1,
+            maxCapacity: { $numberLong: (200 * 1024 * 1024).toString() },
+            name: 'Gen 2 Space 1',
+            used: { $numberLong: _.round(100 * 1024 * 1024 * Math.random()).toString() }
+          },
+          {
+            capacity: { $numberLong: (250 * 1024 * 1024).toString() },
+            index: 2,
+            maxCapacity: { $numberLong: (1000 * 1024 * 1024).toString() },
+            name: 'Gen 2 Space 2',
+            used: { $numberLong: _.round(250 * 1024 * 1024 * Math.random()).toString() }
           }
         ]
       }
-    ));
-  });
+    ]
+  }
 }
 
 module.exports = jvmMemory;
--- a/src/app/components/jvm-info/jvm-memory/jvm-memory.controller.js	Wed Oct 11 14:59:09 2017 -0400
+++ b/src/app/components/jvm-info/jvm-memory/jvm-memory.controller.js	Wed Oct 11 14:59:09 2017 -0400
@@ -160,7 +160,7 @@
 
   _start () {
     this._stop();
-    this._update();
+    this._loadHistoricalData();
     this._refresh = this._interval(() => this._update(), this.refreshRate);
   }
 
@@ -184,8 +184,8 @@
   }
 
   set dataAgeLimit (val) {
-    this._dataAgeLimit = val;
-    this._trimData();
+    this._dataAgeLimit = parseInt(val);
+    this._loadHistoricalData();
   }
 
   get dataAgeLimit () {
@@ -194,67 +194,88 @@
 
   multichartMetaspace () {
     return new Promise(resolve =>
-      this._jvmMemoryService.getJvmMemory(this.jvmId).then(resp =>
-        resolve(this.convertMemStat(resp[0].metaspaceUsed))
+      this._jvmMemoryService.getSnapshot(this.jvmId).then(resp =>
+        resolve(this.convertMemStat(resp.metaspaceUsed))
       )
     );
   }
 
   multichartSpace (generationIndex, spaceIndex) {
     return new Promise(resolve =>
-      this._jvmMemoryService.getJvmMemory(this.jvmId).then(resp => {
+      this._jvmMemoryService.getSnapshot(this.jvmId).then(resp => {
         generationIndex = parseInt(generationIndex);
         spaceIndex = parseInt(spaceIndex);
-        let generation = resp[0].generations[generationIndex];
+        let generation = resp.generations[generationIndex];
         let space = generation.spaces[spaceIndex];
         resolve(this.convertMemStat(space.used));
       })
     );
   }
 
-  _update () {
-    this._jvmMemoryService.getJvmMemory(this.jvmId, this._lastUpdate).then(resp => {
-      this._lastUpdate = this._getCurrentTimestamp();
-      let keys = [];
-      resp.forEach(update => {
-        for (let i = 0; i < update.generations.length; i++) {
-          update.generations[i].index = i;
-        }
+  _loadHistoricalData () {
+    this._clearData();
+    let now = this._getCurrentTimestamp();
+    let limit = now - this._dataAgeLimit;
+    this._jvmMemoryService.getJvmMemory(this.jvmId, limit)
+      .then(resp => this._processUpdates(resp));
+  }
 
-        let timestamp = this._metricToNumber(update.timeStamp);
+  _clearData () {
+    this._metaspaceData.timestamps = [];
+    this._metaspaceData.used = [];
+    this._metaspaceData.capacity = [];
+    this._generationData.clear();
+  }
 
-        let metaspaceUsed = this._metricToNumber(update.metaspaceUsed);
-        let metaspaceCapacity = this._metricToNumber(update.metaspaceCapacity);
+  _update () {
+    this._jvmMemoryService.getJvmMemory(this.jvmId, this._lastUpdate)
+      .then(resp => this._processUpdates(resp));
+  }
 
-        this._metaspaceData.timestamps.push(timestamp);
-        this._metaspaceData.used.push(metaspaceUsed);
-        this._metaspaceData.capacity.push(metaspaceCapacity);
+  _processUpdates (resp) {
+    this._lastUpdate = this._getCurrentTimestamp();
+    let keys = [];
 
-        update.generations.forEach(generation => {
-          generation.spaces.forEach(space => {
-            let key = this._getKey(generation.index, space.index);
-            keys.push(key);
-            if (!this._generationData.has(key)) {
-              this._generationData.set(key, {
-                timestamps: [],
-                used: [],
-                capacity: []
-              });
-            }
+    resp.forEach(update => {
+      for (let i = 0; i < update.generations.length; i++) {
+        update.generations[i].index = i;
+      }
+
+      let timestamp = this._metricToNumber(update.timeStamp);
+
+      let metaspaceUsed = this.convertMemStat(update.metaspaceUsed);
+      let metaspaceCapacity = this.convertMemStat(update.metaspaceCapacity);
+
+      this._metaspaceData.timestamps.push(timestamp);
+      this._metaspaceData.used.push(metaspaceUsed);
+      this._metaspaceData.capacity.push(metaspaceCapacity);
 
-            let used = this._metricToNumber(space.used);
-            let capacity = this._metricToNumber(space.capacity);
-            let data = this._generationData.get(key);
-            data.timestamps.push(timestamp);
-            data.used.push(used);
-            data.capacity.push(capacity);
-          });
+      update.generations.forEach(generation => {
+        generation.spaces.forEach(space => {
+          let key = this._getKey(generation.index, space.index);
+          keys.push(key);
+          if (!this._generationData.has(key)) {
+            this._generationData.set(key, {
+              timestamps: [],
+              used: [],
+              capacity: []
+            });
+          }
+
+          let used = this.convertMemStat(space.used);
+          let capacity = this.convertMemStat(space.capacity);
+          let data = this._generationData.get(key);
+          data.timestamps.push(timestamp);
+          data.used.push(used);
+          data.capacity.push(capacity);
         });
       });
-      this._trimData();
+    });
+    this._trimData();
+    if (resp.length > 0) {
       this._updateLineCharts(keys);
       this._updateBarCharts(resp[0]);
-    });
+    }
   }
 
   _trimData () {
@@ -332,7 +353,6 @@
         let key = this._getKey(gen.index, space.index);
 
         let generationData = this._generationData.get(key);
-        let latestTimestamp = _.last(generationData.timestamp);
         let latestUsed = _.last(generationData.used);
         let latestCapacity = _.last(generationData.capacity);
 
--- a/src/app/components/jvm-info/jvm-memory/jvm-memory.controller.spec.js	Wed Oct 11 14:59:09 2017 -0400
+++ b/src/app/components/jvm-info/jvm-memory/jvm-memory.controller.spec.js	Wed Oct 11 14:59:09 2017 -0400
@@ -47,7 +47,8 @@
         then: sinon.spy()
       };
       memSvc = {
-        getJvmMemory: sinon.stub().returns(promise)
+        getJvmMemory: sinon.stub().returns(promise),
+        getSnapshot: sinon.stub().returns(promise)
       };
       scaleSvc = {
         format: sinon.stub().returns({
@@ -202,13 +203,12 @@
   });
 
   describe('_update', () => {
-    let data, func;
+    let data;
     beforeEach(() => {
-      func = promise.then.args[0][0];
       data = [{
         agentId: 'foo-agentId',
         jvmId: 'foo-jvmId',
-        timeStamp: Date.now(),
+        timeStamp: { $numberLong: Date.now().toString() },
         metaspaceMaxCapacity: { $numberLong: '0' },
         metaspaceMinCapacity: { $numberLong: '0' },
         metaspaceCapacity: { $numberLong: (40 * 1024 * 1024).toString() },
@@ -231,7 +231,8 @@
           }
         ]
       }];
-      func(data);
+      ctrl._update();
+      promise.then.yield(data);
     });
 
     it('should update metaspaceSnapshotData', () => {
@@ -239,7 +240,7 @@
         used: 20,
         total: 40
       });
-      scaleSvc.format.should.be.calledTwice();
+      scaleSvc.format.callCount.should.equal(4);
       scaleSvc.format.firstCall.should.be.calledWithMatch(20 * 1024 * 1024);
     });
 
@@ -258,7 +259,7 @@
           ]
         }
       });
-      scaleSvc.format.should.be.calledTwice();
+      scaleSvc.format.callCount.should.equal(4);
       scaleSvc.format.secondCall.should.be.calledWithMatch(30 * 1024 * 1024);
     });
 
@@ -267,7 +268,7 @@
       let space = generation.spaces[0];
       space.capacity = { $numberLong: (100 * 1024 * 1024).toString() };
       space.used = { $numberLong: (50 * 1024 * 1024).toString() };
-      func(data);
+      promise.then.yield(data);
       ctrl.generationSnapshotData.should.deepEqual({
         0: {
           index: 0,
@@ -282,11 +283,52 @@
           ]
         }
       });
-      scaleSvc.format.callCount.should.equal(4);
-      scaleSvc.format.args[0][0].should.deepEqual(20 * 1024 * 1024);
-      scaleSvc.format.args[1][0].should.deepEqual(30 * 1024 * 1024);
-      scaleSvc.format.args[2][0].should.deepEqual(20 * 1024 * 1024);
-      scaleSvc.format.args[3][0].should.deepEqual(50 * 1024 * 1024);
+    });
+
+    it('should do nothing but trim if update is empty', () => {
+      sinon.spy(ctrl, '_trimData');
+      ctrl.metaspaceSnapshotData.should.deepEqual({
+        used: 20,
+        total: 40
+      });
+      ctrl.generationSnapshotData.should.deepEqual({
+        0: {
+          index: 0,
+          name: 'Generation 0',
+          collector: 'Shenandoah',
+          spaces: [
+            {
+              index: 0,
+              used: 30,
+              total: 50
+            }
+          ]
+        }
+      });
+
+      ctrl._trimData.should.not.be.called();
+      ctrl._update();
+      promise.then.yield([]);
+      ctrl._trimData.should.be.called();
+
+      ctrl.metaspaceSnapshotData.should.deepEqual({
+        used: 20,
+        total: 40
+      });
+      ctrl.generationSnapshotData.should.deepEqual({
+        0: {
+          index: 0,
+          name: 'Generation 0',
+          collector: 'Shenandoah',
+          spaces: [
+            {
+              index: 0,
+              used: 30,
+              total: 50
+            }
+          ]
+        }
+      });
     });
   });
 
@@ -323,12 +365,12 @@
   });
 
   describe('dataAgeLimit', () => {
-    it('should cause a data trim on change', () => {
-      sinon.spy(ctrl, '_trimData');
-      ctrl._trimData.should.not.be.called;
+    it('should cause a data clear on change', () => {
+      sinon.spy(ctrl, '_clearData');
+      ctrl._clearData.should.not.be.called;
       ctrl.dataAgeLimit = 10000;
-      ctrl._trimData.should.be.calledOnce();
-      ctrl._trimData.restore();
+      ctrl._clearData.should.be.calledOnce();
+      ctrl._clearData.restore();
     });
 
     it('should reflect changes in getter', () => {
@@ -353,9 +395,9 @@
       });
       promise.then.should.be.calledTwice();
       let prom = promise.then.secondCall.args[0];
-      prom([{
+      prom({
         metaspaceUsed: { $numberLong: '9001' }
-      }]);
+      });
     });
   });
 
@@ -374,7 +416,7 @@
       });
       promise.then.should.be.calledTwice();
       let prom = promise.then.secondCall.args[0];
-      prom([{
+      prom({
         generations: [
           {
             spaces: [
@@ -401,7 +443,7 @@
             ]
           }
         ]
-      }]);
+      });
     });
   });
 
--- a/src/app/components/jvm-info/jvm-memory/jvm-memory.service.js	Wed Oct 11 14:59:09 2017 -0400
+++ b/src/app/components/jvm-info/jvm-memory/jvm-memory.service.js	Wed Oct 11 14:59:09 2017 -0400
@@ -44,6 +44,16 @@
       }
     }).then(res => res.data.response);
   }
+
+  getSnapshot (jvmId) {
+    return this.http.get(urlJoin(this.gatewayUrl, 'jvm-memory', '0.0.3', 'jvms', jvmId), {
+      params: {
+        limit: 1,
+        sort: '-timeStamp'
+      }
+    }).then(res => res.data.response[0]);
+  }
+
 }
 
 export default angular
--- a/src/app/components/jvm-info/jvm-memory/jvm-memory.service.spec.js	Wed Oct 11 14:59:09 2017 -0400
+++ b/src/app/components/jvm-info/jvm-memory/jvm-memory.service.spec.js	Wed Oct 11 14:59:09 2017 -0400
@@ -54,7 +54,7 @@
     should.exist(svc);
   });
 
-  describe('getJvmMemory(jvmId)', () => {
+  describe('getJvmMemory(jvmId, since)', () => {
     it('should resolve mock data', done => {
       let expected = {
         response: [
@@ -75,4 +75,25 @@
     });
   });
 
+  describe('getSnapshot(jvmId)', () => {
+    it('should resolve mock data', done => {
+      let expected = {
+        response: [
+          {
+            metaspace: 100
+          }
+        ]
+      };
+      httpBackend.when('GET', 'http://example.com:1234/jvm-memory/0.0.3/jvms/foo-jvmId?limit=1&sort=-timeStamp')
+        .respond(expected);
+      svc.getSnapshot('foo-jvmId').then(res => {
+        res.should.deepEqual(expected.response[0]);
+        done();
+      });
+      httpBackend.expectGET('http://example.com:1234/jvm-memory/0.0.3/jvms/foo-jvmId?limit=1&sort=-timeStamp');
+      httpBackend.flush();
+      scope.$apply();
+    });
+  });
+
 });