# HG changeset patch # User Andrew Azores # Date 1507748349 14400 # Node ID 4023b79e4d58191aa77749392780d1ba5fb6b471 # Parent a0ae36ed4a9a74a7629a6a12f0e9e809f0064be7 Load historical jvm-memory data at controller init Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-October/025371.html diff -r a0ae36ed4a9a -r 4023b79e4d58 mock-api/endpoints/jvm-memory.endpoint.js --- 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; diff -r a0ae36ed4a9a -r 4023b79e4d58 src/app/components/jvm-info/jvm-memory/jvm-memory.controller.js --- 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); diff -r a0ae36ed4a9a -r 4023b79e4d58 src/app/components/jvm-info/jvm-memory/jvm-memory.controller.spec.js --- 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 @@ ] } ] - }]); + }); }); }); diff -r a0ae36ed4a9a -r 4023b79e4d58 src/app/components/jvm-info/jvm-memory/jvm-memory.service.js --- 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 diff -r a0ae36ed4a9a -r 4023b79e4d58 src/app/components/jvm-info/jvm-memory/jvm-memory.service.spec.js --- 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(); + }); + }); + });