From 86677005daa57ac43aea00452468e596d6c4a3bf Mon Sep 17 00:00:00 2001 From: Pasi Niemi Date: Mon, 13 Jul 2015 15:58:35 +0300 Subject: [PATCH] Fix nv graph updating for time series charts --- .../scripts/modules/graphs/access-graph.js | 43 ++------ .../scripts/modules/graphs/childcpu-graph.js | 37 ++----- .../scripts/modules/graphs/heap-graph.js | 47 +++----- .../webapp/scripts/services/utils.js | 100 +++++++++++++++--- 4 files changed, 120 insertions(+), 107 deletions(-) diff --git a/willow-servers/src/main/resources/webapp/scripts/modules/graphs/access-graph.js b/willow-servers/src/main/resources/webapp/scripts/modules/graphs/access-graph.js index 9898e441..d5c78933 100644 --- a/willow-servers/src/main/resources/webapp/scripts/modules/graphs/access-graph.js +++ b/willow-servers/src/main/resources/webapp/scripts/modules/graphs/access-graph.js @@ -7,6 +7,7 @@ Box.Application.addModule('access-graph', function(context) { { legend: "req/h", step: hour }, { legend: "req/2h", step: twoHours } ]; var legend = "req/min"; var moduleElement, moduleConf, isTimescaleLongerThanDay, detailsStart, detailsStop, detailsStep = 60000; + var chart, chartData; function xTicks(d) { var date = new Date(d); @@ -24,9 +25,16 @@ Box.Application.addModule('access-graph', function(context) { } } + var onmessage = function(parsedData) { + if (!parsedData.length || !parsedData[0].values.length) { return; } + utils.mergePoints(chartData, parsedData); + chart.update(); + }; + function createAccessGraph(data) { + chartData = data; nv.addGraph(function() { - var chart = nv.models.multiBarChart() + chart = nv.models.multiBarChart() .margin({top: 30, right: 20, bottom: 50, left: 75}) .showControls(false) .stacked(true) @@ -43,41 +51,12 @@ Box.Application.addModule('access-graph', function(context) { .attr("y", 35) .attr("text-anchor", "left") .text(legend); - - var latestTimestamp = data[0].values[data[0].values.length - 1].x, // keeps track of latest processed bit of data - socket = utils.configureSocket({ + utils.configureSocket({ start: detailsStart, stop: detailsStop, step: detailsStep, metricKey: moduleConf.chart.type - }); - - socket.onmessage = function(event) { - var parsedData = JSON.parse(event.data), - parsedDataTimestamp; - - // no data or no values = there's nothing to update - if (!parsedData.length || !parsedData[0].values.length) { return; } - - // we can assume all values in a set have same timestamp - parsedDataTimestamp = parsedData[0].values[0].x; - // make sure we're showing new data only - if (latestTimestamp >= parsedDataTimestamp) { return; } - // update the value for next cycle - latestTimestamp = parsedData[0].values[0].x; - - // feed new data into chart data - parsedData.forEach(function(set, setIndex) { - set.values.forEach(function(value) { - data[setIndex].values.push(value); - data[setIndex].values.shift(); - }); - }); - - // update the chart once all data is in place - chart.update(); - }; - + }, onmessage); return chart; }); } diff --git a/willow-servers/src/main/resources/webapp/scripts/modules/graphs/childcpu-graph.js b/willow-servers/src/main/resources/webapp/scripts/modules/graphs/childcpu-graph.js index 88c4feb3..7e6576ae 100644 --- a/willow-servers/src/main/resources/webapp/scripts/modules/graphs/childcpu-graph.js +++ b/willow-servers/src/main/resources/webapp/scripts/modules/graphs/childcpu-graph.js @@ -4,6 +4,13 @@ Box.Application.addModule('childcpu-graph', function(context) { var nv, d3, host, windowSvc, metrics, store, utils; var moduleElement, moduleConf, detailsStart, detailsStop, detailsStep, isTimescaleLongerThanDay; + var chart, chartData; + + var onmessage = function(parsedData) { + if (!parsedData.length || !parsedData[0].values.length) { return; } + utils.mergePoints(chartData, parsedData); + chart.update(); + }; function xTicks(d) { var date = new Date(d); @@ -16,8 +23,9 @@ Box.Application.addModule('childcpu-graph', function(context) { } function createChildCpuGraph(data) { + chartData = data; nv.addGraph(function() { - var chart = nv.models.lineChart(); + chart = nv.models.lineChart(); chart.margin({right: 25}); chart.xAxis.tickFormat(xTicks); chart.yAxis.tickFormat(d3.format('%')); @@ -26,35 +34,12 @@ Box.Application.addModule('childcpu-graph', function(context) { .transition().duration(500) .call(chart); - var latestTimestamp = data[0].values[data[0].values.length - 1].x, - socket = utils.configureSocket({ + utils.configureSocket({ start: detailsStart, stop: detailsStop, step: detailsStep, metricKey: moduleConf.chart.type - }); - - socket.onmessage = function(event) { - var parsedData = JSON.parse(event.data), - parsedDataTimestamp; - - // no data or no values = there's nothing to update - if (!parsedData.length || !parsedData[0].values.length) { return; } - - // we can assume all values in a set have same timestamp - parsedDataTimestamp = parsedData[0].values[0].x; - - if (latestTimestamp >= parsedDataTimestamp) { return; } - // update the value for next cycle - latestTimestamp = parsedData[0].values[0].x; - - parsedData[0].values.forEach(function(value) { - data[0].values.push(value); - data[0].values.shift(); - }); - // update the chart once all data is in place - chart.update(); - }; + }, onmessage); return chart; }); diff --git a/willow-servers/src/main/resources/webapp/scripts/modules/graphs/heap-graph.js b/willow-servers/src/main/resources/webapp/scripts/modules/graphs/heap-graph.js index 3ea6ba63..06d806e9 100644 --- a/willow-servers/src/main/resources/webapp/scripts/modules/graphs/heap-graph.js +++ b/willow-servers/src/main/resources/webapp/scripts/modules/graphs/heap-graph.js @@ -4,6 +4,13 @@ Box.Application.addModule('heap-graph', function(context) { var nv, d3, host, windowSvc, metrics, store, utils; var moduleElement, moduleConf, detailsStart, detailsStop, detailsStep, isTimescaleLongerThanDay; + var chart, chartData; + + var onmessage = function(parsedData) { + if (!parsedData.length || !parsedData[0].values.length) { return; } + utils.mergePoints(chartData, parsedData); + chart.update(); + }; function xTicks(d) { var date = new Date(d); @@ -16,8 +23,9 @@ Box.Application.addModule('heap-graph', function(context) { } function createHeapGraph(data) { + chartData = data; nv.addGraph(function() { - var chart = nv.models.lineChart(); + chart = nv.models.lineChart(); chart.margin({right: 25}); chart.xAxis.tickFormat(xTicks); chart.yAxis.tickFormat(d3.format(',.2s')); @@ -25,37 +33,12 @@ Box.Application.addModule('heap-graph', function(context) { .datum(data) .transition().duration(500) .call(chart); - - var latestTimestamp = data[0].values[data[0].values.length - 1].x, - socket = utils.configureSocket({ - start: detailsStart, - stop: detailsStop, - step: detailsStep, - metricKey: moduleConf.chart.type - }); - - socket.onmessage = function(event) { - var parsedData = JSON.parse(event.data), - parsedDataTimestamp; - - // no data or no values = there's nothing to update - if (!parsedData.length || !parsedData[0].values.length) { return; } - - // we can assume all values in a set have same timestamp - parsedDataTimestamp = parsedData[0].values[0].x; - - if (latestTimestamp >= parsedDataTimestamp) { return; } - // update the value for next cycle - latestTimestamp = parsedData[0].values[0].x; - - parsedData[0].values.forEach(function(value) { - data[0].values.push(value); - data[0].values.shift(); - }); - // update the chart once all data is in place - chart.update(); - }; - + utils.configureSocket({ + start: detailsStart, + stop: detailsStop, + step: detailsStep, + metricKey: moduleConf.chart.type + }, onmessage); return chart; }); } diff --git a/willow-servers/src/main/resources/webapp/scripts/services/utils.js b/willow-servers/src/main/resources/webapp/scripts/services/utils.js index 0550dfb4..0164a5bc 100644 --- a/willow-servers/src/main/resources/webapp/scripts/services/utils.js +++ b/willow-servers/src/main/resources/webapp/scripts/services/utils.js @@ -2,7 +2,51 @@ Box.Application.addService('utils', function(application) { 'use strict'; var $ = application.getGlobal('jQuery'); var d3 = application.getGlobal('d3'); + var socket; + var messageHandlers = {}; + var pollConfs = {}; + var registeredConfs = []; + var onmessage = function(event) { + var eventData = JSON.parse(event.data); + if (messageHandlers[eventData.id]) { + messageHandlers[eventData.id](eventData.data); + } + }; + var registerPending = function() { + if (socket && socket.readyState === 1) { + for (var metricId in pollConfs) { + if (registeredConfs.indexOf(metricId) === -1) { + socket.send( JSON.stringify(pollConfs[metricId]) ); + registeredConfs.push(metricId); + } + } + } + }; + var mergePointArrays = function(chartPoints, parsedPoints) { + if (!chartPoints || !parsedPoints || parsedPoints.length === 0) { + return chartPoints; + } + var halfStep = (chartPoints[1].x - chartPoints[0].x) / 2; + for (var i=0; i= chartPoints[chartPoints.length - 1].x + halfStep) { + chartPoints.push(nextPoint); + chartPoints.shift(); + } else { + for (var j=chartPoints.length - 1; j>-1; j--) { + if (nextPoint.x > chartPoints[j].x - halfStep && + nextPoint.x <= chartPoints[j].x + halfStep) { + chartPoints[j].y = nextPoint.y; + chartPoints[j].y0 = nextPoint.y; + chartPoints[j].y1 = nextPoint.y; + continue; + } + } + } + } + return chartPoints; + }; return { debouncer: function(func , timeout) { var timeoutID , tmOut = timeout || 200; @@ -118,26 +162,48 @@ Box.Application.addService('utils', function(application) { } }, - configureSocket: function(opts) { - var loc = window.location, - ws_uri = (loc.protocol === 'https:' ? 'wss://' : 'ws://') + loc.host + "/poll/", - socket = new WebSocket(ws_uri); - - socket.onopen = function(e) { - var pollConf = { - metricKey: "/" + opts.metricKey, - start: opts.start, - stop: opts.stop, - step: opts.step, - minSteps: 1 - }; - - socket.send( JSON.stringify(pollConf) ); + configureSocket: function(opts, onmsg) { + if (!socket) { + var loc = window.location; + var ctx = "/"; + var ctxEnd = loc.pathname.lastIndexOf("/"); + if (ctxEnd > 0) { + if (loc.pathname.indexOf("/") === 0) { + ctx = ""; + } + ctx += loc.pathname.substring(0, contextEnd) + "/"; + } + var ws_uri = (loc.protocol === 'https:' ? 'wss://' : 'ws://') + loc.host + ctx +"poll/"; + socket = new WebSocket(ws_uri); + socket.onopen = registerPending; + } + var metricId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); + messageHandlers[metricId] = onmsg; + var pollConf = { + id: metricId, + metricKey: "/" + opts.metricKey, + start: opts.start, + stop: opts.stop, + step: opts.step, + minSteps: 10 }; - + pollConfs[metricId] = pollConf; + socket.onmessage = onmessage; + registerPending(); return socket; }, - + mergePoints: function(chartData, parsedData) { + parsedData.forEach(function(series) { + for (var i=0; i