Remove mechanism to reset named map's provider as, in the end, it's reading from storage (redis) always so cache isn't doing its job. There is already a mechanism to invalidate cache entry when a template is modified (see template-maps emits on "update" and "delete", and listeners attached at server startup)

This commit is contained in:
Daniel García Aubert 2019-09-12 20:32:52 +02:00
parent 9d6726227a
commit 6229455d25
2 changed files with 96 additions and 68 deletions

View File

@ -4,7 +4,6 @@ var _ = require('underscore');
var dot = require('dot'); var dot = require('dot');
var NamedMapMapConfigProvider = require('../models/mapconfig/provider/named-map-provider'); var NamedMapMapConfigProvider = require('../models/mapconfig/provider/named-map-provider');
var templateName = require('../backends/template_maps').templateName; var templateName = require('../backends/template_maps').templateName;
var queue = require('queue-async');
var LruCache = require("lru-cache"); var LruCache = require("lru-cache");
@ -57,23 +56,7 @@ NamedMapProviderCache.prototype.get = function(user, templateId, config, authTok
var namedMapProvider = namedMapProviders[providerKey]; var namedMapProvider = namedMapProviders[providerKey];
var self = this; return callback(null, namedMapProvider);
queue(2)
.defer(namedMapProvider.getTemplate.bind(namedMapProvider))
.defer(this.templateMaps.getTemplate.bind(this.templateMaps), user, templateId)
.awaitAll(function templatesQueueDone(err, results) {
if (err) {
return callback(err);
}
// We want to reset provider its template has changed
// Ideally this should be done in a passive mode where this cache gets notified of template changes
var uniqueFingerprints = _.uniq(results.map(self.templateMaps.fingerPrint)).length;
if (uniqueFingerprints > 1) {
namedMapProvider.reset();
}
return callback(null, namedMapProvider);
});
}; };
NamedMapProviderCache.prototype.invalidate = function(user, templateId) { NamedMapProviderCache.prototype.invalidate = function(user, templateId) {

View File

@ -1,13 +1,12 @@
'use strict'; 'use strict';
require('../support/test_helper'); require('../support/test_helper');
var RedisPool = require('redis-mpool');
const helper = require('../support/test_helper');
var assert = require('../support/assert'); var assert = require('../support/assert');
var mapnik = require('windshaft').mapnik; var mapnik = require('windshaft').mapnik;
var CartodbWindshaft = require('../../lib/cartodb/server'); var CartodbWindshaft = require('../../lib/cartodb/server');
var serverOptions = require('../../lib/cartodb/server_options'); var serverOptions = require('../../lib/cartodb/server_options');
var TemplateMaps = require('../../lib/cartodb/backends/template_maps.js');
describe('named maps provider cache', function() { describe('named maps provider cache', function() {
var server; var server;
@ -16,14 +15,8 @@ describe('named maps provider cache', function() {
server = new CartodbWindshaft(serverOptions); server = new CartodbWindshaft(serverOptions);
}); });
// configure redis pool instance to use in tests
var redisPool = new RedisPool(global.environment.redis);
var templateMaps = new TemplateMaps(redisPool, {
max_user_templates: global.environment.maxUserTemplates
});
var username = 'localhost'; var username = 'localhost';
const apikey = 1234;
var templateName = 'template_with_color'; var templateName = 'template_with_color';
var IMAGE_TOLERANCE = 20; var IMAGE_TOLERANCE = 20;
@ -31,7 +24,7 @@ describe('named maps provider cache', function() {
function createTemplate(color) { function createTemplate(color) {
return { return {
version: '0.0.1', version: '0.0.1',
name: templateName, name: `${templateName}_${color}`,
auth: { auth: {
method: 'open' method: 'open'
}, },
@ -56,17 +49,13 @@ describe('named maps provider cache', function() {
}; };
} }
afterEach(function (done) { function getNamedTile(templateId, options, callback) {
templateMaps.delTemplate(username, templateName, done);
});
function getNamedTile(options, callback) {
if (!callback) { if (!callback) {
callback = options; callback = options;
options = {}; options = {};
} }
var url = '/api/v1/map/named/' + templateName + '/all/' + [0,0,0].join('/') + '.png'; var url = '/api/v1/map/named/' + templateId + '/all/' + [0,0,0].join('/') + '.png';
var requestOptions = { var requestOptions = {
url: url, url: url,
@ -88,60 +77,116 @@ describe('named maps provider cache', function() {
assert.response(server, requestOptions, expectedResponse, function (res, err) { assert.response(server, requestOptions, expectedResponse, function (res, err) {
var img; var img;
if (statusCode === 200) { if (res.statusCode === 200) {
img = mapnik.Image.fromBytes(new Buffer(res.body, 'binary')); img = mapnik.Image.fromBytes(new Buffer.from(res.body, 'binary'));
} }
return callback(err, res, img); return callback(err, res, img);
}); });
} }
function addTemplate (template, callback) {
const createTemplateRequest = {
url: `/api/v1/map/named?api_key=${apikey}`,
method: 'POST',
headers: {
host: username,
'Content-Type': 'application/json'
},
body: JSON.stringify(template)
};
const expectedResponse = {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
assert.response(server, createTemplateRequest, expectedResponse, (res, err) => {
let template;
if (res.statusCode === 200) {
template = JSON.parse(res.body);
}
return callback(err, res, template);
});
}
function deleteTemplate (templateId, callback) {
const deleteTemplateRequest = {
url: `/api/v1/map/named/${templateId}?api_key=${apikey}`,
method: 'DELETE',
headers: {
host: 'localhost',
}
};
const expectedResponse = {
status: 204,
};
assert.response(server, deleteTemplateRequest, expectedResponse, (res, err) => {
return callback(err, res);
});
}
function previewFixture(color) { function previewFixture(color) {
return './test/fixtures/provider/populated_places_simple_reduced-' + color + '.png'; return './test/fixtures/provider/populated_places_simple_reduced-' + color + '.png';
} }
var colors = ['red', 'red', 'green', 'blue']; var colors = ['black', 'red', 'green', 'blue'];
colors.forEach(function(color) { colors.forEach(function(color) {
it('should return an image estimating its bounds based on dataset', function (done) { it('should return an image estimating its bounds based on dataset', function (done) {
templateMaps.addTemplate(username, createTemplate(color), function (err) { addTemplate(createTemplate(color), function (err, res, template) {
if (err) { if (err) {
return done(err); return done(err);
} }
getNamedTile(function(err, res, img) {
getNamedTile(template.template_id, function(err, res, img) {
assert.ok(!err); assert.ok(!err);
assert.imageIsSimilarToFile(img, previewFixture(color), IMAGE_TOLERANCE, done); assert.imageIsSimilarToFile(img, previewFixture(color), IMAGE_TOLERANCE, (err) => {
}); assert.ifError(err);
});
});
});
it('should fail to use template from named map provider after template deletion', function (done) { const keysToDelete = {};
var color = 'black'; keysToDelete['map_tpl|localhost'] = 0;
templateMaps.addTemplate(username, createTemplate(color), function (err) { helper.deleteRedisKeys(keysToDelete, done);
if (err) {
return done(err);
}
getNamedTile(function(err, res, img) {
assert.ok(!err);
assert.imageIsSimilarToFile(img, previewFixture(color), IMAGE_TOLERANCE, function(err) {
assert.ok(!err);
templateMaps.delTemplate(username, templateName, function (err) {
assert.ok(!err);
getNamedTile({ statusCode: 404 }, function(err, res) {
assert.ok(!err);
assert.deepEqual(
JSON.parse(res.body).errors,
["Template 'template_with_color' of user 'localhost' not found"]
);
// add template again so it's clean in afterEach
templateMaps.addTemplate(username, createTemplate(color), done);
});
}); });
}); });
}); });
}); });
}); });
it('should fail to use template from named map provider after template deletion', function (done) {
const color = 'black';
const templateId = `${templateName}_${color}`;
addTemplate(createTemplate(color), function (err) {
assert.ifError(err);
getNamedTile(templateId, function(err, res, img) {
assert.ifError(err);
assert.imageIsSimilarToFile(img, previewFixture(color), IMAGE_TOLERANCE, function (err) {
assert.ifError(err);
deleteTemplate(templateId, function (err) {
assert.ifError(err);
getNamedTile(templateId, { statusCode: 404 }, function(err, res) {
assert.ifError(err);
assert.deepEqual(
JSON.parse(res.body).errors,
["Template 'template_with_color_black' of user 'localhost' not found"]
);
done();
});
});
});
});
});
});
}); });