Merge pull request #177 from cubedro/develop

Node model refactoring
This commit is contained in:
Marian OANCΞA 2015-06-03 04:01:15 +03:00
commit 748a6407e0
8 changed files with 217 additions and 143 deletions

16
app.js
View File

@ -88,6 +88,7 @@ api.on('connection', function (spark)
{ {
console.log((new Date()).toJSON(), '[API]', '[CON]', 'Open:', spark.address.ip); console.log((new Date()).toJSON(), '[API]', '[CON]', 'Open:', spark.address.ip);
spark.on('hello', function (data) spark.on('hello', function (data)
{ {
console.log((new Date()).toJSON(), '[API]', '[CON]', 'Hello', data['id']); console.log((new Date()).toJSON(), '[API]', '[CON]', 'Hello', data['id']);
@ -104,7 +105,7 @@ api.on('connection', function (spark)
{ {
data.ip = spark.address.ip; data.ip = spark.address.ip;
data.spark = spark.id; data.spark = spark.id;
data.latency = spark.latency; data.latency = spark.latency || 0;
var info = Nodes.add( data ); var info = Nodes.add( data );
spark.emit('ready'); spark.emit('ready');
@ -128,6 +129,7 @@ api.on('connection', function (spark)
} }
}); });
spark.on('update', function (data) spark.on('update', function (data)
{ {
if( !_.isUndefined(data.id) && !_.isUndefined(data.stats) ) if( !_.isUndefined(data.id) && !_.isUndefined(data.stats) )
@ -161,6 +163,7 @@ api.on('connection', function (spark)
} }
}); });
spark.on('block', function (data) spark.on('block', function (data)
{ {
if( !_.isUndefined(data.id) && !_.isUndefined(data.block) ) if( !_.isUndefined(data.id) && !_.isUndefined(data.block) )
@ -193,6 +196,7 @@ api.on('connection', function (spark)
} }
}); });
spark.on('pending', function (data) spark.on('pending', function (data)
{ {
if( !_.isUndefined(data.id) && !_.isUndefined(data.stats) ) if( !_.isUndefined(data.id) && !_.isUndefined(data.stats) )
@ -215,6 +219,7 @@ api.on('connection', function (spark)
} }
}); });
spark.on('stats', function (data) spark.on('stats', function (data)
{ {
if( !_.isUndefined(data.id) && !_.isUndefined(data.stats) ) if( !_.isUndefined(data.id) && !_.isUndefined(data.stats) )
@ -238,6 +243,7 @@ api.on('connection', function (spark)
} }
}); });
spark.on('history', function (data) spark.on('history', function (data)
{ {
console.log((new Date()).toJSON(), '[API]', '[HIS]', 'Got from:', data.id); console.log((new Date()).toJSON(), '[API]', '[HIS]', 'Got from:', data.id);
@ -256,6 +262,7 @@ api.on('connection', function (spark)
}); });
spark.on('node-ping', function (data) spark.on('node-ping', function (data)
{ {
var start = (!_.isUndefined(data) && !_.isUndefined(data.clientTime) ? data.clientTime : null); var start = (!_.isUndefined(data) && !_.isUndefined(data.clientTime) ? data.clientTime : null);
@ -268,16 +275,20 @@ api.on('connection', function (spark)
console.log((new Date()).toJSON(), '[API]', '[PIN]', 'Ping from:', data['id']); console.log((new Date()).toJSON(), '[API]', '[PIN]', 'Ping from:', data['id']);
}); });
spark.on('latency', function (data) spark.on('latency', function (data)
{ {
if( !_.isUndefined(data.id) ) if( !_.isUndefined(data.id) )
{ {
var latency = Nodes.updateLatency(data.id, data.latency); var latency = Nodes.updateLatency(data.id, data.latency);
if(latency)
{
client.write({ client.write({
action: 'latency', action: 'latency',
data: latency data: latency
}); });
}
console.log((new Date()).toJSON(), '[API]', '[PIN]', 'Latency:', latency, 'from:', data.id); console.log((new Date()).toJSON(), '[API]', '[PIN]', 'Latency:', latency, 'from:', data.id);
@ -294,6 +305,7 @@ api.on('connection', function (spark)
} }
}); });
spark.on('end', function (data) spark.on('end', function (data)
{ {
var stats = Nodes.inactive(spark.id); var stats = Nodes.inactive(spark.id);
@ -307,6 +319,8 @@ api.on('connection', function (spark)
}); });
}); });
client.on('connection', function (clientSpark) client.on('connection', function (clientSpark)
{ {
clientSpark.on('ready', function (data) clientSpark.on('ready', function (data)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -167,9 +167,7 @@ Collection.prototype.removeOldNodes = function()
for(var i = this._items.length - 1; i >= 0; i--) for(var i = this._items.length - 1; i >= 0; i--)
{ {
var node = this._items[i]; if( this._items[i].isInactiveAndOld() )
if( node.isInactiveAndOld() )
{ {
deleteList.push(i); deleteList.push(i);
} }

View File

@ -386,7 +386,7 @@ History.prototype.getMinersCount = function()
.value(); .value();
} }
History.prototype.getCharts = function() History.prototype.getCharts = function(callback)
{ {
var chartHistory = _( this._items ) var chartHistory = _( this._items )
.sortByOrder( 'height', false ) .sortByOrder( 'height', false )

View File

@ -4,7 +4,7 @@ var _ = require('lodash');
var MAX_HISTORY = 40; var MAX_HISTORY = 40;
var MAX_INACTIVE_TIME = 1000*60*60*4; var MAX_INACTIVE_TIME = 1000*60*60*4;
var Node = function Node(data) var Node = function(data)
{ {
this.id = null; this.id = null;
this.info = {}; this.info = {};
@ -17,28 +17,32 @@ var Node = function Node(data)
pending: 0, pending: 0,
gasPrice: 0, gasPrice: 0,
block: { block: {
difficulty: 0,
number: 0, number: 0,
hash: '0x0000000000000000000000000000000000000000000000000000000000000000',
difficulty: 0,
totalDifficulty: 0,
gasLimit: 0, gasLimit: 0,
timestamp: 0, timestamp: 0,
time: 0, time: 0,
arrival: 0, arrival: 0,
propagation: 0,
received: 0, received: 0,
propagation: 0,
transactions: [], transactions: [],
uncles: [] uncles: []
}, },
propagationAvg: 0, propagationAvg: 0,
latency: 0, latency: 0,
uptime: 0, uptime: 100
lastUpdate: 0
}; };
this.history = new Array(MAX_HISTORY); this.history = new Array(MAX_HISTORY);
this.uptime = { this.uptime = {
started: null, started: null,
history: [] up: 0,
down: 0,
lastStatus: null,
lastUpdate: null
}; };
this.init(data); this.init(data);
@ -50,21 +54,15 @@ Node.prototype.init = function(data)
{ {
_.fill(this.history, -1); _.fill(this.history, -1);
if( this.id === null ) if( this.id === null && this.uptime.started === null )
{
this.uptime.started = _.now();
this.setState(true); this.setState(true);
}
if( !_.isUndefined(data.id) ) this.id = _.result(data, 'id', this.id);
this.id = data.id;
this.setInfo(data);
if( !_.isUndefined(data.latency) ) if( !_.isUndefined(data.latency) )
this.stats.latency = data.latency; this.stats.latency = data.latency;
return this; this.setInfo(data);
} }
Node.prototype.setInfo = function(data) Node.prototype.setInfo = function(data)
@ -73,35 +71,26 @@ Node.prototype.setInfo = function(data)
{ {
this.info = data.info; this.info = data.info;
if( _.isUndefined(data.info.canUpdateHistory) ) if( !_.isUndefined(data.info.canUpdateHistory) )
data.info.canUpdateHistory = false; {
this.info.canUpdateHistory = _.result(data, 'info.canUpdateHistory', false);
}
} }
if( !_.isUndefined(data.ip) ) if( !_.isUndefined(data.ip) )
{ {
if(data.ip === '::ffff:127.0.0.1') this.setGeo(data.ip);
data.ip = '84.117.82.122';
this.info.ip = data.ip;
this.setGeo();
} }
if( !_.isUndefined(data.spark) ) this.spark = _.result(data, 'spark', null);
this.spark = data.spark;
var uptimeCnt = this.uptime.history.length;
if( uptimeCnt > 0 && this.uptime.history[uptimeCnt - 1].status === 'down' )
this.setState(true); this.setState(true);
return this;
} }
Node.prototype.setGeo = function() Node.prototype.setGeo = function(ip)
{ {
this.geo = geoip.lookup(this.info.ip); this.info.ip = ip;
this.geo = geoip.lookup(ip);
return this;
} }
Node.prototype.getInfo = function() Node.prototype.getInfo = function()
@ -109,31 +98,32 @@ Node.prototype.getInfo = function()
return { return {
id: this.id, id: this.id,
info: this.info, info: this.info,
geo: this.geo, stats: {
stats: this.stats, active: this.stats.active,
history: this.history mining: this.stats.mining,
hashrate: this.stats.hashrate,
peers: this.stats.peers,
gasPrice: this.stats.gasPrice,
block: this.stats.block,
propagationAvg: this.stats.propagationAvg,
uptime: this.stats.uptime,
latency: this.stats.latency,
pending: this.stats.pending,
},
history: this.history,
geo: this.geo
}; };
} }
Node.prototype.setStats = function(stats, history) Node.prototype.setStats = function(stats, history)
{ {
if( !_.isUndefined(stats) && !_.isUndefined(stats.block) && !_.isUndefined(stats.block.number) ) if( !_.isUndefined(stats) )
{ {
this.history = history; this.setBlock( _.result(stats, 'block', this.stats.block), history );
var positives = _.filter(history, function(p) { this.setBasicStats(stats);
return p >= 0;
});
if(positives.length > 0) this.setPending( _.result(stats, 'pending', this.stats.pending) );
stats.propagationAvg = Math.round( _.sum(positives) / positives.length );
else
stats.propagationAvg = 0;
if( !_.isUndefined(this.stats.latency) )
stats.latency = this.stats.latency;
this.stats = stats;
return this.getStats(); return this.getStats();
} }
@ -143,22 +133,14 @@ Node.prototype.setStats = function(stats, history)
Node.prototype.setBlock = function(block, history) Node.prototype.setBlock = function(block, history)
{ {
if( !_.isUndefined(block) && !_.isUndefined(block.number) ) if( !_.isUndefined(block) && !_.isUndefined(block.number) && ( !_.isEqual(history, this.history) || !_.isEqual(block, this.stats.block) ))
{
if(block.number !== this.stats.block.number && block.hash !== this.stats.block.hash)
{ {
this.history = history;
var propagationAvg = 0;
var positives = _.filter(history, function(p) {
return p >= 0;
});
if(positives.length > 0)
propagationAvg = Math.round( _.sum(positives) / positives.length );
else
propagationAvg = 0;
this.stats.block = block; this.stats.block = block;
this.stats.propagationAvg = propagationAvg; }
this.setHistory(history);
return this.getBlockStats(); return this.getBlockStats();
} }
@ -166,9 +148,36 @@ Node.prototype.setBlock = function(block, history)
return false; return false;
} }
Node.prototype.setHistory = function(history)
{
if( _.isEqual(history, this.history) )
{
return false;
}
if( !_.isArray(history) )
{
this.history = _.fill( new Array(MAX_HISTORY), -1 );
this.stats.propagationAvg = 0;
return true;
}
this.history = history;
var positives = _.filter(history, function(p) {
return p >= 0;
});
this.stats.propagationAvg = ( positives.length > 0 ? Math.round( _.sum(positives) / positives.length ) : 0 );
positives = null;
return true;
}
Node.prototype.setPending = function(stats) Node.prototype.setPending = function(stats)
{ {
if( !_.isUndefined(stats) && !_.isUndefined(stats.pending) ) if( !_.isUndefined(stats) && !_.isUndefined(stats.pending) && !_.isEqual(stats.pending, this.stats.pending))
{ {
this.stats.pending = stats.pending; this.stats.pending = stats.pending;
@ -183,7 +192,14 @@ Node.prototype.setPending = function(stats)
Node.prototype.setBasicStats = function(stats) Node.prototype.setBasicStats = function(stats)
{ {
if( !_.isUndefined(stats) ) if( !_.isUndefined(stats) && !_.isEqual(stats, {
active: this.stats.active,
mining: this.stats.mining,
hashrate: this.stats.hashrate,
peers: this.stats.peers,
gasPrice: this.stats.gasPrice,
uptime: this.stats.uptime
}) )
{ {
this.stats.active = stats.active; this.stats.active = stats.active;
this.stats.mining = stats.mining; this.stats.mining = stats.mining;
@ -200,7 +216,7 @@ Node.prototype.setBasicStats = function(stats)
Node.prototype.setLatency = function(latency) Node.prototype.setLatency = function(latency)
{ {
if( !_.isUndefined(latency) ) if( !_.isUndefined(latency) && !_.isEqual(latency, this.stats.latency) )
{ {
this.stats.latency = latency; this.stats.latency = latency;
@ -217,7 +233,17 @@ Node.prototype.getStats = function()
{ {
return { return {
id: this.id, id: this.id,
stats: this.stats, stats: {
active: this.stats.active,
mining: this.stats.mining,
hashrate: this.stats.hashrate,
peers: this.stats.peers,
gasPrice: this.stats.gasPrice,
block: this.stats.block,
propagationAvg: this.stats.propagationAvg,
uptime: this.stats.uptime,
pending: this.stats.pending
},
history: this.history history: this.history
}; };
} }
@ -249,11 +275,41 @@ Node.prototype.getBasicStats = function()
Node.prototype.setState = function(active) Node.prototype.setState = function(active)
{ {
var now = _.now();
if(this.uptime.started !== null)
{
if(this.uptime.lastStatus === active)
{
this.uptime[(active ? 'up' : 'down')] += now - this.uptime.lastUpdate;
}
else
{
this.uptime[(active ? 'down' : 'up')] += now - this.uptime.lastUpdate;
}
}
else
{
this.uptime.started = now;
}
this.stats.active = active; this.stats.active = active;
this.uptime.history.push({ this.uptime.lastStatus = active;
status: (active ? 'up' : 'down'), this.uptime.lastUpdate = now;
time: _.now()
}); this.stats.uptime = this.calculateUptime();
now = undefined;
}
Node.prototype.calculateUptime = function()
{
if(this.uptime.lastUpdate === this.uptime.started)
{
return 100;
}
return Math.round( this.uptime.up / (this.uptime.lastUpdate - this.uptime.started) * 100);
} }
Node.prototype.getBlockNumber = function() Node.prototype.getBlockNumber = function()
@ -268,14 +324,8 @@ Node.prototype.canUpdate = function()
Node.prototype.isInactiveAndOld = function() Node.prototype.isInactiveAndOld = function()
{ {
if(this.stats.active) if( this.uptime.lastStatus === false && this.uptime.lastUpdate !== null && (_.now() - this.uptime.lastUpdate) > MAX_INACTIVE_TIME )
return false;
var lastState = this.uptime.history[this.uptime.history.length -1];
if( !_.isUndefined(lastState) && !_.isUndefined(lastState.status) && !_.isUndefined(lastState.time) )
{ {
if( lastState.status === 'down' && (_.now() - lastState.time) > MAX_INACTIVE_TIME )
return true; return true;
} }

View File

@ -35,7 +35,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
$scope.latency = 0; $scope.latency = 0;
$scope.currentApiVersion = "0.0.12"; $scope.currentApiVersion = "0.0.13";
$scope.predicate = $localStorage.predicate || ['-pinned', '-stats.active', '-stats.block.number', 'stats.block.propagation']; $scope.predicate = $localStorage.predicate || ['-pinned', '-stats.active', '-stats.block.number', 'stats.block.propagation'];
$scope.reverse = $localStorage.reverse || false; $scope.reverse = $localStorage.reverse || false;
@ -134,14 +134,9 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
// console.log('Action: ', action); // console.log('Action: ', action);
// console.log('Data: ', data); // console.log('Data: ', data);
switch(action) { switch(action)
{
case "init": case "init":
var oldNodes = [];
if( $scope.nodes.length > 0 ){
oldNodes = $scope.nodes;
}
$scope.nodes = data; $scope.nodes = data;
_.forEach($scope.nodes, function (node, index) { _.forEach($scope.nodes, function (node, index) {
@ -161,8 +156,12 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
}); });
if( $scope.nodes.length > 0 ) if( $scope.nodes.length > 0 )
{
toastr['success']("Got nodes list", "Got nodes!"); toastr['success']("Got nodes list", "Got nodes!");
updateActiveNodes();
}
break; break;
case "add": case "add":
@ -170,8 +169,8 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
if( addNewNode(data) ) if( addNewNode(data) )
toastr['success']("New node "+ $scope.nodes[findIndex({id: data.id})].info.name +" connected!", "New node!"); toastr['success']("New node "+ $scope.nodes[findIndex({id: data.id})].info.name +" connected!", "New node!");
// else else
// toastr['info']("Node "+ $scope.nodes[index].info.name +" reconnected!", "Node is back!"); toastr['info']("Node "+ $scope.nodes[index].info.name +" reconnected!", "Node is back!");
break; break;
@ -203,6 +202,8 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
} }
$scope.nodes[index].stats = data.stats; $scope.nodes[index].stats = data.stats;
updateBestBlock();
} }
break; break;
@ -229,6 +230,8 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
$scope.nodes[index].stats.block = data.block; $scope.nodes[index].stats.block = data.block;
$scope.nodes[index].stats.propagationAvg = data.propagationAvg; $scope.nodes[index].stats.propagationAvg = data.propagationAvg;
updateBestBlock();
} }
break; break;
@ -261,6 +264,8 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
$scope.nodes[index].stats.peers = data.stats.peers; $scope.nodes[index].stats.peers = data.stats.peers;
$scope.nodes[index].stats.gasPrice = data.stats.gasPrice; $scope.nodes[index].stats.gasPrice = data.stats.gasPrice;
$scope.nodes[index].stats.uptime = data.stats.uptime; $scope.nodes[index].stats.uptime = data.stats.uptime;
updateActiveNodes();
} }
} }
@ -275,6 +280,8 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
if( _.isUndefined($scope.nodes[index].pinned) ) if( _.isUndefined($scope.nodes[index].pinned) )
$scope.nodes[index].pinned = false; $scope.nodes[index].pinned = false;
updateActiveNodes();
} }
break; break;
@ -338,7 +345,9 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
if( !_.isUndefined(data.stats) ) if( !_.isUndefined(data.stats) )
$scope.nodes[index].stats = data.stats; $scope.nodes[index].stats = data.stats;
// toastr['error']("Node "+ $scope.nodes[index].info.name +" went away!", "Node connection was lost!"); toastr['error']("Node "+ $scope.nodes[index].info.name +" went away!", "Node connection was lost!");
updateActiveNodes();
} }
break; break;
@ -351,9 +360,10 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
var node = $scope.nodes[index]; var node = $scope.nodes[index];
if( !_.isUndefined(node) && !_.isUndefined(node.stats) && !_.isUndefined(node.stats.latency) ) if( !_.isUndefined(node) && !_.isUndefined(node.stats) && !_.isUndefined(node.stats.latency) )
// console.log(data.latency); {
$scope.nodes[index].stats.latency = data.latency; $scope.nodes[index].stats.latency = data.latency;
} }
}
break; break;
@ -366,10 +376,7 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
break; break;
} }
if( action !== "latency" && action !== "pending" && action !== "client-ping" ) $scope.$apply();
{
updateStats();
}
} }
function findIndex(search) function findIndex(search)
@ -430,34 +437,21 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
$scope.nodes[index] = data; $scope.nodes[index] = data;
updateActiveNodes();
return false; return false;
} }
function updateStats() function updateActiveNodes()
{
if( $scope.nodes.length )
{ {
updateBestBlock();
$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) {
return parseInt(node.stats.block.number);
}).stats.block.number;
if( bestBlock > $scope.bestBlock )
{
$scope.bestBlock = bestBlock;
$scope.bestStats = _.max($scope.nodes, function (node) {
return parseInt(node.stats.block.number);
}).stats;
$scope.lastBlock = $scope.bestStats.block.arrived;
$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;
@ -481,8 +475,26 @@ netStatsApp.controller('StatsCtrl', function($scope, $filter, $localStorage, soc
longitude: 0 longitude: 0
}; };
}); });
}
// $scope.$apply(); function updateBestBlock()
{
if( $scope.nodes.length )
{
var bestBlock = _.max($scope.nodes, function (node) {
return parseInt(node.stats.block.number);
}).stats.block.number;
if( bestBlock > $scope.bestBlock )
{
$scope.bestBlock = bestBlock;
$scope.bestStats = _.max($scope.nodes, function (node) {
return parseInt(node.stats.block.number);
}).stats;
$scope.lastBlock = $scope.bestStats.block.arrived;
$scope.lastDifficulty = $scope.bestStats.block.difficulty;
}
} }
} }
}); });

View File

@ -536,7 +536,7 @@ angular.module('netStatsApp.filters', [])
return 'text-danger'; return 'text-danger';
if(compareVersions(info.client, '<', current)) if(compareVersions(info.client, '<', current))
return 'text-warning'; return 'text-danger';
return 'hidden'; return 'hidden';
}; };