# HG changeset patch # User Andrew Azores # Date 1508874696 14400 # Node ID 87a683af97e89cdfaed0d301707e2bdec9fc83b1 # Parent 9f3ee8157ac691a795d91698b3b84b7857dbe0ff Use jvm-gc /delta Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-October/025439.html diff -r 9f3ee8157ac6 -r 87a683af97e8 mock-api/endpoints/jvm-gc.endpoint.js --- a/mock-api/endpoints/jvm-gc.endpoint.js Tue Oct 24 15:50:49 2017 -0400 +++ b/mock-api/endpoints/jvm-gc.endpoint.js Tue Oct 24 15:51:36 2017 -0400 @@ -1,39 +1,47 @@ function jvmGc (server) { var _ = require('lodash'); server.init('jvmGc'); - var accumulatedMicros = 1000; + server.app.get('/jvm-gc/0.0.3/jvms/:jvmId', function (req, res) { server.logRequest('jvm-gc', req); var jvmId = req.params.jvmId; - var limit = req.query.limit || 1; + var limit = req.query.limit; + if (limit == 1) { + var event = Math.random() > 0.8; + var micros = event ? Math.random() * 1000 : 0; + res.setHeader('Content-Type', 'application/json'); + res.send(JSON.stringify({ + response: [{ + jvmId: jvmId, + collectorName: 'foo-collector', + timeStamp: { $numberLong: Date.now().toString() }, + wallTimeInMicros: { $numberLong: micros } + }] + })); + } + }); - var response = []; - for (var i = 0; i < limit; i++) { - if (Math.random() > 0.9) { - accumulatedMicros = _.floor(accumulatedMicros * 1.1); - } - if (accumulatedMicros > 1000000) { - // clients probably won't like this "rollover", but we don't want to - // grow unbounded either - accumulatedMicros = 1000; - } - var data = { - agentId: 'foo-agentId', - jvmId: jvmId, - timeStamp: { $numberLong: (Date.now() - i).toString() }, - collectorName: 'fooCollector', - runCount: { $numberLong: '0' }, - wallTimeInMicros: { $numberLong: accumulatedMicros.toString() } - }; - response.push(data); - } + server.app.get('/jvm-gc/0.0.3/delta/:jvmId', function (req, res) { + server.logRequest('jvm-gc', req); + + var jvmId = req.params.jvmId; + + var event = Math.random() > 0.8; + var micros = event ? Math.random() * 1000 : 0; + + var data = { + jvmId: jvmId, + collectorName: 'foo-collector', + timeStamp: { $numberLong: Date.now().toString() }, + wallTimeDelta: { $numberLong: micros } + }; res.setHeader('Content-Type', 'application/json'); res.send(JSON.stringify( { - response: response + response: [data] } )); }); diff -r 9f3ee8157ac6 -r 87a683af97e8 src/app/components/jvm-info/jvm-gc/jvm-gc.controller.js --- a/src/app/components/jvm-info/jvm-gc/jvm-gc.controller.js Tue Oct 24 15:50:49 2017 -0400 +++ b/src/app/components/jvm-info/jvm-gc/jvm-gc.controller.js Tue Oct 24 15:51:36 2017 -0400 @@ -145,21 +145,22 @@ } _trimData () { + let now = Date.now(); + let oldestLimit = now - parseInt(this.dataAgeLimit); + for (let entry of this._collectorData) { let collector = entry[0]; let samples = entry[1]; - let now = Date.now(); - let oldestLimit = now - parseInt(this.dataAgeLimit); - - while (true) { - let oldest = samples[0]; - if (angular.isDefined(oldest) && oldest.timestamp < oldestLimit) { - samples.shift(); - } else { + let spliceCount = 0; + for (; spliceCount < samples.length; spliceCount++) { + let sample = samples[spliceCount]; + if (sample.timestamp >= oldestLimit) { break; } } + samples.splice(0, spliceCount); + this._collectorData.set(collector, samples); } } @@ -173,11 +174,10 @@ yData: [collector] }; - for (let i = 1; i < samples.length; i++) { + for (let i = 0; i < samples.length; i++) { let sample = samples[i]; - let lastSample = samples[i - 1]; data.xData.push(sample.timestamp); - data.yData.push(sample.micros - lastSample.micros); + data.yData.push(sample.micros); } this.chartData[collector] = data; @@ -185,56 +185,27 @@ } _update () { - this._svc.getJvmGcData(this.jvmId) - .then(resp => this._processData(resp), angular.noop); + this._svc.getJvmGcData(this.jvmId).then(resp => this._processData(resp)); } _processData (resp) { - let seenCollectors = new Set(); - let latestStamp = 0; - for (let i = resp.data.response.length - 1; i >= 0; i--) { - let data = resp.data.response[i]; - let collectorName = data.collectorName; - let timestamp = this._metricToNumber(data.timeStamp); - let micros = this._metricToNumber(data.wallTimeInMicros); + resp.forEach(update => { + let timestamp = this._metricToNumber(update.timeStamp); + let collectorName = update.collectorName; + let micros = this._metricToNumber(update.wallTimeDelta); - seenCollectors.add(collectorName); this._makeConfig(collectorName, micros); if (!this._collectorData.has(collectorName)) { this._collectorData.set(collectorName, []); } - if (timestamp > latestStamp) { - latestStamp = timestamp; - } - let collectorData = this._collectorData.get(collectorName); - if (collectorData.length === 0) { - collectorData.push({ - timestamp: timestamp, - micros: micros - }); - } - let last = collectorData[collectorData.length - 1]; - if (timestamp > last.timestamp) { - collectorData.push({ - timestamp: timestamp, - micros: micros - }); - } - } - - for (let entry of this._collectorData) { - let collectorName = entry[0]; - let collectorData = entry[1]; - if (!seenCollectors.has(collectorName)) { - collectorData.push({ - timestamp: latestStamp, - micros: collectorData[collectorData.length - 1].micros - }); - } - } + collectorData.push({ + timestamp: timestamp, + micros: micros + }); + }); this._trimData(); @@ -247,8 +218,8 @@ multichartFn (collector) { return new Promise(resolve => { - this._svc.getJvmGcData(this.jvmId, 1, collector).then(resp => { - resolve(this._metricToNumber(resp.data.response[0].wallTimeInMicros)); + this._svc.getSnapshot(this.jvmId, collector).then(resp => { + resolve(this._metricToNumber(resp)); }); }); } diff -r 9f3ee8157ac6 -r 87a683af97e8 src/app/components/jvm-info/jvm-gc/jvm-gc.controller.spec.js --- a/src/app/components/jvm-info/jvm-gc/jvm-gc.controller.spec.js Tue Oct 24 15:50:49 2017 -0400 +++ b/src/app/components/jvm-info/jvm-gc/jvm-gc.controller.spec.js Tue Oct 24 15:51:36 2017 -0400 @@ -31,7 +31,7 @@ describe('JvmGcController', () => { - let interval, dateFilterStub, dateFormatSpy, svc, promise, ctrl, translate, sanitizeService; + let interval, dateFilterStub, dateFormatSpy, svc, promise, snapshotPromise, ctrl, translate, sanitizeService; beforeEach(() => { angular.mock.module(filtersModule); angular.mock.module(servicesModule); @@ -51,7 +51,11 @@ interval.cancel = sinon.spy(); promise = { then: sinon.spy() }; - svc = { getJvmGcData: sinon.stub().returns(promise) }; + snapshotPromise = { then: sinon.stub() }; + svc = { + getJvmGcData: sinon.stub().returns(promise), + getSnapshot: sinon.stub().returns(snapshotPromise) + }; sanitizeService = { sanitize: sinon.spy() }; @@ -70,7 +74,8 @@ DATE_FORMAT: dateFormatSpy, jvmGcService: svc, sanitizeService: sanitizeService, - $translate: translate + $translate: translate, + metricToNumberFilter: m => parseInt(m.$numberLong) }); ctrl.$onInit(); }); @@ -91,25 +96,18 @@ svc.getJvmGcData.should.be.calledTwice(); promise.then.should.be.calledTwice(); - promise.then.secondCall.should.be.calledWith(sinon.match.func, sinon.match.func); + promise.then.secondCall.should.be.calledWith(sinon.match.func); ctrl.collectors.should.deepEqual([]); let successHandler = promise.then.args[1][0]; - successHandler({ - data: { - response: [ - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: '100' }, - wallTimeInMicros: { $numberLong: '50' } - } - ] + successHandler([ + { + collectorName: 'fooCollector', + timeStamp: { $numberLong: '100' }, + wallTimeInMicros: { $numberLong: '5050' }, + wallTimeDelta: { $numberLong: '50' } } - }); + ]); ctrl.collectors.should.deepEqual(['fooCollector']); - - let errorHandler = promise.then.args[1][1]; - errorHandler.should.equal(angular.noop); - errorHandler(); }); it('should reset interval on refreshRate change', () => { @@ -260,156 +258,69 @@ ctrl.chartData.should.deepEqual({ fooCollector: { - xData: ['timestamps', 101], - yData: ['fooCollector', 10] + xData: ['timestamps', 100, 101], + yData: ['fooCollector', 50, 60] } }); }); }); describe('_processData', () => { - it('should process singleton service results', () => { - ctrl.collectors.should.deepEqual([]); - ctrl.chartConfigs.should.deepEqual({}); - ctrl._collectorData.has('fooCollector').should.be.false(); - let timestamp = Date.now().toString(); - ctrl._processData({ - data: { - response: [ - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: timestamp }, - wallTimeInMicros: { $numberLong: '50' } - } - ] - } - }); - ctrl._collectorData.has('fooCollector').should.be.true(); - ctrl._collectorData.get('fooCollector').should.be.an.Array(); - ctrl._collectorData.get('fooCollector').length.should.equal(1); - ctrl._collectorData.get('fooCollector')[0].should.deepEqual({ timestamp: parseInt(timestamp), micros: 50 }); - }); - it('should process multiple service results', () => { ctrl.collectors.should.deepEqual([]); ctrl.chartConfigs.should.deepEqual({}); ctrl._collectorData.has('fooCollector').should.be.false(); - let timestampA = Date.now().toString(); - let timestampB = (Date.now() - 10).toString(); - ctrl._processData({ - data: { - response: [ - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: timestampA }, - wallTimeInMicros: { $numberLong: '50' } - }, - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: timestampB }, - wallTimeInMicros: { $numberLong: '25' } - } - ] + let timestampA = Date.now(); + let timestampB = timestampA - 10; + ctrl._processData([ + { + collectorName: 'fooCollector', + timeStamp: { $numberLong: timestampA.toString() }, + wallTimeDelta: { $numberLong: '50' }, + wallTimeInMicros: { $numberLong: '5050' } + }, + { + collectorName: 'fooCollector', + timeStamp: { $numberLong: timestampB.toString() }, + wallTimeDelta: { $numberLong: '25' }, + wallTimeInMicros: { $numberLong: '2525' } } - }); + ]); ctrl._collectorData.has('fooCollector').should.be.true(); - ctrl._collectorData.get('fooCollector').should.be.an.Array(); - ctrl._collectorData.get('fooCollector').length.should.equal(2); - ctrl._collectorData.get('fooCollector')[0].should.deepEqual({ timestamp: parseInt(timestampB), micros: 25 }); - ctrl._collectorData.get('fooCollector')[1].should.deepEqual({ timestamp: parseInt(timestampA), micros: 50 }); + let result = ctrl._collectorData.get('fooCollector'); + result.should.be.an.Array(); + result.should.deepEqual([ + { timestamp: timestampA, micros: 50 }, + { timestamp: timestampB, micros: 25 } + ]); }); it('should append new data', () => { - let timestampA = Date.now().toString(); - let timestampB = (Date.now() + 5000).toString(); - ctrl._processData({ - data: { - response: [ - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: timestampA }, - wallTimeInMicros: { $numberLong: '50' } - } - ] - } - }); - ctrl._processData({ - data: { - response: [ - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: timestampB }, - wallTimeInMicros: { $numberLong: '100' } - } - ] - } - }); - ctrl._collectorData.has('fooCollector').should.be.true(); - ctrl._collectorData.get('fooCollector').should.be.an.Array(); - ctrl._collectorData.get('fooCollector').length.should.equal(2); - ctrl._collectorData.get('fooCollector')[0].should.deepEqual({ timestamp: parseInt(timestampA), micros: 50 }); - ctrl._collectorData.get('fooCollector')[1].should.deepEqual({ timestamp: parseInt(timestampB), micros: 100 }); - }); - - it('should append a sample with duplicate elapsed time if no sample received for a collector', () => { - let timestampA = Date.now().toString(); - let timestampB = (Date.now() + 10).toString(); - ctrl._processData({ - data: { - response: [ - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: timestampA }, - wallTimeInMicros: { $numberLong: '100' } - } - ] + let timestampA = Date.now(); + let timestampB = timestampA + 5000; + ctrl._processData([ + { + collectorName: 'fooCollector', + timeStamp: { $numberLong: timestampA.toString() }, + wallTimeDelta: { $numberLong: '50' }, + wallTimeInMicros: { $numberLong: '5050' } } - }); - ctrl._processData({ - data: { - response: [ - { - collectorName: 'barCollector', - timeStamp: { $numberLong: timestampB }, - wallTimeInMicros: { $numberLong: '200' } - } - ] + ]); + ctrl._processData([ + { + collectorName: 'fooCollector', + timeStamp: { $numberLong: timestampB.toString() }, + wallTimeDelta: { $numberLong: '100' }, + wallTimeInMicros: { $numberLong: '10100' } } - }); - ctrl._collectorData.get('fooCollector').length.should.equal(2); - ctrl._collectorData.get('fooCollector')[0].should.deepEqual({ timestamp: parseInt(timestampA), micros: 100 }); - ctrl._collectorData.get('fooCollector')[1].should.deepEqual({ timestamp: parseInt(timestampB), micros: 100 }); - - ctrl._collectorData.get('barCollector').length.should.equal(1); - ctrl._collectorData.get('barCollector')[0].should.deepEqual({ timestamp: parseInt(timestampB), micros: 200 }); - }); - - it('should ignore duplicate timestamps', () => { - let timestamp = Date.now().toString(); - ctrl._processData({ - data: { - response: [ - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: timestamp }, - wallTimeInMicros: { $numberLong: 200 } - }, - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: timestamp }, - wallTimeInMicros: { $numberLong: 100 } - }, - { - collectorName: 'fooCollector', - timeStamp: { $numberLong: timestamp }, - wallTimeInMicros: { $numberLong: 100 } - } - ] - } - }); - ctrl._collectorData.get('fooCollector').length.should.equal(1); - // note: response is processed in reverse order, so 100 is seen first - ctrl._collectorData.get('fooCollector')[0].should.deepEqual({ timestamp: parseInt(timestamp), micros: 100 }); + ]); + ctrl._collectorData.has('fooCollector').should.be.true(); + let result = ctrl._collectorData.get('fooCollector'); + result.should.be.an.Array(); + result.should.deepEqual([ + { timestamp: timestampA, micros: 50 }, + { timestamp: timestampB, micros: 100 } + ]); }); }); @@ -442,23 +353,16 @@ }); it('should resolve jvm-gc stat', done => { - promise.then.should.be.calledOnce(); - let res = ctrl.multichartFn(); - res.then(v => { + svc.getSnapshot.should.not.be.called(); + snapshotPromise.then.should.not.be.called(); + ctrl.multichartFn('foo-collector').then(v => { v.should.equal(400); done(); }); - promise.then.should.be.calledTwice(); - let prom = promise.then.secondCall.args[0]; - prom({ - data: { - response: [ - { - wallTimeInMicros: { $numberLong: '400' } - } - ] - } - }); + svc.getSnapshot.should.be.calledOnce(); + svc.getSnapshot.should.be.calledWith('foo-jvmId', 'foo-collector'); + snapshotPromise.then.should.be.calledOnce(); + snapshotPromise.then.yield({ $numberLong: '400' }); }); }); diff -r 9f3ee8157ac6 -r 87a683af97e8 src/app/components/jvm-info/jvm-gc/jvm-gc.service.js --- a/src/app/components/jvm-info/jvm-gc/jvm-gc.service.js Tue Oct 24 15:50:49 2017 -0400 +++ b/src/app/components/jvm-info/jvm-gc/jvm-gc.service.js Tue Oct 24 15:51:36 2017 -0400 @@ -35,15 +35,21 @@ this.gatewayUrl = gatewayUrl; } - getJvmGcData (jvmId, limit = 1, collectorName) { + getJvmGcData (jvmId) { + return this.http.get(urlJoin(this.gatewayUrl, 'jvm-gc', '0.0.3', 'delta', jvmId)).then(resp => { + return resp.data.response; + }); + } + + getSnapshot (jvmId, collectorName) { let params = { - limit: limit, + limit: 1, sort: '-timeStamp', + query: 'collectorName==' + collectorName }; - if (collectorName) { - params.query = 'collectorName==' + collectorName; - } - return this.http.get(urlJoin(this.gatewayUrl, 'jvm-gc', '0.0.3', 'jvms', jvmId), { params: params }); + return this.http.get(urlJoin(this.gatewayUrl, 'jvm-gc', '0.0.3', 'jvms', jvmId), { params: params }).then(resp => { + return resp.data.response[0].wallTimeInMicros; + }); } } diff -r 9f3ee8157ac6 -r 87a683af97e8 src/app/components/jvm-info/jvm-gc/jvm-gc.service.spec.js --- a/src/app/components/jvm-info/jvm-gc/jvm-gc.service.spec.js Tue Oct 24 15:50:49 2017 -0400 +++ b/src/app/components/jvm-info/jvm-gc/jvm-gc.service.spec.js Tue Oct 24 15:51:36 2017 -0400 @@ -54,48 +54,51 @@ should.exist(svc); }); - describe('getJvmGcdata(jvmId, limit, collectorName)', () => { + describe('getJvmGcdata (jvmId)', () => { it('should resolve mock data', done => { let expected = { - metaspace: 100 + response: [ + { + jvmId: 'foo-jvmId', + collectorName: 'foo-collector', + timeStamp: { $numberLong: '1234' }, + wallTimeDelta: { $numberLong: '8888' }, + wallTimeInMicros: { $numberLong: '9999' } + } + ] }; - httpBackend.when('GET', 'http://example.com:1234/jvm-gc/0.0.3/jvms/foo-jvmId?limit=1&sort=-timeStamp') + httpBackend.when('GET', 'http://example.com:1234/jvm-gc/0.0.3/delta/foo-jvmId') .respond(expected); svc.getJvmGcData('foo-jvmId').then(res => { - res.data.should.deepEqual(expected); + res.should.deepEqual(expected.response); done(); }); - httpBackend.expectGET('http://example.com:1234/jvm-gc/0.0.3/jvms/foo-jvmId?limit=1&sort=-timeStamp'); + httpBackend.expectGET('http://example.com:1234/jvm-gc/0.0.3/delta/foo-jvmId'); httpBackend.flush(); scope.$apply(); }); + }); - it('should allow overriding limit', done => { + describe('getSnapshot (jvmId, collectorName)', () => { + it('should resolve mock data', done => { let expected = { - metaspace: 100 + response: [ + { + jvmId: 'foo-jvmId', + collectorName: 'foo-collector', + timeStamp: { $numberLong: '1234' }, + wallTimeDelta: { $numberLong: '8888' }, + wallTimeInMicros: { $numberLong: '9999' } + } + ] }; - httpBackend.when('GET', 'http://example.com:1234/jvm-gc/0.0.3/jvms/foo-jvmId?limit=5&sort=-timeStamp') + httpBackend.when('GET', 'http://example.com:1234/jvm-gc/0.0.3/jvms/foo-jvmId?limit=1&query=collectorName%3D%3Dfoo-collector&sort=-timeStamp') .respond(expected); - svc.getJvmGcData('foo-jvmId', 5).then(res => { - res.data.should.deepEqual(expected); + svc.getSnapshot('foo-jvmId', 'foo-collector').then(res => { + res.should.deepEqual(expected.response[0].wallTimeInMicros); done(); }); - httpBackend.expectGET('http://example.com:1234/jvm-gc/0.0.3/jvms/foo-jvmId?limit=5&sort=-timeStamp'); - httpBackend.flush(); - scope.$apply(); - }); - - it('should allow specifying collectorName', done => { - let expected = { - metaspace: 100 - }; - httpBackend.when('GET', 'http://example.com:1234/jvm-gc/0.0.3/jvms/foo-jvmId?limit=5&query=collectorName%3D%3DfooCollector&sort=-timeStamp') - .respond(expected); - svc.getJvmGcData('foo-jvmId', 5, 'fooCollector').then(res => { - res.data.should.deepEqual(expected); - done(); - }); - httpBackend.expectGET('http://example.com:1234/jvm-gc/0.0.3/jvms/foo-jvmId?limit=5&query=collectorName%3D%3DfooCollector&sort=-timeStamp'); + httpBackend.expectGET('http://example.com:1234/jvm-gc/0.0.3/jvms/foo-jvmId?limit=1&query=collectorName%3D%3Dfoo-collector&sort=-timeStamp'); httpBackend.flush(); scope.$apply(); });