diff --git a/lib/cartodb/api/api-router.js b/lib/cartodb/api/api-router.js index 60946a78..fb62aa46 100644 --- a/lib/cartodb/api/api-router.js +++ b/lib/cartodb/api/api-router.js @@ -29,6 +29,7 @@ const VarnishHttpCacheBackend = require('../cache/backend/varnish_http'); const FastlyCacheBackend = require('../cache/backend/fastly'); const NamedMapProviderCache = require('../cache/named_map_provider_cache'); const NamedMapsCacheEntry = require('../cache/model/named_maps_entry'); +const NamedMapProviderReporter = require('../stats/reporter/named-map-provider'); const SqlWrapMapConfigAdapter = require('../models/mapconfig/adapter/sql-wrap-mapconfig-adapter'); const MapConfigNamedLayersAdapter = require('../models/mapconfig/adapter/mapconfig-named-layers-adapter'); @@ -161,6 +162,13 @@ module.exports = class ApiRouter { layergroupAffectedTablesCache ); + const namedMapProviderReporter = new NamedMapProviderReporter({ + namedMapProviderCache, + intervalInMilliseconds: rendererCacheOpts.statsInterval + }); + + namedMapProviderReporter.start(); + const collaborators = { analysisStatusBackend, attributesBackend, diff --git a/lib/cartodb/stats/reporter/named-map-provider.js b/lib/cartodb/stats/reporter/named-map-provider.js new file mode 100644 index 00000000..c115b716 --- /dev/null +++ b/lib/cartodb/stats/reporter/named-map-provider.js @@ -0,0 +1,33 @@ +'use strict'; + +const statKeyTemplate = ctx => `windshaft.named-map-provider-cache.${ctx.metric}`; + +module.exports = class NamedMapProviderReporter { + constructor ({ namedMapProviderCache, intervalInMilliseconds } = {}) { + this.namedMapProviderCache = namedMapProviderCache; + this.intervalInMilliseconds = intervalInMilliseconds; + this.intervalId = null; + } + + start () { + const { providerCache: cache } = this.namedMapProviderCache; + const { statsClient: stats } = global; + + this.intervalId = setInterval(() => { + stats.gauge(statKeyTemplate({ metric: 'named-map.count' }), cache.length); + const providers = cache.dump(); + + const namedMapIntantiations = providers.reduce((acc, { v: providers }) => { + acc += Object.keys(providers).length; + return acc; + }, 0); + + stats.gauge(statKeyTemplate({ metric: 'named-map.intantiation.count' }), namedMapIntantiations); + }, this.intervalInMilliseconds); + } + + stop () { + clearInterval(this.intervalId); + this.intervalId = null; + } +}; diff --git a/test/unit/cartodb/stats/reporter/named-map-provider.js b/test/unit/cartodb/stats/reporter/named-map-provider.js new file mode 100644 index 00000000..551a1c21 --- /dev/null +++ b/test/unit/cartodb/stats/reporter/named-map-provider.js @@ -0,0 +1,61 @@ +'use strict'; + +const assert = require('assert'); +const NamedMapProviderReporter = require('../../../../../lib/cartodb/stats/reporter/named-map-provider'); + +describe('named-map-provider-reporter', function () { + it('should report metrics every 100 ms', function (done) { + const oldStatsClient = global.statsClient; + + global.statsClient = { + gauge: function (metric, value) { + this[metric] = value; + } + }; + + const dummyCacheEntries = [ + { + k: 'foo:template_1', + v: { 'instantiation_1': 1 } + }, + { + k: 'bar:template_2', + v: { 'instantiation_1': 1, 'instantiation_2': 2 } + }, + { + k: 'buz:template_3', + v: { 'instantiation_1': 1, 'instantiation_2': 2, 'instantiation_3': 3 } + } + ]; + + const reporter = new NamedMapProviderReporter({ + namedMapProviderCache: { + providerCache: { + dump: () => dummyCacheEntries, + length: dummyCacheEntries.length + } + }, + intervalInMilliseconds: 100 + }); + + reporter.start(); + + setTimeout(() => { + reporter.stop(); + + assert.strictEqual( + global.statsClient['windshaft.named-map-provider-cache.named-map.count'], + 3 + ); + + assert.strictEqual( + global.statsClient['windshaft.named-map-provider-cache.named-map.intantiation.count'], + 6 + ); + + global.statsClient = oldStatsClient; + + done(); + }, 110); + }); +});