Merge pull request #105 from cubedro/develop

Made stats calls async
This commit is contained in:
Marian OANCΞA 2015-05-05 03:06:53 +03:00
commit 025159a72b
2 changed files with 261 additions and 152 deletions

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
var web3 = require('web3'); var web3 = require('web3');
var async = require('async');
var _ = require('lodash'); var _ = require('lodash');
var os = require('os'); var os = require('os');
var shelljs = require('shelljs'); var shelljs = require('shelljs');
@ -78,6 +79,7 @@ function Node()
}; };
this._lastStats = JSON.stringify(this.stats); this._lastStats = JSON.stringify(this.stats);
this._lastFetch = 0;
this._tries = 0; this._tries = 0;
this._down = 0; this._down = 0;
@ -97,7 +99,7 @@ function Node()
this.pingInterval = false; this.pingInterval = false;
this.connectionInterval = false; this.connectionInterval = false;
this._lastLatestLog = 0; this._lastChainLog = 0;
this._lastPendingLog = 0; this._lastPendingLog = 0;
this._called = 0 this._called = 0
@ -175,9 +177,10 @@ Node.prototype.setupSockets = function()
.on('ready', function() .on('ready', function()
{ {
self._socket = true; self._socket = true;
self.sendUpdate(true);
console.info('==> The connection has been established.'); console.info('==> The connection has been established.');
self.updateBlock();
self.update(true);
}) })
.on('data', function incoming(data) .on('data', function incoming(data)
{ {
@ -214,7 +217,7 @@ Node.prototype.setupSockets = function()
}) })
.on('reconnecting', function reconnecting(opts) .on('reconnecting', function reconnecting(opts)
{ {
console.warn('We are scheduling a reconnect operation', opts); console.warn('!!!', 'We are scheduling a reconnect operation', opts);
}); });
} }
@ -231,36 +234,26 @@ Node.prototype.emit = function(message, payload)
} }
} }
Node.prototype.isActive = function() Node.prototype.setInactive = function()
{ {
this._tries++;
try {
var peers = web3.toDecimal(web3.net.peerCount);
if(peers !== null)
{
this.stats.peers = peers;
this.stats.active = true;
return true;
}
}
catch (err) {
console.error("peerCount:", err);
}
this.stats.active = false; this.stats.active = false;
this.stats.listening = false;
this.stats.mining = false;
this.stats.peers = 0; this.stats.peers = 0;
this.stats.pending = 0;
this.stats.mining = false;
this.stats.hashrate = 0;
this.stats.gasPrice = 0;
this.stats.miner = false;
this._down++; this._down++;
return false; return this;
} }
Node.prototype.getInfo = function() Node.prototype.getInfo = function()
{ {
var start = _.now();
console.info('==>', 'Getting info');
try { try {
this.info.coinbase = web3.eth.coinbase; this.info.coinbase = web3.eth.coinbase;
this.info.node = web3.version.client; this.info.node = web3.version.client;
@ -268,7 +261,8 @@ Node.prototype.getInfo = function()
this.info.protocol = web3.toDecimal(web3.version.ethereum); this.info.protocol = web3.toDecimal(web3.version.ethereum);
this.info.api = web3.version.api; this.info.api = web3.version.api;
console.info(this.info); console.info('==>', 'Got info in', _.now() - start, 'ms');
console.info(' i ', this.info);
return true; return true;
} }
@ -285,7 +279,8 @@ Node.prototype.getBlock = function(number)
number: 0, number: 0,
hash: '?', hash: '?',
difficulty: 0, difficulty: 0,
timestamp: 0 timestamp: 0,
miner: ''
}; };
if( _.isUndefined(number) ) if( _.isUndefined(number) )
@ -294,30 +289,16 @@ Node.prototype.getBlock = function(number)
try { try {
block = web3.eth.getBlock(number, true); block = web3.eth.getBlock(number, true);
if( block.hash != '?' && !_.isUndefined(block.difficulty) ) if( block.hash != '?' && !_.isUndefined(block.difficulty) && !_.isUndefined(block.totalDifficulty) )
{ {
block.difficulty = web3.toDecimal( block.difficulty ); block.difficulty = web3.toDecimal( block.difficulty );
block.totalDifficulty = web3.toDecimal( block.totalDifficulty );
} }
} }
catch (err) { catch (err) {
console.error("getBlock(" + number + "):", err); console.error("getBlock(" + number + "):", err);
if(number > 0) return false;
{
try {
number--;
block = web3.eth.getBlock(number, true);
if(block.hash !== '?' && !_.isUndefined(block.difficulty) )
{
block.difficulty = web3.toDecimal( block.difficulty );
}
}
catch (err) {
console.error("getBlock(" + number + "):", err);
}
}
} }
return block; return block;
@ -325,26 +306,26 @@ Node.prototype.getBlock = function(number)
Node.prototype.getMinerName = function(miner) Node.prototype.getMinerName = function(miner)
{ {
var result = _.find(this._knownMiners, {miner: miner}); var result = _.find(this._knownMiners, { miner: miner });
if(result !== undefined) if (result !== undefined)
{ {
return result.name; return result.name;
} }
else else
{ {
if(this._Registrar !== null) if (this._Registrar !== null)
{ {
var name = this._Registrar.name(miner); var name = this._Registrar.name(miner);
if(name.length > 0) if(name.length > 0)
{ {
this._knownMiners.push({miner: miner, name: name}); this._knownMiners.push({ miner: miner, name: name });
return name; return name;
} }
} }
this._knownMiners.push({miner: miner, name: false}); this._knownMiners.push({ miner: miner, name: false });
} }
return false; return false;
@ -355,74 +336,182 @@ Node.prototype.uptime = function()
this.stats.uptime = ((this._tries - this._down) / this._tries) * 100; this.stats.uptime = ((this._tries - this._down) / this._tries) * 100;
} }
Node.prototype.getStats = function() Node.prototype.getStats = function(forced)
{
var self = this;
var now = _.now();
var lastFetchAgo = now - this._lastFetch;
this._lastFetch = now;
if (this._socket)
this._lastStats = JSON.stringify(this.stats);
if (this._web3 && (lastFetchAgo >= UPDATE_INTERVAL || forced === true))
{
console.log('==> Getting stats; last update:', lastFetchAgo, '- forced:', (forced === true));
async.parallel({
start: function (callback)
{
callback(null, _.now());
},
peers: function (callback)
{
async.nextTick(function () {
var peers;
try {
peers = web3.toDecimal(web3.net.peerCount);
}
catch (err) {
console.error('xx> PeerCount failed: ', err);
callback(err, null);
}
callback(null, peers);
});
},
pending: function (callback)
{
async.nextTick(function () {
try {
web3.eth.getBlockTransactionCount('pending', callback);
}
catch (err) {
console.error('xx> Pending failed: ', err);
callback(err, null);
}
});
},
mining: function (callback)
{
async.nextTick(function () {
var mining;
try {
mining = web3.eth.mining;
}
catch (err) {
console.error('xx> Mining failed: ', err);
callback(err, null);
}
callback(null, mining);
});
},
hashrate: function (callback)
{
async.nextTick(function () {
var hashrate;
try {
hashrate = web3.eth.hashrate;
}
catch (err) {
console.error('xx> Hashrate failed: ', err);
callback(err, null);
}
callback(null, hashrate);
});
},
gasPrice: function (callback)
{
async.nextTick(function () {
var gasPrice;
try {
gasPrice = web3.toBigNumber(web3.eth.gasPrice).toString(10);
}
catch (err) {
console.error('xx> gasPrice failed: ', err);
callback(err, null);
}
callback(null, gasPrice);
});
},
minerName: function (callback)
{
async.nextTick(function () {
var minerName;
try {
minerName = self.getMinerName(self.stats.block.miner);
}
catch (err) {
console.error('xx> minerName failed: ', err);
callback(err, null);
}
callback(null, minerName);
});
}
},
function (err, results)
{
self._tries++;
if (err) {
console.error('xx> getStats error: ', err);
self.setInactive();
return false;
}
results.end = _.now();
results.diff = results.end - results.start;
// console.log('==> Got getStats results: ', results);
console.log('==> Got getStats results in', results.diff, 'ms');
if(results.peers !== null)
{
self.stats.active = true;
self.stats.peers = results.peers;
self.stats.pending = results.pending;
self.stats.mining = results.mining;
self.stats.hashrate = results.hashrate;
self.stats.gasPrice = results.gasPrice;
}
else {
self.setInactive();
}
self.uptime();
self.sendUpdate();
});
}
}
Node.prototype.getStatsBlock = function ()
{ {
var start = _.now();
console.log('==> statsstart: ', start);
if(this._socket) if(this._socket)
this._lastStats = JSON.stringify(this.stats); this._lastStats = JSON.stringify(this.stats);
if(this._web3) if(this._web3)
{ {
var blockStart = _.now(); var start = _.now();
console.log('==> blockstart: ', blockStart);
console.log('==> blockstart diff: ', (blockStart - start));
var block = this.getBlock(); var block = this.getBlock();
var blockEnd = _.now(); var end = _.now();
console.log('==> blockend: ', blockEnd);
console.log('==> block diff: ', (blockEnd - blockStart));
if( !_.isUndefined(block) && !_.isUndefined(block.number) && !_.isUndefined(block.hash) && block.hash !== '?' ) if( !_.isUndefined(block) && !_.isUndefined(block.number) && !_.isUndefined(block.hash) && block.hash !== '?' )
{ {
console.log("==> Got block:", block.number, 'in', end - start, 'ms');
this.stats.block = block; this.stats.block = block;
this.sendUpdate();
this.isActive();
if(PENDING_WORKS) {
try
{
this.stats.pending = web3.eth.getBlockTransactionCount('pending');
}
catch (err)
{
PENDING_WORKS = false;
console.error("getBlockTransactionCount('pending'):", err);
}
}
this.stats.mining = web3.eth.mining;
if(this.stats.mining)
{
try {
this.stats.hashrate = web3.eth.hashrate;
}
catch (err)
{
console.error('hashrate: ', err);
this.stats.hashrate = 0;
}
}
else
this.stats.hashrate = 0;
this.stats.gasPrice = web3.toBigNumber(web3.eth.gasPrice).toString(10);
} }
else else
{ {
console.error("getStats: couldn't fetch block..."); console.error("xx> getStatsBlock: couldn't fetch block...");
} }
} }
this.uptime(); this.sendUpdate();
var end = _.now();
console.log('==> end: ', end);
console.log('==> stats diff: ', (end - start));
} }
Node.prototype.getHistory = function(range) Node.prototype.getHistory = function (range)
{ {
var history = []; var history = [];
var interv = {}; var interv = {};
@ -443,7 +532,7 @@ Node.prototype.getHistory = function(range)
}; };
} }
for(var i = interv.min; i <= interv.max; i++) for (var i = interv.min; i <= interv.max; i++)
{ {
var block = this.getBlock(( !_.isUndefined(range.list) ? range.list[i] : i)); var block = this.getBlock(( !_.isUndefined(range.list) ? range.list[i] : i));
@ -456,21 +545,28 @@ Node.prototype.getHistory = function(range)
return history.reverse(); return history.reverse();
} }
Node.prototype.changed = function() Node.prototype.updateBlock = function()
{
this.getStatsBlock();
return this;
};
Node.prototype.update = function(forced)
{
this.getStats(forced);
return this;
};
Node.prototype.changed = function ()
{ {
var changed = ! _.isEqual( this._lastStats, JSON.stringify(this.stats) ); var changed = ! _.isEqual( this._lastStats, JSON.stringify(this.stats) );
if(this._tries - this._lastSent > 5)
{
this._lastSent = this._tries;
return true;
}
return changed; return changed;
} }
Node.prototype.prepareStats = function() Node.prototype.prepareStats = function ()
{ {
return { return {
id: this.id, id: this.id,
@ -478,36 +574,12 @@ Node.prototype.prepareStats = function()
}; };
} }
Node.prototype.sendUpdate = function(force) Node.prototype.sendUpdate = function (force)
{ {
if( this.changed() || force ) if( this.changed() || force )
this.emit('update', this.prepareStats()); this.emit('update', this.prepareStats());
} }
Node.prototype.update = function()
{
this.getStats();
this.sendUpdate();
return this.stats;
};
Node.prototype.updatePending = function()
{
if(PENDING_WORKS)
{
try {
this.stats.pending = web3.eth.getBlockTransactionCount('pending');
this.sendUpdate();
}
catch (err) {
PENDING_WORKS = false;
console.error("getBlockTransactionCount('pending'):", err);
}
}
}
Node.prototype.ping = function() Node.prototype.ping = function()
{ {
this._latency = _.now(); this._latency = _.now();
@ -519,25 +591,52 @@ Node.prototype.setWatches = function()
var self = this; var self = this;
try { try {
this.pendingFilter = web3.eth.filter('chain'); this.chainFilter = web3.eth.filter('chain');
this.chainFilter.watch( function (log)
{
var now = _.now();
var time = now - self._lastChainLog;
self._lastChainLog = now;
console.log('>>> Chain Filter triggered: ', now, '- last trigger:', time);
if(time > 50)
{
self.updateBlock();
}
else
{
debounce(function() {
self.updateBlock();
}, 50);
}
});
}
catch (err)
{
console.error("Couldn't set up chain filter");
console.error(err);
}
try {
this.pendingFilter = web3.eth.filter('pending');
this.pendingFilter.watch( function (log) this.pendingFilter.watch( function (log)
{ {
if(PENDING_WORKS) { var now = _.now();
var now = _.now(); var time = now - self._lastPendingLog;
var time = now - self._lastPendingLog; self._lastPendingLog = now;
if(time > 50) console.log('>>> Pending Filter triggered: ', now, '- last trigger:', time);
{
self.update();
}
else
{
debounce(function() {
self.updatePending();
}, 50);
}
self._lastPendingLog = now; if(time > 50)
{
self.update(true);
}
else
{
debounce(function() {
self.update(true);
}, 50);
} }
}); });
} }
@ -558,24 +657,34 @@ Node.prototype.setWatches = function()
Node.prototype.installContract = function() Node.prototype.installContract = function()
{ {
var start = _.now();
try { try {
Contract = web3.eth.contract( registrar.desc ); Contract = web3.eth.contract( registrar.desc );
this._Registrar = new Contract( registrar.address ); this._Registrar = new Contract( registrar.address );
console.log('==>', 'Installed Registrar contract in', _.now() - start, 'ms');
} }
catch (err) catch (err)
{ {
console.error("Couldn't set up registrar contract"); console.error("!!!", "Couldn't set up registrar contract");
console.error(err); console.error(err);
} }
} }
Node.prototype.init = function() Node.prototype.init = function()
{ {
// Fetch node info
this.getInfo(); this.getInfo();
this.startSocketConnection();
// Install Registrar contract
this.installContract(); this.installContract();
// Start socket connection
this.startSocketConnection();
// Set filters
this.setWatches(); this.setWatches();
this.update();
} }
Node.prototype.stop = function() Node.prototype.stop = function()
@ -583,7 +692,6 @@ Node.prototype.stop = function()
if(this._socket) if(this._socket)
socket.end(); socket.end();
if(this.updateInterval) if(this.updateInterval)
clearInterval(this.updateInterval); clearInterval(this.updateInterval);

View File

@ -8,6 +8,7 @@
"lib": "./lib" "lib": "./lib"
}, },
"dependencies": { "dependencies": {
"async": "^0.9.0",
"debounce": "1.0.0", "debounce": "1.0.0",
"debug": "2.1.3", "debug": "2.1.3",
"lodash": "3.7.0", "lodash": "3.7.0",