performance improvements

pull/5/head
cubedro 10 years ago
parent d340b08f2a
commit 8cf001d24c
  1. 5
      app.js
  2. 2
      dist/css/netstats.min.css
  3. 2
      dist/index.html
  4. 2
      dist/js/netstats.min.js
  5. 2
      dist/js/netstats.min.js.map
  6. 10
      models/collection.js
  7. 6
      public/css/style.css
  8. 164
      public/js/controllers.js
  9. 88
      public/js/directives.js
  10. 2
      public/js/script.js
  11. 11
      views/index.jade

@ -157,11 +157,6 @@ api.on('connection', function(spark) {
}); });
askedForHistory = false; askedForHistory = false;
client.write({
action: 'charts',
data: Nodes.getCharts()
});
}); });
spark.on('node-ping', function(data) spark.on('node-ping', function(data)

File diff suppressed because one or more lines are too long

2
dist/index.html vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -22,12 +22,12 @@ Collection.prototype.update = function(id, stats)
{ {
var node = this.getNode({ id: id }); var node = this.getNode({ id: id });
if(!node) if (!node)
return false; return false;
var block = this._blockchain.add(stats.block, id); var block = this._blockchain.add(stats.block, id);
if(! block) if (!block)
return false; return false;
var propagationHistory = this._blockchain.getNodePropagation(id); var propagationHistory = this._blockchain.getNodePropagation(id);
@ -43,7 +43,7 @@ Collection.prototype.addHistory = function(id, blocks)
{ {
var node = this.getNode({ id: id }); var node = this.getNode({ id: id });
if(!node) if (!node)
return false; return false;
blocks = blocks.reverse(); blocks = blocks.reverse();
@ -60,7 +60,7 @@ Collection.prototype.updateLatency = function(id, latency)
{ {
var node = this.getNode({ id: id }); var node = this.getNode({ id: id });
if(!node) if (!node)
return false; return false;
return node.setLatency(latency); return node.setLatency(latency);
@ -70,7 +70,7 @@ Collection.prototype.inactive = function(id)
{ {
var node = this.getNode({ spark: id }); var node = this.getNode({ spark: id });
if(!node) if (!node)
return false; return false;
node.setState(false); node.setState(false);

@ -277,6 +277,12 @@ table td i {
table td.peerPropagationChart { table td.peerPropagationChart {
padding: 4px 5px !important; padding: 4px 5px !important;
} }
nodepropagchart {
display: inline-block;
width: 107px;
height: 20px;
vertical-align: top;
}
.table>tbody>tr>td, .table>tbody>tr>td,
.table>thead>tr>th { .table>thead>tr>th {

@ -69,7 +69,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
$scope.$apply(); $scope.$apply();
}, 200); }, 200);
$scope.getNumber = function(num) { $scope.getNumber = function (num) {
return new Array(num); return new Array(num);
} }
@ -118,7 +118,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
$scope.nodes = data; $scope.nodes = data;
_.forEach($scope.nodes, function(node, index) { _.forEach($scope.nodes, function (node, index) {
// Init hashrate // Init hashrate
if( _.isUndefined(node.stats.hashrate) ) if( _.isUndefined(node.stats.hashrate) )
$scope.nodes[index].stats.hashrate = 0; $scope.nodes[index].stats.hashrate = 0;
@ -132,10 +132,6 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
// Init or recover pin // Init or recover pin
$scope.nodes[index].pinned = _.result(_.find(oldNodes, 'id', node.id), 'pinned', false); $scope.nodes[index].pinned = _.result(_.find(oldNodes, 'id', node.id), 'pinned', false);
$scope.$apply();
makePeerPropagationChart($scope.nodes[index]);
}); });
if($scope.nodes.length > 0) if($scope.nodes.length > 0)
@ -144,27 +140,26 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
break; break;
case "add": case "add":
if(addNewNode(data)) var index = findIndex({id: data.id});
toastr['success']("New node "+ $scope.nodes[findIndex({id: data.id})].info.name +" connected!", "New node!");
else
toastr['info']("Node "+ $scope.nodes[findIndex({id: data.id})].info.name +" reconnected!", "Node is back!");
$scope.$apply(); if( addNewNode(data) )
makePeerPropagationChart($scope.nodes[findIndex({id: data.id})]); toastr['success']("New node "+ $scope.nodes[index].info.name +" connected!", "New node!");
else
toastr['info']("Node "+ $scope.nodes[index].info.name +" reconnected!", "Node is back!");
break; break;
case "update": case "update":
if(typeof data.stats.hashrate === 'undefined')
data.stats.hashrate = 0;
var index = findIndex({id: data.id}); var index = findIndex({id: data.id});
if( _.isUndefined(data.stats.hashrate) )
data.stats.hashrate = 0;
if( !_.isUndefined($scope.nodes[index].stats) ) { if( !_.isUndefined($scope.nodes[index].stats) ) {
if($scope.nodes[index].stats.block.number < data.stats.block.number) if( $scope.nodes[index].stats.block.number < data.stats.block.number )
{ {
var best = _.max($scope.nodes, function(node) { var best = _.max($scope.nodes, function (node) {
return parseInt(node.stats.block.number); return parseInt(node.stats.block.number);
}).stats.block; }).stats.block;
@ -173,23 +168,22 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
} else { } else {
data.stats.block.arrived = best.arrived; data.stats.block.arrived = best.arrived;
} }
$scope.nodes[index].history = data.history;
} }
$scope.nodes[index].stats = data.stats; $scope.nodes[index].stats = data.stats;
$scope.nodes[index].history = data.history;
$scope.$apply();
makePeerPropagationChart($scope.nodes[index]);
} }
break; break;
case "info": case "info":
$scope.nodes[findIndex({id: data.id})].info = data.info; var index = findIndex({id: data.id});
$scope.nodes[index].info = data.info;
if(_.isUndefined($scope.nodes[findIndex({id: data.id})].pinned)) if( _.isUndefined($scope.nodes[index].pinned) )
$scope.nodes[findIndex({id: data.id})].pinned = false; $scope.nodes[index].pinned = false;
break; break;
@ -200,10 +194,8 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
break; break;
case "uncleCount": case "uncleCount":
$scope.uncleCountChart = data;
$scope.uncleCount = data[0] + data[1]; $scope.uncleCount = data[0] + data[1];
$scope.uncleCountChart = data.reverse();
jQuery('.spark-uncles').sparkline($scope.uncleCountChart.reverse(), {type: 'bar', barSpacing: 1});
break; break;
@ -214,40 +206,34 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
$scope.difficultyChart = data.difficulty; $scope.difficultyChart = data.difficulty;
$scope.blockPropagationChart = data.propagation.histogram; $scope.blockPropagationChart = data.propagation.histogram;
$scope.blockPropagationAvg = data.propagation.avg; $scope.blockPropagationAvg = data.propagation.avg;
$scope.uncleCountChart = data.uncleCount;
$scope.uncleCount = data.uncleCount[0] + data.uncleCount[1]; $scope.uncleCount = data.uncleCount[0] + data.uncleCount[1];
$scope.uncleCountChart = data.uncleCount.reverse();
$scope.transactionDensity = data.transactions; $scope.transactionDensity = data.transactions;
$scope.gasSpending = data.gasSpending; $scope.gasSpending = data.gasSpending;
$scope.miners = data.miners; $scope.miners = data.miners;
$scope.$apply();
getMinersNames(); getMinersNames();
jQuery('.spark-blocktimes').sparkline($scope.lastBlocksTime, {type: 'bar', tooltipSuffix: ' s'});
jQuery('.spark-difficulty').sparkline($scope.difficultyChart, {type: 'bar'});
jQuery('.spark-transactions').sparkline($scope.transactionDensity, {type: 'bar'});
jQuery('.spark-gasspending').sparkline($scope.gasSpending, {type: 'bar'});
jQuery('.spark-uncles').sparkline($scope.uncleCountChart.reverse(), {type: 'bar', barSpacing: 1});
break; break;
case "inactive": case "inactive":
var index = findIndex({id: data.id});
if( !_.isUndefined(data.stats) ) if( !_.isUndefined(data.stats) )
$scope.nodes[findIndex({id: data.id})].stats = data.stats; $scope.nodes[index].stats = data.stats;
toastr['error']("Node "+ $scope.nodes[findIndex({id: data.id})].info.name +" went away!", "Node connection was lost!"); toastr['error']("Node "+ $scope.nodes[index].info.name +" went away!", "Node connection was lost!");
break; break;
case "latency": case "latency":
if( !_.isUndefined(data.id) ) var index = findIndex({id: data.id});
var node = $scope.nodes[findIndex({id: data.id})];
if( !_.isUndefined(node) && !_.isUndefined(node.stats) && !_.isUndefined(node.stats.latency)) if( !_.isUndefined(data.id) )
$scope.nodes[findIndex({id: data.id})].stats.latency = data.latency; var node = $scope.nodes[index];
$scope.$apply(); if( !_.isUndefined(node) && !_.isUndefined(node.stats) && !_.isUndefined(node.stats.latency) )
$scope.nodes[index].stats.latency = data.latency;
break; break;
@ -257,7 +243,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
break; break;
} }
if(action !== "latency") if( action !== "latency" && action !== "client-ping" )
{ {
updateStats(); updateStats();
} }
@ -268,39 +254,11 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
return _.findIndex($scope.nodes, search); return _.findIndex($scope.nodes, search);
} }
function makePeerPropagationChart(node)
{
jQuery('.' + node.id).sparkline(node.history, {
type: 'bar',
negBarColor: '#7f7f7f',
zeroAxis: false,
height: 20,
barWidth : 2,
barSpacing : 1,
tooltipSuffix: '',
chartRangeMax: 8000,
colorMap: jQuery.range_map({
'0:1': '#10a0de',
'1:1000': '#7bcc3a',
'1001:3000': '#FFD162',
'3001:7000': '#ff8a00',
'7001:': '#F74B4B'
}),
tooltipFormatter: function (spark, opt, ms) {
var tooltip = '<div class="tooltip-arrow"></div><div class="tooltip-inner">';
tooltip += $filter('blockPropagationFilter')(ms[0].value, '');
tooltip += '</div>';
return tooltip;
}
});
}
function getMinersNames() function getMinersNames()
{ {
if($scope.miners.length > 0) if( $scope.miners.length > 0 )
{ {
_.forIn($scope.miners, function(value, key) _.forIn($scope.miners, function (value, key)
{ {
if(value.name !== false) if(value.name !== false)
return; return;
@ -310,7 +268,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
var name = _.result(_.find(_.pluck($scope.nodes, 'info'), 'coinbase', value.miner), 'name'); var name = _.result(_.find(_.pluck($scope.nodes, 'info'), 'coinbase', value.miner), 'name');
if(typeof name !== 'undefined') if( !_.isUndefined(name) )
$scope.miners[key].name = name; $scope.miners[key].name = name;
}); });
} }
@ -319,44 +277,32 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
function addNewNode(data) function addNewNode(data)
{ {
var index = findIndex({id: data.id}); var index = findIndex({id: data.id});
if(index < 0)
{
if(typeof data.stats !== 'undefined' && typeof data.stats.hashrate === 'undefined')
data.stats.hashrate = 0;
data.pinned = false; if( _.isUndefined(data.history) )
{
data.history = new Array(40);
_.fill(data.history, -1);
}
if( _.isUndefined(data.history) ) if( index < 0 )
{
if( !_.isUndefined(data.stats) && _.isUndefined(data.stats.hashrate) )
{ {
data.history = new Array(40); data.stats.hashrate = 0;
_.fill(data.history, -1);
} }
data.pinned = false;
$scope.nodes.push(data); $scope.nodes.push(data);
return true; return true;
} }
if( !_.isUndefined($scope.nodes[index].pinned) ) data.pinned = ( !_.isUndefined($scope.nodes[index].pinned) ? $scope.nodes[index].pinned : false);
{
data.pinned = $scope.nodes[index].pinned
}
else
{
data.pinned = false;
}
if( !_.isUndefined($scope.nodes[index].pinned) ) if( !_.isUndefined($scope.nodes[index].history) )
{ {
data.history = $scope.nodes[index].history data.history = $scope.nodes[index].history;
}
else
{
if( _.isUndefined(data.history) )
{
data.history = new Array(40);
_.fill(data.history, -1);
}
} }
$scope.nodes[index] = data; $scope.nodes[index] = data;
@ -366,22 +312,22 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
function updateStats() function updateStats()
{ {
if($scope.nodes.length) if( $scope.nodes.length )
{ {
$scope.nodesTotal = $scope.nodes.length; $scope.nodesTotal = $scope.nodes.length;
$scope.nodesActive = _.filter($scope.nodes, function(node) { $scope.nodesActive = _.filter($scope.nodes, function (node) {
return node.stats.active == true; return node.stats.active == true;
}).length; }).length;
var bestBlock = _.max($scope.nodes, function(node) { var bestBlock = _.max($scope.nodes, function (node) {
return parseInt(node.stats.block.number); return parseInt(node.stats.block.number);
}).stats.block.number; }).stats.block.number;
if(bestBlock > $scope.bestBlock) if( bestBlock > $scope.bestBlock )
{ {
$scope.bestBlock = bestBlock; $scope.bestBlock = bestBlock;
$scope.bestStats = _.max($scope.nodes, function(node) { $scope.bestStats = _.max($scope.nodes, function (node) {
return parseInt(node.stats.block.number); return parseInt(node.stats.block.number);
}).stats; }).stats;
@ -389,11 +335,11 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, socket, _, toastr)
$scope.lastDifficulty = $scope.bestStats.block.difficulty; $scope.lastDifficulty = $scope.bestStats.block.difficulty;
} }
$scope.upTimeTotal = _.reduce($scope.nodes, function(total, node) { $scope.upTimeTotal = _.reduce($scope.nodes, function (total, node) {
return total + node.stats.uptime; return total + node.stats.uptime;
}, 0) / $scope.nodes.length; }, 0) / $scope.nodes.length;
$scope.map = _.map($scope.nodes, function(node) { $scope.map = _.map($scope.nodes, function (node) {
var fill = $filter('bubbleClass')(node.stats, $scope.bestBlock); var fill = $filter('bubbleClass')(node.stats, $scope.bestBlock);
if(node.geo != null) if(node.geo != null)

@ -2,13 +2,89 @@
/* Directives */ /* Directives */
angular.module('netStatsApp.directives', []). angular.module('netStatsApp.directives', [])
directive('appVersion', ['version', function(version) { .directive('appVersion', ['version', function (version) {
return function(scope, elm, attrs) { return function(scope, elm, attrs) {
elm.text(version); elm.text(version);
}; };
}]). }])
directive('nodemap', ['$compile', function($compile) { .directive('sparkchart', ['$compile', '$filter', function($compile, $filter) {
return {
restrict: 'EA',
scope: {
data: '='
},
link: function (scope, element, attrs)
{
scope.init = function ()
{
element.empty();
jQuery(element[0]).sparkline(scope.data, {
type: 'bar',
tooltipSuffix: (attrs.tooltipsuffix || '')
});
}
scope.init();
scope.$watch('data', function ()
{
scope.init();
}, true);
}
};
}])
.directive('nodepropagchart', ['$compile', '$filter', function($compile, $filter) {
return {
restrict: 'EA',
scope: {
data: '='
},
link: function (scope, element, attrs)
{
var options = {
type: 'bar',
negBarColor: '#7f7f7f',
zeroAxis: false,
height: 20,
barWidth : 2,
barSpacing : 1,
tooltipSuffix: '',
chartRangeMax: 8000,
colorMap: jQuery.range_map({
'0:1': '#10a0de',
'1:1000': '#7bcc3a',
'1001:3000': '#FFD162',
'3001:7000': '#ff8a00',
'7001:': '#F74B4B'
}),
tooltipFormatter: function (spark, opt, ms) {
var tooltip = '<div class="tooltip-arrow"></div><div class="tooltip-inner">';
tooltip += $filter('blockPropagationFilter')(ms[0].value, '');
tooltip += '</div>';
return tooltip;
}
};
scope.init = function ()
{
element.empty();
jQuery(element[0]).sparkline(scope.data, options);
}
scope.init();
scope.$watch('data', function ()
{
scope.init();
}, true);
}
};
}])
.directive('nodemap', ['$compile', function($compile) {
return { return {
restrict: 'EA', restrict: 'EA',
scope: { scope: {
@ -112,8 +188,8 @@ angular.module('netStatsApp.directives', []).
}, true); }, true);
} }
}; };
}]). }])
directive('histogram', ['$compile', function($compile) { .directive('histogram', ['$compile', function($compile) {
return { return {
restrict: 'EA', restrict: 'EA',
scope: { scope: {

@ -10,7 +10,7 @@
$.fn.sparkline.defaults.bar.barSpacing = 1; $.fn.sparkline.defaults.bar.barSpacing = 1;
$.fn.sparkline.defaults.bar.tooltipClassname = 'jqstooltip'; $.fn.sparkline.defaults.bar.tooltipClassname = 'jqstooltip';
$.fn.sparkline.defaults.bar.tooltipOffsetX = 0; $.fn.sparkline.defaults.bar.tooltipOffsetX = 0;
$.fn.sparkline.defaults.bar.tooltipFormat = $.spformat('<div class="tooltip-arrow"></div><div class="tooltip-inner">{{prefix}}{{value}}{{suffix}}</div>'); $.fn.sparkline.defaults.bar.tooltipFormat = $.spformat('<div class="tooltip-arrow"></div><div class="tooltip-inner">{{prefix}}{{value}} {{suffix}}</div>');
$.fn.sparkline.defaults.bar.colorMap = $.range_map({ $.fn.sparkline.defaults.bar.colorMap = $.range_map({
'0:5': '#10a0de', '0:5': '#10a0de',
'6:12': '#7bcc3a', '6:12': '#7bcc3a',

@ -92,14 +92,14 @@ block content
//- i.icon-time //- i.icon-time
span.small-title block time span.small-title block time
//- span.small-value {{ avgBlockTime | avgTimeFilter }} //- span.small-value {{ avgBlockTime | avgTimeFilter }}
span.big-details.spark-blocktimes sparkchart.big-details.spark-blocktimes(data="lastBlocksTime", tooltipsuffix="s")
div.col-xs-3.stat-holder div.col-xs-3.stat-holder
div.big-info.chart.text-info div.big-info.chart.text-info
//- i.icon-difficulty //- i.icon-difficulty
span.small-title difficulty span.small-title difficulty
//- span.small-value {{ lastDifficulty | number }} //- span.small-value {{ lastDifficulty | number }}
span.big-details.spark-difficulty sparkchart.big-details.spark-difficulty(data="difficultyChart")
div.col-xs-3.stat-holder.xpull-right div.col-xs-3.stat-holder.xpull-right
div.big-info.chart.xdouble-chart(class="{{ blockPropagationAvg | propagationAvgTimeClass : true }}") div.big-info.chart.xdouble-chart(class="{{ blockPropagationAvg | propagationAvgTimeClass : true }}")
@ -123,19 +123,19 @@ block content
span.small-title uncle count #[&nbsp;] span.small-title uncle count #[&nbsp;]
span.small (25 blocks per bar) span.small (25 blocks per bar)
//- span.small-value {{ bestStats.block.uncles.length }}/{{ uncleCount }} //- span.small-value {{ bestStats.block.uncles.length }}/{{ uncleCount }}
span.big-details.spark-uncles sparkchart.big-details.spark-uncles(data="uncleCountChart")
div.col-xs-3.stat-holder div.col-xs-3.stat-holder
div.big-info.chart.text-info div.big-info.chart.text-info
//- i.icon-uncle //- i.icon-uncle
span.small-title transactions span.small-title transactions
span.big-details.spark-transactions sparkchart.big-details.spark-transactions(data="transactionDensity")
div.col-xs-3.stat-holder div.col-xs-3.stat-holder
div.big-info.chart.text-info div.big-info.chart.text-info
//- i.icon-gasprice //- i.icon-gasprice
span.small-title gas spending span.small-title gas spending
span.big-details.spark-gasspending sparkchart.big-details.spark-gasspending(data="gasSpending")
@ -209,5 +209,6 @@ block content
div.propagationBox div.propagationBox
span {{node.stats.block.propagation | blockPropagationFilter}} span {{node.stats.block.propagation | blockPropagationFilter}}
td.peerPropagationChart(class="{{node.id}}") td.peerPropagationChart(class="{{node.id}}")
nodepropagchart(data="node.history")
td(class="{{ node.stats | propagationNodeAvgTimeClass : bestBlock }}") {{ node.stats | blockPropagationAvgFilter : bestBlock }} td(class="{{ node.stats | propagationNodeAvgTimeClass : bestBlock }}") {{ node.stats | blockPropagationAvgFilter : bestBlock }}
td(class="{{ node.stats.uptime | upTimeClass : node.stats.active }}") {{ node.stats.uptime | upTimeFilter }} td(class="{{ node.stats.uptime | upTimeClass : node.stats.active }}") {{ node.stats.uptime | upTimeFilter }}

Loading…
Cancel
Save