Browse Source

HUE-8649 [jb] Add support for appending data in the performance graph

Johan Ahlen 7 years ago
parent
commit
cfb266b9ca
1 changed files with 263 additions and 116 deletions
  1. 263 116
      desktop/core/src/desktop/templates/ko_components/ko_performance_graph.mako

+ 263 - 116
desktop/core/src/desktop/templates/ko_components/ko_performance_graph.mako

@@ -21,15 +21,6 @@ from django.utils.translation import ugettext as _
 
 
 <%def name="performanceGraph()">
 <%def name="performanceGraph()">
 
 
-  <style>
-
-    ## TODO: proper clip in d3
-    .line {
-      clip-path: url(#clip);
-    }
-
-  </style>
-
   <script type="text/html" id="performance-graph-stats">
   <script type="text/html" id="performance-graph-stats">
     <div style="width: 70px; display:inline-block" data-bind="style: { color: color }">${ _("Min") } <span style="font-weight: 300;" data-bind="text: min"></span></div>
     <div style="width: 70px; display:inline-block" data-bind="style: { color: color }">${ _("Min") } <span style="font-weight: 300;" data-bind="text: min"></span></div>
     <div style="width: 70px; display:inline-block" data-bind="style: { color: color }">${ _("Max") } <span style="font-weight: 300;" data-bind="text: max"></span></div>
     <div style="width: 70px; display:inline-block" data-bind="style: { color: color }">${ _("Max") } <span style="font-weight: 300;" data-bind="text: max"></span></div>
@@ -53,29 +44,34 @@ from django.utils.translation import ugettext as _
 
 
 
 
     (function () {
     (function () {
-      var generateFakeData = function () {
-        var entries = 100;
-        var timeDiff = 300000; // 5 minutes
-        var startTime = Date.now() - entries * timeDiff;
-        var result = [['time', 'cpu', 'memory']];
-
-        var lastCpuVal = 0;
-        var lastMemVal = 0;
-        for (var i = 0; i < 100; i++) {
-          lastCpuVal = Math.round(Math.max(Math.min(lastCpuVal + (Math.random() - 0.5) * 20, 100), 0));
-          lastMemVal = Math.round(Math.max(Math.min(lastMemVal + (Math.random() - 0.5) * 20, 100), 0)); // Percentage of total mem
-          result.push([startTime + timeDiff * i, lastCpuVal, lastMemVal]);
+
+      var addFakeMeasurement = function (data, time) {
+        var lastCpuVal = data.length ? data[data.length - 1][1] : 0;
+        var lastMemVal = data.length ? data[data.length - 1][2] : 0;
+        var lastQueryCount = data.length ? data[data.length - 1][3] : 0;
+        var lastIO = data.length ? data[data.length - 1][3] : 0;
+
+        var newQueryCount = Math.round(Math.max(Math.min(lastQueryCount + (Math.random() - 0.5) * 15, 25), 0));
+        var diff = newQueryCount - lastQueryCount;
+        if (diff > 0) {
+          lastCpuVal = Math.round(Math.max(Math.min(lastCpuVal + Math.random() * 20, 100), 0));
+          lastMemVal = Math.round(Math.max(Math.min(lastMemVal + Math.random() * 20, 100), 0));
+          lastIO = Math.round(Math.max(Math.min(lastIO + Math.random() * 20, 100), 0));
+        } else {
+          lastCpuVal = Math.round(Math.max(Math.min(lastCpuVal - Math.random() * 20, 100), 0));
+          lastMemVal = Math.round(Math.max(Math.min(lastMemVal - Math.random() * 20, 100), 0));
+          lastIO = Math.round(Math.max(Math.min(lastIO - Math.random() * 20, 100), 0));
         }
         }
-        return result;
+        data.push([time, lastCpuVal, lastMemVal, lastIO, lastQueryCount]);
       };
       };
 
 
-      var AVAILABLE_TYPES = [{
-        label: '${ _("CPU") }',
-        color: '#29A7DE'
-      }, {
-        label: '${ _("Memory") }',
-        color: '#5D8A8A'
-      }];
+      var generateFakeData = function (data, timeOffset, points) {
+        var startTime = Date.now() - timeOffset;
+
+        for (var i = 0; i < points; i++) {
+          addFakeMeasurement(data, startTime + i * timeOffset/points )
+        }
+      };
 
 
       function bytesToSize(bytes) {
       function bytesToSize(bytes) {
         var sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
         var sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
@@ -84,7 +80,7 @@ from django.utils.translation import ugettext as _
         }
         }
         var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
         var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
         return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
         return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
-      };
+      }
 
 
       /**
       /**
        * @param {object} params
        * @param {object} params
@@ -98,29 +94,51 @@ from django.utils.translation import ugettext as _
         self.graphHeight = 500;
         self.graphHeight = 500;
         self.graphWidth = 750;
         self.graphWidth = 750;
 
 
-        self.cpuStats = ko.observable({ average: '-', min: '-', max: '-', color: AVAILABLE_TYPES[0].color });
-        self.memoryStats = ko.observable({ average: '-', min: '-', max: '-', color: AVAILABLE_TYPES[1].color });
-        self.chartData = ko.observable();
+        self.data = [];
+        self.series = [{
+          label: '${ _("CPU") }',
+          color: '#29A7DE',
+          enabled: true
+        }, {
+          label: '${ _("Memory") }',
+          color: '#ACA0A4',
+          enabled: true
+        }, {
+          label: '${ _("IO") }',
+          color: '#E4689A',
+          enabled: true
+        }, {
+          label: '${ _("Queries") }',
+          color: '#EBA81A',
+          enabled: true
+        }];
+
+        self.cpuStats = ko.observable({ average: '-', min: '-', max: '-', color: self.series[0].color });
+        self.memoryStats = ko.observable({ average: '-', min: '-', max: '-', color: self.series[1].color });
         self.memoryLimit = 64 * 1024 * 1024 * 1024; // 64GB should be fetched from API
         self.memoryLimit = 64 * 1024 * 1024 * 1024; // 64GB should be fetched from API
 
 
-        self.fetchData();
+        self.fetchData(300000, 100);
 
 
         self.graphContainerRendered = function (domTree) {
         self.graphContainerRendered = function (domTree) {
           var graphElement = domTree[1];
           var graphElement = domTree[1];
 
 
           var maxQueryCount = 20;
           var maxQueryCount = 20;
 
 
-          var subTop = self.graphHeight - 100;
+          var subTop = self.graphHeight - 150;
+          var legendTop = self.graphHeight - 50;
 
 
           var mainMargin = { top: 10, right: 70, left: 70, bottom: self.graphHeight - subTop + 40 };
           var mainMargin = { top: 10, right: 70, left: 70, bottom: self.graphHeight - subTop + 40 };
           var subMargin = { top: subTop, right: 70, bottom: 40, left: 70 };
           var subMargin = { top: subTop, right: 70, bottom: 40, left: 70 };
-          var height = self.graphHeight - mainMargin.top - mainMargin.bottom;
-          var subHeight = self.graphHeight - subMargin.top - subMargin.bottom;
+          var legendMargin = { top: legendTop, right: 70, bottom: 10, left: 70 };
+
           var width = self.graphWidth - mainMargin.left - mainMargin.right;
           var width = self.graphWidth - mainMargin.left - mainMargin.right;
 
 
           var mainHeight = self.graphHeight - mainMargin.top - mainMargin.bottom;
           var mainHeight = self.graphHeight - mainMargin.top - mainMargin.bottom;
           var mainYScale = d3.scaleLinear().range([mainHeight, 0]);
           var mainYScale = d3.scaleLinear().range([mainHeight, 0]);
 
 
+          var subHeight = self.graphHeight - subMargin.top - subMargin.bottom - 50;
+
+          var legendHeight = self.graphHeight - legendMargin.top - legendMargin.bottom;
           var subXScale = d3.scaleTime().range([0, width]);
           var subXScale = d3.scaleTime().range([0, width]);
           var subYScale = d3.scaleLinear().range([subHeight, 0]);
           var subYScale = d3.scaleLinear().range([subHeight, 0]);
 
 
@@ -136,13 +154,21 @@ from django.utils.translation import ugettext as _
           });
           });
 
 
           var subXAxis = d3.axisBottom(subXScale);
           var subXAxis = d3.axisBottom(subXScale);
-          ##  var subYAxis = d3.axisLeft(subYScale).ticks(2);
+
+          var dateBisector = d3.bisector(function(d) { return d[0]; }).left;
 
 
           var graphCount = 2;
           var graphCount = 2;
 
 
           var mainLines = [];
           var mainLines = [];
           var subLines = [];
           var subLines = [];
 
 
+          var toggleSeries = function (i) {
+            self.series[i].enabled = !self.series[i].enabled;
+            svg.selectAll('.line-' + i).style('display', self.series[i].enabled ? null : 'none');
+            svg.selectAll('.line-circle-' + i).style('display', self.series[i].enabled ? null : 'none');
+            svg.select('.legend-circle-' + i).attr('fill-opacity', self.series[i].enabled ? 1 : 0.2);
+          };
+
           for (var i = 0; i < graphCount; i++) {
           for (var i = 0; i < graphCount; i++) {
             mainLines.push(d3.line().curve(d3.curveMonotoneX).x(function (d) {
             mainLines.push(d3.line().curve(d3.curveMonotoneX).x(function (d) {
               return mainXScale(d[0]);
               return mainXScale(d[0]);
@@ -159,119 +185,241 @@ from django.utils.translation import ugettext as _
           var svg = d3.select(graphElement).append('svg').attr('width', self.graphWidth).attr('height', self.graphHeight);
           var svg = d3.select(graphElement).append('svg').attr('width', self.graphWidth).attr('height', self.graphHeight);
 
 
           svg.append('defs')
           svg.append('defs')
-            .append('clipPath')
-            .attr('id', 'clip')
-            .append('rect')
-            .attr('width', width)
-            .attr('height', mainHeight);
+                  .append('clipPath')
+                  .attr('id', 'clip')
+                  .append('rect')
+                  .attr('width', width)
+                  .attr('height', mainHeight);
 
 
           var main = svg.append('g')
           var main = svg.append('g')
-            .attr('class', 'focus')
-            .attr('transform', 'translate(' + mainMargin.left + ',' + mainMargin.top + ')');
+                  .attr('class', 'focus')
+                  .attr('transform', 'translate(' + mainMargin.left + ',' + mainMargin.top + ')');
 
 
           var sub = svg.append('g')
           var sub = svg.append('g')
-            .attr('class', 'context')
-            .attr('transform', 'translate(' + subMargin.left + ',' + subMargin.top + ')');
+                  .attr('class', 'context')
+                  .attr('transform', 'translate(' + subMargin.left + ',' + subMargin.top + ')');
 
 
-          var brush = d3.brushX()
-            .extent([[0, 0], [width, subHeight]])
-            .on('brush end', function () {
-              if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'zoom') {
-                return;
-              }
-              var s = d3.event.selection || subXScale.range();
-              mainXScale.domain(s.map(subXScale.invert, subXScale));
-              for (var i = 0; i < mainLines.length; i++) {
-                main.select('.line-' + i).attr('d', mainLines[i]);
-              }
-              main.select('.axis--x').call(mainXAxis);
-              svg.select('.zoom').call(zoom.transform, d3.zoomIdentity.scale(width / (s[1] - s[0])).translate(-s[0], 0));
+          var legend = svg.append('g')
+                  .attr('class', 'context')
+                  .attr('transform', 'translate(' + legendMargin.left + ',' + legendMargin.top + ')');
+
+          var nextSeriesX = 5;
+          for (var i = 0; i < graphCount; i++) {
+            var legendSerie = legend.append('g')
+              .on('click', function(d, j) {
+                toggleSeries(this);
+              }.bind(i))
+              .on('mouseover', function(d, j) {
+
+              })
+              .on('mouseout', function(d, j) {
+
+              });
+            legendSerie.append('circle')
+                    .attr('class', 'legend-circle-' + i)
+                    .style('fill', function(d, j) { return self.series[i].color })
+                    .style('stroke', function(d, j) { return self.series[i].color  })
+                    .attr('r', 5);
+            legendSerie.append('text')
+                    .text(self.series[i].label)
+                    .attr('text-anchor', 'start')
+                    .attr('dy', '.32em')
+                    .attr('dx', '8');
+
+            legendSerie.attr('transform', function(d, j) {
+              var length = d3.select(this).select('text').node().getComputedTextLength() + 28;
+              var xpos = nextSeriesX;
+              nextSeriesX += length;
+              return 'translate(' + xpos + ',' + 5 + ')'
             });
             });
+          }
+
+          var brush = d3.brushX()
+                  .extent([[0, 0], [width, subHeight]])
+                  .on('brush end', function () {
+                    if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'zoom') {
+                      return;
+                    }
+                    var s = d3.event.selection || subXScale.range();
+                    mainXScale.domain(s.map(subXScale.invert, subXScale));
+                    for (var i = 0; i < mainLines.length; i++) {
+                      main.select('.line-' + i).attr('d', mainLines[i]);
+                      main.selectAll('.line-circle-' + i)
+                              .attr("cx", function(d) { return mainXScale(d[0]); })
+                              .attr("cy", function(d) { return mainYScale(d[i + 1]); });
+                    }
+                    main.select('.axis--x').call(mainXAxis);
+                    svg.select('.zoom').call(zoom.transform, d3.zoomIdentity.scale(width / (s[1] - s[0])).translate(-s[0], 0));
+                  });
 
 
           var zoom = d3.zoom()
           var zoom = d3.zoom()
-            .scaleExtent([1, Infinity])
-            .translateExtent([[0, 0], [width, mainHeight]])
-            .extent([[0, 0], [width, mainHeight]])
-            .on('zoom', function () {
-              if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'brush') {
-                return;
-              }
-              var t = d3.event.transform;
-              mainXScale.domain(t.rescaleX(subXScale).domain());
-              for (var i = 0; i < mainLines.length; i++) {
-                main.select('.line-' + i).attr('d', mainLines[i]);
-              }
-              main.select('.axis--x').call(mainXAxis);
-              sub.select('.brush').call(brush.move, mainXScale.range().map(t.invertX, t));
-            });
+                  .scaleExtent([1, Infinity])
+                  .translateExtent([[0, 0], [width, mainHeight]])
+                  .extent([[0, 0], [width, mainHeight]])
+                  .on('zoom', function () {
+                    if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'brush') {
+                      return;
+                    }
+                    var t = d3.event.transform;
+                    mainXScale.domain(t.rescaleX(subXScale).domain());
+                    for (var i = 0; i < mainLines.length; i++) {
+                      main.select('.line-' + i).attr('d', mainLines[i]);
+                      main.selectAll('.line-circle-' + i)
+                              .attr("cx", function(d) { return mainXScale(d[0]); })
+                              .attr("cy", function(d) { return mainYScale(d[i + 1]); });
+                    }
+                    main.select('.axis--x').call(mainXAxis);
+                    sub.select('.brush').call(brush.move, mainXScale.range().map(t.invertX, t));
+                  });
 
 
           var drawGraphs = function () {
           var drawGraphs = function () {
-            var data = self.chartData().slice(1);
-
-            mainXScale.domain([data[0][0], data[data.length - 1][0]]);
+            mainXScale.domain([self.data[0][0], self.data[self.data.length - 1][0]]);
             mainYScale.domain([0, 100]);
             mainYScale.domain([0, 100]);
             subXScale.domain(mainXScale.domain());
             subXScale.domain(mainXScale.domain());
             subYScale.domain(mainYScale.domain());
             subYScale.domain(mainYScale.domain());
 
 
             main.append('g')
             main.append('g')
-              .attr('class', 'axis axis--x')
-              .attr('transform', 'translate(0,' + height + ')')
-              .call(mainXAxis);
+                    .attr('class', 'axis axis--x')
+                    .attr('transform', 'translate(0,' + mainHeight + ')')
+                    .call(mainXAxis);
 
 
             main.append('g')
             main.append('g')
-              .attr('class', 'axis axis--y')
-              .call(mainYAxis);
+                    .attr('class', 'axis axis--y')
+                    .call(mainYAxis);
 
 
             main.append('g')
             main.append('g')
-              .attr('class', 'axis axis--y')
-              .attr("transform", "translate(" + width + " ,0)")
-              .call(secondaryYAxis);
+                    .attr('class', 'axis axis--y')
+                    .attr("transform", "translate(" + width + " ,0)")
+                    .call(secondaryYAxis);
 
 
             sub.append('g')
             sub.append('g')
-              .attr('class', 'axis axis--x')
-              .attr('transform', 'translate(0,' + subHeight + ')')
-              .call(subXAxis);
+                    .attr('class', 'axis axis--x')
+                    .attr('transform', 'translate(0,' + subHeight + ')')
+                    .call(subXAxis);
 
 
             sub.append('g')
             sub.append('g')
-              .attr('class', 'brush')
-              .call(brush)
-              .call(brush.move, mainXScale.range());
+                    .attr('class', 'brush')
+                    .call(brush)
+                    .call(brush.move, mainXScale.range());
 
 
             svg.append('rect')
             svg.append('rect')
-              .attr('class', 'zoom')
-              .attr('fill', 'none')
-              .attr('width', width)
-              .attr('height', mainHeight)
-              .attr('transform', 'translate(' + mainMargin.left + ',' + mainMargin.top + ')')
-              .call(zoom);
+                    .attr('class', 'zoom')
+                    .attr('fill', 'none')
+                    .attr('width', width)
+                    .attr('height', mainHeight)
+                    .attr('transform', 'translate(' + mainMargin.left + ',' + mainMargin.top + ')')
+                    .call(zoom);
 
 
             for (var i = 0; i < graphCount; i++) {
             for (var i = 0; i < graphCount; i++) {
               main.append('path')
               main.append('path')
-                .datum(data)
-                .attr('stroke', function (d, j) { return AVAILABLE_TYPES[i].color })
-                .attr('fill', 'none')
-                .classed('line line-' + i, true)
-                .attr('d', mainLines[i]);
+                      .datum(self.data)
+                      .attr('stroke', function (d, j) { return self.series[i].color })
+                      .attr('fill', 'none')
+                      .attr("clip-path", "url(#clip)")
+                      .classed('line line-' + i, true)
+                      .attr('d', mainLines[i]);
+
+              main.selectAll("line-circle-" + i)
+                      .data(self.data)
+                      .enter()
+                      .append("circle")
+                      .attr('stroke', function (d, j) { return 'white'; })
+                      .attr('fill', function (d, j) { return self.series[i].color })
+                      .attr("clip-path", "url(#clip)")
+                      .attr("class", "line-circle-" + i)
+                      .attr("r", 1.5)
+                      .attr("cx", function(d) { return mainXScale(d[0]); })
+                      .attr("cy", function(d) { return mainYScale(d[i + 1]); });
+
               sub.append('path')
               sub.append('path')
-                .datum(data)
-                .attr('stroke', function (d, j) { return AVAILABLE_TYPES[i].color })
-                .attr('fill', 'none')
-                .classed('line-' + i, true)
-                .attr('d', subLines[i]);
+                      .datum(self.data)
+                      .attr('stroke', function (d, j) { return self.series[i].color })
+                      .attr('fill', 'none')
+                      .classed('line line-' + i, true)
+                      .attr('d', subLines[i]);
+
+              var focus = svg.append("g")
+                      .attr("class", "point-focus point-focus-" + i)
+                      .attr('fill', 'none')
+                      .attr('stroke', function (d, j) { return self.series[i].color })
+                      .style("display", "none");
+
+              focus.append("circle")
+                      .attr("r", 5);
             }
             }
+
+            // Hover overlay
+            svg.append("rect")
+                    .attr("class", "overlay")
+                    .attr("fill-opacity", "0")
+                    .attr("fill", "white")
+                    .attr('transform', 'translate(' + mainMargin.left + ',' + mainMargin.top + ')')
+                    .attr("width", width)
+                    .attr("height", mainHeight)
+                    .on("mouseover", function() {
+                      for (var i = 0; i < graphCount; i++) {
+                        if (self.series[i].enabled) {
+                          svg.select('.point-focus-' + i).style("display", null);
+                        }
+                      }
+                    })
+                    .on("mouseout", function() {
+                      svg.selectAll('.point-focus').style("display", "none");
+                    })
+                    .on("mousemove", function () {
+                              var x = mainXScale.invert(d3.mouse(this)[0]);
+                              var j = dateBisector(self.data, x, 1);
+                              var d0 = self.data[j-1];
+                              var d1 = self.data[j];
+                              var closest = x - d0 > d1 - x ? d1 : d0;
+                              for (var i = 0; i < graphCount; i++) {
+                                if (self.series[i].enabled) {
+                                  svg.select('.point-focus-' + i).attr("transform", "translate(" + (mainXScale(closest[0]) + mainMargin.left) + "," + (mainYScale(closest[i + 1]) + mainMargin.top) + ")");
+                                }
+                              }
+                            }
+                    );
           };
           };
 
 
           drawGraphs();
           drawGraphs();
+
+          var fakeAppend = function () {
+            var currentDomain = mainXScale.domain();
+            var currentSubDomain = subXScale.domain();
+            addFakeMeasurement(self.data, Date.now());
+            var mainStart = currentDomain[0].getTime() !== currentSubDomain[0].getTime() ? currentDomain[0] : self.data[0][0];
+            var mainEnd = currentDomain[1].getTime() !== currentSubDomain[1].getTime() ? currentDomain[1] : self.data[self.data.length - 1][0];
+            mainXScale.domain([mainStart, mainEnd]);
+            subXScale.domain([self.data[0][0], self.data[self.data.length - 1][0]]);
+
+
+            for (var i = 0; i < mainLines.length; i++) {
+              main.select('.line-' + i).attr('d', mainLines[i]);
+              main.selectAll('.line-circle-' + i)
+                      .attr("cx", function(d) { return mainXScale(d[0]); })
+                      .attr("cy", function(d) { return mainYScale(d[i + 1]); });
+              sub.select('.line-' + i).attr('d', subLines[i]);
+            }
+
+            sub.select('.axis--x').call(subXAxis);
+            main.select('.axis--x').call(mainXAxis);
+
+             sub.select('.brush').call(brush);
+
+            window.setTimeout(fakeAppend, 2000);
+          };
+
+          window.setTimeout(fakeAppend, 2000);
         }
         }
       }
       }
 
 
-      PerformanceGraph.prototype.fetchData = function () {
+      PerformanceGraph.prototype.fetchData = function (timeOffset, points) {
         var self = this;
         var self = this;
 
 
-        var chartData = generateFakeData();
-        var cpuStats = { average: 0, min: 0, max: 0, color: AVAILABLE_TYPES[0].color };
-        var memoryStats = { average: 0, min: 0, max: 0, color: AVAILABLE_TYPES[1].color };
-        chartData.slice(1).forEach(function (val) {
+        generateFakeData(self.data, timeOffset, points);
+        var cpuStats = { average: 0, min: 0, max: 0, color: self.series[0].color };
+        var memoryStats = { average: 0, min: 0, max: 0, color: self.series[1].color };
+        self.data.forEach(function (val) {
           cpuStats.max = Math.max(val[1], cpuStats.max);
           cpuStats.max = Math.max(val[1], cpuStats.max);
           cpuStats.min = Math.min(val[1], cpuStats.min);
           cpuStats.min = Math.min(val[1], cpuStats.min);
           cpuStats.average += val[1];
           cpuStats.average += val[1];
@@ -279,8 +427,8 @@ from django.utils.translation import ugettext as _
           memoryStats.min = Math.min(val[2], memoryStats.min);
           memoryStats.min = Math.min(val[2], memoryStats.min);
           memoryStats.average += val[2];
           memoryStats.average += val[2];
         });
         });
-        cpuStats.average = cpuStats.average / (chartData.length - 1);
-        memoryStats.average = memoryStats.average / (chartData.length - 1);
+        cpuStats.average = Math.round(cpuStats.average / self.data.length);
+        memoryStats.average = Math.round(memoryStats.average / self.data.length);
 
 
         cpuStats.average += '%';
         cpuStats.average += '%';
         cpuStats.min += '%';
         cpuStats.min += '%';
@@ -290,7 +438,6 @@ from django.utils.translation import ugettext as _
         memoryStats.max = bytesToSize(self.memoryLimit * memoryStats.max / 100);
         memoryStats.max = bytesToSize(self.memoryLimit * memoryStats.max / 100);
         self.cpuStats(cpuStats);
         self.cpuStats(cpuStats);
         self.memoryStats(memoryStats);
         self.memoryStats(memoryStats);
-        self.chartData(chartData);
       };
       };
 
 
       ko.components.register('performance-graph', {
       ko.components.register('performance-graph', {