z-nomp/libs/stats.js

284 lines
10 KiB
JavaScript
Raw Normal View History

var zlib = require('zlib');
2014-03-19 13:24:29 -07:00
var redis = require('redis');
var async = require('async');
2014-03-19 13:24:29 -07:00
var os = require('os');
var algos = require('stratum-pool/lib/algoProperties.js');
2014-03-19 13:24:29 -07:00
module.exports = function(logger, portalConfig, poolConfigs){
var _this = this;
var logSystem = 'Stats';
2014-03-19 13:24:29 -07:00
var redisClients = [];
var redisStats;
this.statHistory = [];
this.statPoolHistory = [];
this.stats = {};
this.statsString = '';
setupStatsRedis();
gatherStatHistory();
2014-03-19 13:24:29 -07:00
var canDoStats = true;
2014-03-19 13:24:29 -07:00
Object.keys(poolConfigs).forEach(function(coin){
if (!canDoStats) return;
2014-03-19 13:24:29 -07:00
var poolConfig = poolConfigs[coin];
var redisConfig = poolConfig.redis;
2014-03-19 13:24:29 -07:00
for (var i = 0; i < redisClients.length; i++){
var client = redisClients[i];
if (client.client.port === redisConfig.port && client.client.host === redisConfig.host){
client.coins.push(coin);
return;
}
}
redisClients.push({
coins: [coin],
client: redis.createClient(redisConfig.port, redisConfig.host)
});
});
function setupStatsRedis(){
redisStats = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);
redisStats.on('error', function(err){
logger.error(logSystem, 'Historics', 'Redis for stats had an error ' + JSON.stringify(err));
});
}
function gatherStatHistory(){
var retentionTime = (((Date.now() / 1000) - portalConfig.website.stats.historicalRetention) | 0).toString();
redisStats.zrangebyscore(['statHistory', retentionTime, '+inf'], function(err, replies){
if (err) {
logger.error(logSystem, 'Historics', 'Error when trying to grab historical stats ' + JSON.stringify(err));
return;
}
for (var i = 0; i < replies.length; i++){
_this.statHistory.push(JSON.parse(replies[i]));
}
_this.statHistory = _this.statHistory.sort(function(a, b){
return a.time - b.time;
});
_this.statHistory.forEach(function(stats){
addStatPoolHistory(stats);
});
});
}
function addStatPoolHistory(stats){
var data = {
time: stats.time,
pools: {}
};
for (var pool in stats.pools){
data.pools[pool] = {
hashrate: stats.pools[pool].hashrate,
workerCount: stats.pools[pool].workerCount,
blocks: stats.pools[pool].blocks
}
}
_this.statPoolHistory.push(data);
}
2014-03-19 13:24:29 -07:00
this.getGlobalStats = function(callback){
2014-03-19 13:24:29 -07:00
var statGatherTime = Date.now() / 1000 | 0;
2014-03-22 12:58:51 -07:00
var allCoinStats = {};
2014-03-19 13:24:29 -07:00
async.each(redisClients, function(client, callback){
var windowTime = (((Date.now() / 1000) - portalConfig.website.stats.hashrateWindow) | 0).toString();
2014-03-19 13:24:29 -07:00
var redisCommands = [];
var redisCommandTemplates = [
['zremrangebyscore', ':hashrate', '-inf', '(' + windowTime],
['zrangebyscore', ':hashrate', windowTime, '+inf'],
['hgetall', ':stats'],
['scard', ':blocksPending'],
['scard', ':blocksConfirmed'],
2016-12-06 15:56:34 -08:00
['scard', ':blocksKicked']
2014-03-22 12:58:51 -07:00
];
var commandsPerCoin = redisCommandTemplates.length;
2014-03-22 12:58:51 -07:00
client.coins.map(function(coin){
redisCommandTemplates.map(function(t){
2014-03-22 12:58:51 -07:00
var clonedTemplates = t.slice(0);
clonedTemplates[1] = coin + clonedTemplates[1];
2014-03-22 12:58:51 -07:00
redisCommands.push(clonedTemplates);
});
});
2014-04-02 14:42:50 -07:00
2014-03-19 13:24:29 -07:00
client.client.multi(redisCommands).exec(function(err, replies){
if (err){
logger.error(logSystem, 'Global', 'error with getting global stats ' + JSON.stringify(err));
2014-03-19 13:24:29 -07:00
callback(err);
}
else{
for(var i = 0; i < replies.length; i += commandsPerCoin){
2014-03-22 12:58:51 -07:00
var coinName = client.coins[i / commandsPerCoin | 0];
2014-03-19 13:24:29 -07:00
var coinStats = {
2014-03-22 12:58:51 -07:00
name: coinName,
symbol: poolConfigs[coinName].coin.symbol.toUpperCase(),
2014-03-22 12:58:51 -07:00
algorithm: poolConfigs[coinName].coin.algorithm,
2014-03-19 13:24:29 -07:00
hashrates: replies[i + 1],
poolStats: {
validShares: replies[i + 2] ? (replies[i + 2].validShares || 0) : 0,
validBlocks: replies[i + 2] ? (replies[i + 2].validBlocks || 0) : 0,
2014-05-07 02:45:25 -07:00
invalidShares: replies[i + 2] ? (replies[i + 2].invalidShares || 0) : 0,
totalPaid: replies[i + 2] ? (replies[i + 2].totalPaid || 0) : 0
},
2014-03-22 12:58:51 -07:00
blocks: {
pending: replies[i + 3],
confirmed: replies[i + 4],
orphaned: replies[i + 5]
}
2014-03-19 13:24:29 -07:00
};
2014-03-22 12:58:51 -07:00
allCoinStats[coinStats.name] = (coinStats);
2014-03-19 13:24:29 -07:00
}
callback();
}
});
}, function(err){
if (err){
logger.error(logSystem, 'Global', 'error getting all stats' + JSON.stringify(err));
2014-03-19 13:24:29 -07:00
callback();
return;
}
var portalStats = {
time: statGatherTime,
2014-03-19 13:24:29 -07:00
global:{
workers: 0,
hashrate: 0
},
algos: {},
2014-03-19 13:24:29 -07:00
pools: allCoinStats
};
2014-03-22 12:58:51 -07:00
Object.keys(allCoinStats).forEach(function(coin){
var coinStats = allCoinStats[coin];
2014-03-19 13:24:29 -07:00
coinStats.workers = {};
coinStats.shares = 0;
coinStats.hashrates.forEach(function(ins){
var parts = ins.split(':');
var workerShares = parseFloat(parts[0]);
2014-03-19 13:24:29 -07:00
var worker = parts[1];
2014-05-13 08:30:54 -07:00
if (workerShares > 0) {
coinStats.shares += workerShares;
if (worker in coinStats.workers)
coinStats.workers[worker].shares += workerShares;
else
coinStats.workers[worker] = {
shares: workerShares,
invalidshares: 0,
hashrateString: null
};
}
else {
if (worker in coinStats.workers)
coinStats.workers[worker].invalidshares -= workerShares; // workerShares is negative number!
else
coinStats.workers[worker] = {
shares: 0,
invalidshares: -workerShares,
hashrateString: null
};
}
2014-03-19 13:24:29 -07:00
});
2014-04-23 11:53:19 -07:00
2014-04-20 11:04:41 -07:00
var shareMultiplier = Math.pow(2, 32) / algos[coinStats.algorithm].multiplier;
2014-04-23 11:53:19 -07:00
coinStats.hashrate = shareMultiplier * coinStats.shares / portalConfig.website.stats.hashrateWindow;
coinStats.workerCount = Object.keys(coinStats.workers).length;
portalStats.global.workers += coinStats.workerCount;
2014-04-02 14:42:50 -07:00
/* algorithm specific global stats */
var algo = coinStats.algorithm;
if (!portalStats.algos.hasOwnProperty(algo)){
portalStats.algos[algo] = {
workers: 0,
hashrate: 0,
hashrateString: null
};
}
2014-04-02 14:42:50 -07:00
portalStats.algos[algo].hashrate += coinStats.hashrate;
portalStats.algos[algo].workers += Object.keys(coinStats.workers).length;
2014-05-13 08:30:54 -07:00
for (var worker in coinStats.workers) {
coinStats.workers[worker].hashrateString = _this.getReadableHashRateString(shareMultiplier * coinStats.workers[worker].shares / portalConfig.website.stats.hashrateWindow);
}
delete coinStats.hashrates;
delete coinStats.shares;
coinStats.hashrateString = _this.getReadableHashRateString(coinStats.hashrate);
});
Object.keys(portalStats.algos).forEach(function(algo){
var algoStats = portalStats.algos[algo];
algoStats.hashrateString = _this.getReadableHashRateString(algoStats.hashrate);
2014-03-19 13:24:29 -07:00
});
2014-03-22 12:58:51 -07:00
2014-03-19 13:24:29 -07:00
_this.stats = portalStats;
2014-03-22 12:58:51 -07:00
_this.statsString = JSON.stringify(portalStats);
_this.statHistory.push(portalStats);
addStatPoolHistory(portalStats);
var retentionTime = (((Date.now() / 1000) - portalConfig.website.stats.historicalRetention) | 0);
for (var i = 0; i < _this.statHistory.length; i++){
if (retentionTime < _this.statHistory[i].time){
if (i > 0) {
_this.statHistory = _this.statHistory.slice(i);
_this.statPoolHistory = _this.statPoolHistory.slice(i);
}
break;
}
}
redisStats.multi([
['zadd', 'statHistory', statGatherTime, _this.statsString],
['zremrangebyscore', 'statHistory', '-inf', '(' + retentionTime]
]).exec(function(err, replies){
if (err)
logger.error(logSystem, 'Historics', 'Error adding stats to historics ' + JSON.stringify(err));
});
2014-03-19 13:24:29 -07:00
callback();
});
};
this.getReadableHashRateString = function(hashrate){
var i = -1;
2016-12-04 09:34:10 -08:00
var byteUnits = [ ' Sol',' KSol', ' MSol', ' GSol', ' TSol', ' PSol' ];
do {
2016-12-04 09:34:10 -08:00
hashrate = hashrate / 100000;
i++;
} while (hashrate > 1000);
return hashrate.toFixed(2) + byteUnits[i];
};
2014-03-19 13:24:29 -07:00
};