Port everything related to stats from windshaft

This commit is contained in:
Raul Ochoa
2015-09-14 18:47:01 +02:00
parent 469b602484
commit 2d3088ba27
7 changed files with 239 additions and 33 deletions
+8 -2
View File
@@ -13,6 +13,10 @@ var NamedMapsCacheEntry = require('./cache/model/named_maps_entry');
var VarnishHttpCacheBackend = require('./cache/backend/varnish_http');
var FastlyCacheBackend = require('./cache/backend/fastly');
var StatsClient = require('./stats/client');
var Profiler = require('./stats/profiler_proxy');
var RendererStatsReporter = require('./stats/reporter/renderer');
var windshaft = require('windshaft');
var mapnik = windshaft.mapnik;
@@ -46,7 +50,7 @@ var timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, {encodin
module.exports = function(serverOptions) {
// Make stats client globally accessible
global.statsClient = windshaft.stats.Client.getInstance(serverOptions.statsd);
global.statsClient = StatsClient.getInstance(serverOptions.statsd);
var redisPool = new RedisPool(_.defaults(global.environment.redis, {
name: 'windshaft:server',
@@ -156,6 +160,8 @@ module.exports = function(serverOptions) {
statsInterval: 60000 // reports stats every milliseconds defined here
});
var rendererCache = new windshaft.cache.RendererCache(rendererFactory, rendererCacheOpts);
var rendererStatsReporter = new RendererStatsReporter(rendererCache, rendererCacheOpts.statsInterval);
rendererStatsReporter.start();
var attributesBackend = new windshaft.backend.Attributes(mapStore);
var previewBackend = new windshaft.backend.Preview(rendererCache);
@@ -472,7 +478,7 @@ function bootstrap(opts) {
app.use(function bootstrap$prepareRequestResponse(req, res, next) {
req.context = req.context || {};
req.profiler = new windshaft.stats.Profiler({
req.profiler = new Profiler({
statsd_client: global.statsClient,
profile: opts.useProfiler
});
+74
View File
@@ -0,0 +1,74 @@
var _ = require('underscore');
var debug = require('debug')('windshaft:stats_client');
var StatsD = require('node-statsd').StatsD;
module.exports = {
/**
* Returns an StatsD instance or an stub object that replicates the StatsD public interface so there is no need to
* keep checking if the stats_client is instantiated or not.
*
* The first call to this method implies all future calls will use the config specified in the very first call.
*
* TODO: It's far from ideal to use make this a singleton, improvement desired.
* We proceed this way to be able to use StatsD from several places sharing one single StatsD instance.
*
* @param config Configuration for StatsD, if undefined it will return an stub
* @returns {StatsD|Object}
*/
getInstance: function(config) {
if (!this.instance) {
var instance;
if (config) {
instance = new StatsD(config);
instance.last_error = { msg: '', count: 0 };
instance.socket.on('error', function (err) {
var last_err = instance.last_error;
var last_msg = last_err.msg;
var this_msg = '' + err;
if (this_msg !== last_msg) {
debug("statsd client socket error: " + err);
instance.last_error.count = 1;
instance.last_error.msg = this_msg;
} else {
++last_err.count;
if (!last_err.interval) {
instance.last_error.interval = setInterval(function () {
var count = instance.last_error.count;
if (count > 1) {
debug("last statsd client socket error repeated " + count + " times");
instance.last_error.count = 1;
//console.log("Clearing interval");
clearInterval(instance.last_error.interval);
instance.last_error.interval = null;
}
}, 1000);
}
}
});
} else {
var stubFunc = function (stat, value, sampleRate, callback) {
if (_.isFunction(callback)) {
callback(null, 0);
}
};
instance = {
timing: stubFunc,
increment: stubFunc,
decrement: stubFunc,
gauge: stubFunc,
unique: stubFunc,
set: stubFunc,
sendAll: stubFunc,
send: stubFunc
};
}
this.instance = instance;
}
return this.instance;
}
};
+53
View File
@@ -0,0 +1,53 @@
var Profiler = require('step-profiler');
/**
* Proxy to encapsulate node-step-profiler module so there is no need to check if there is an instance
*/
function ProfilerProxy(opts) {
this.profile = !!opts.profile;
this.profiler = null;
if (!!opts.profile) {
this.profiler = new Profiler({statsd_client: opts.statsd_client});
}
}
ProfilerProxy.prototype.done = function(what) {
if (this.profile) {
this.profiler.done(what);
}
};
ProfilerProxy.prototype.end = function() {
if (this.profile) {
this.profiler.end();
}
};
ProfilerProxy.prototype.start = function(what) {
if (this.profile) {
this.profiler.start(what);
}
};
ProfilerProxy.prototype.add = function(what) {
if (this.profile) {
this.profiler.add(what || {});
}
};
ProfilerProxy.prototype.sendStats = function() {
if (this.profile) {
this.profiler.sendStats();
}
};
ProfilerProxy.prototype.toString = function() {
return this.profile ? this.profiler.toString() : "";
};
ProfilerProxy.prototype.toJSONString = function() {
return this.profile ? this.profiler.toJSONString() : "{}";
};
module.exports = ProfilerProxy;
+70
View File
@@ -0,0 +1,70 @@
// - Reports stats about:
// * Total number of renderers
// * For mapnik renderers:
// - the mapnik-pool status: count, unused and waiting
// - the internally cached objects: png and grid
var _ = require('underscore');
function RendererStatsReporter(rendererCache, statsInterval) {
this.rendererCache = rendererCache;
this.statsInterval = statsInterval || 6e4;
this.renderersStatsIntervalId = null;
}
module.exports = RendererStatsReporter;
RendererStatsReporter.prototype.start = function() {
var self = this;
this.renderersStatsIntervalId = setInterval(function() {
var rendererCacheEntries = self.rendererCache.renderers;
if (!rendererCacheEntries) {
return null;
}
global.statsClient.gauge('windshaft.rendercache.count', _.keys(rendererCacheEntries).length);
var renderersStats = _.reduce(rendererCacheEntries, function(_rendererStats, cacheEntry) {
var stats = cacheEntry.renderer && cacheEntry.renderer.getStats && cacheEntry.renderer.getStats();
if (!stats) {
return _rendererStats;
}
_rendererStats.pool.count += stats.pool.count;
_rendererStats.pool.unused += stats.pool.unused;
_rendererStats.pool.waiting += stats.pool.waiting;
_rendererStats.cache.grid += stats.cache.grid;
_rendererStats.cache.png += stats.cache.png;
return _rendererStats;
},
{
pool: {
count: 0,
unused: 0,
waiting: 0
},
cache: {
png: 0,
grid: 0
}
}
);
global.statsClient.gauge('windshaft.mapnik-cache.png', renderersStats.cache.png);
global.statsClient.gauge('windshaft.mapnik-cache.grid', renderersStats.cache.grid);
global.statsClient.gauge('windshaft.mapnik-pool.count', renderersStats.pool.count);
global.statsClient.gauge('windshaft.mapnik-pool.unused', renderersStats.pool.unused);
global.statsClient.gauge('windshaft.mapnik-pool.waiting', renderersStats.pool.waiting);
}, this.statsInterval);
};
RendererStatsReporter.prototype.stop = function() {
clearInterval(this.renderersStatsIntervalId);
this.renderersStatsIntervalId = null;
};
+30 -30
View File
@@ -66,6 +66,18 @@
}
}
},
"debug": {
"version": "2.2.0",
"from": "debug@~2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"dependencies": {
"ms": {
"version": "0.7.1",
"from": "ms@0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
}
}
},
"dot": {
"version": "1.0.3",
"from": "dot@~1.0.2",
@@ -140,9 +152,9 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"process-nextick-args": {
"version": "1.0.2",
"version": "1.0.3",
"from": "process-nextick-args@~1.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz"
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.3.tgz"
},
"string_decoder": {
"version": "0.10.31",
@@ -255,9 +267,9 @@
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.0.tgz",
"dependencies": {
"hoek": {
"version": "2.14.0",
"version": "2.15.0",
"from": "hoek@2.x.x",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.14.0.tgz"
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.15.0.tgz"
},
"boom": {
"version": "2.8.0",
@@ -265,9 +277,9 @@
"resolved": "https://registry.npmjs.org/boom/-/boom-2.8.0.tgz"
},
"cryptiles": {
"version": "2.0.4",
"version": "2.0.5",
"from": "cryptiles@2.x.x",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.4.tgz"
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz"
},
"sntp": {
"version": "1.0.9",
@@ -309,9 +321,9 @@
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-1.8.0.tgz",
"dependencies": {
"bluebird": {
"version": "2.9.34",
"version": "2.10.0",
"from": "bluebird@^2.9.30",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz"
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.0.tgz"
},
"chalk": {
"version": "1.1.1",
@@ -470,6 +482,11 @@
"from": "lzma@~1.3.7",
"resolved": "https://registry.npmjs.org/lzma/-/lzma-1.3.7.tgz"
},
"node-statsd": {
"version": "0.0.7",
"from": "node-statsd@~0.0.7",
"resolved": "https://registry.npmjs.org/node-statsd/-/node-statsd-0.0.7.tgz"
},
"queue-async": {
"version": "1.0.7",
"from": "queue-async@~1.0.7",
@@ -518,6 +535,11 @@
"from": "step@~0.0.5",
"resolved": "https://registry.npmjs.org/step/-/step-0.0.6.tgz"
},
"step-profiler": {
"version": "0.2.1",
"from": "step-profiler@~0.2.1",
"resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.2.1.tgz"
},
"underscore": {
"version": "1.6.0",
"from": "underscore@~1.6.0",
@@ -2287,18 +2309,6 @@
}
}
},
"debug": {
"version": "2.2.0",
"from": "debug@~2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"dependencies": {
"ms": {
"version": "0.7.1",
"from": "ms@0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
}
}
},
"tilelive": {
"version": "4.5.3",
"from": "tilelive@~4.5.3",
@@ -2766,11 +2776,6 @@
}
}
},
"step-profiler": {
"version": "0.2.1",
"from": "step-profiler@~0.2.1",
"resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.2.1.tgz"
},
"torque.js": {
"version": "2.11.0",
"from": "torque.js@~2.11.0",
@@ -2959,11 +2964,6 @@
"version": "1.0.2",
"from": "sphericalmercator@1.0.2",
"resolved": "https://registry.npmjs.org/sphericalmercator/-/sphericalmercator-1.0.2.tgz"
},
"node-statsd": {
"version": "0.0.7",
"from": "node-statsd@~0.0.7",
"resolved": "https://registry.npmjs.org/node-statsd/-/node-statsd-0.0.7.tgz"
}
}
}
+3
View File
@@ -23,6 +23,9 @@
],
"dependencies": {
"express": "~2.5.11",
"debug": "~2.2.0",
"step-profiler": "~0.2.1",
"node-statsd": "~0.0.7",
"underscore" : "~1.6.0",
"dot": "~1.0.2",
"windshaft": "https://github.com/CartoDB/Windshaft/tarball/backend-foundations",
+1 -1
View File
@@ -3,7 +3,7 @@ require('../../../support/test_helper.js');
var assert = require('assert');
var cartodbServer = require('../../../../lib/cartodb/server');
var serverOptions = require('../../../../lib/cartodb/server_options');
var StatsClient = require('windshaft').stats.Client;
var StatsClient = require('../../../../lib/cartodb/stats/client');
var LayergroupController = require('../../../../lib/cartodb/controllers/layergroup');