From 4aabe9d946b5e6e161f0b8dafbac242b0116bbaf Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Tue, 14 Jul 2015 20:10:55 +0200 Subject: [PATCH] Named maps controller adding cache headers This requires a cache for affected tables as it is hitting db for every request right now --- lib/cartodb/controllers/named_maps.js | 41 +++++++++++++++++-- .../models/mapconfig/named_map_provider.js | 30 ++++++++++++++ lib/cartodb/server.js | 3 +- 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index 2aa1a4ee..9b27ad4b 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -5,9 +5,10 @@ var NamedMapsCacheEntry = require('../cache/model/named_maps_entry'); var cors = require('../middleware/cors'); var NamedMapMapConfigProvider = require('../models/mapconfig/named_map_provider'); +var TablesCacheEntry = require('../cache/model/database_tables_entry'); function NamedMapsController(app, pgConnection, templateMaps, tileBackend, previewBackend, surrogateKeysCache, - tablesExtentApi, userLimitsApi) { + tablesExtentApi, userLimitsApi, queryTablesApi) { this.app = app; this.pgConnection = pgConnection; this.templateMaps = templateMaps; @@ -16,6 +17,7 @@ function NamedMapsController(app, pgConnection, templateMaps, tileBackend, previ this.surrogateKeysCache = surrogateKeysCache; this.tablesExtentApi = tablesExtentApi; this.userLimitsApi = userLimitsApi; + this.queryTablesApi = queryTablesApi; } module.exports = NamedMapsController; @@ -29,9 +31,40 @@ NamedMapsController.prototype.register = function(app) { NamedMapsController.prototype.sendResponse = function(req, res, resource, headers, namedMapProvider) { this.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(req.context.user, namedMapProvider.getTemplateName())); - res.setHeader('Content-Type', headers['Content-Type']); - res.setHeader('Cache-Control', 'public,max-age=7200,must-revalidate'); - this.app.sendResponse(res, [resource, 200]); + res.header('Content-Type', headers['Content-Type']); + res.header('Cache-Control', 'public,max-age=7200,must-revalidate'); + + var self = this; + + var dbName = req.params.dbname; + step( + function getAffectedTablesAndLastUpdatedTime() { + namedMapProvider.getAffectedTablesAndLastUpdatedTime(self.queryTablesApi, this); + }, + function sendResponse(err, result) { + req.profiler.done('affectedTables'); + if (err) { + console.log('ERROR generating cache channel: ' + err); + } + if (!result || !!result.affectedTables) { + // we increase cache control as we can invalidate it + res.header('Cache-Control', 'public,max-age=31536000'); + + var lastModifiedDate; + if (Number.isFinite(result.lastUpdatedTime)) { + lastModifiedDate = new Date(result.lastUpdatedTime); + } else { + lastModifiedDate = new Date(); + } + res.header('Last-Modified', lastModifiedDate.toUTCString()); + + var tablesCacheEntry = new TablesCacheEntry(dbName, result.affectedTables); + res.header('X-Cache-Channel', tablesCacheEntry.getCacheChannel()); + self.surrogateKeysCache.tag(res, tablesCacheEntry); + } + self.app.sendResponse(res, [resource, 200]); + } + ); }; NamedMapsController.prototype.tile = function(req, res) { diff --git a/lib/cartodb/models/mapconfig/named_map_provider.js b/lib/cartodb/models/mapconfig/named_map_provider.js index 40c8bfe4..c9970cef 100644 --- a/lib/cartodb/models/mapconfig/named_map_provider.js +++ b/lib/cartodb/models/mapconfig/named_map_provider.js @@ -24,6 +24,8 @@ function NamedMapMapConfigProvider(templateMaps, pgConnection, userLimitsApi, ow // use template after call to mapConfig this.template = null; + this.affectedTablesAndLastUpdate = null; + // providing this.err = null; this.mapConfig = null; @@ -198,3 +200,31 @@ NamedMapMapConfigProvider.prototype.setDBParams = function(cdbuser, params, call NamedMapMapConfigProvider.prototype.getTemplateName = function() { return this.templateName; }; + +NamedMapMapConfigProvider.prototype.getAffectedTablesAndLastUpdatedTime = function(queryTablesApi, callback) { + var self = this; + + if (this.affectedTablesAndLastUpdate !== null) { + return this.affectedTablesAndLastUpdate; + } + + step( + function getMapConfig() { + self.getMapConfig(this); + }, + function getSql(err, mapConfig) { + assert.ifError(err); + return mapConfig.getLayers().map(function(layer) { + return layer.options.sql; + }).join(';'); + }, + function getAffectedTables(err, sql) { + assert.ifError(err); + queryTablesApi.getAffectedTablesAndLastUpdatedTime(self.owner, sql, this); + }, + function finish(err, result) { + self.affectedTablesAndLastUpdate = result; + return callback(err, result); + } + ); +}; diff --git a/lib/cartodb/server.js b/lib/cartodb/server.js index e01d99b1..11d88361 100644 --- a/lib/cartodb/server.js +++ b/lib/cartodb/server.js @@ -221,7 +221,8 @@ module.exports = function(serverOptions) { previewBackend, surrogateKeysCache, tablesExtentApi, - userLimitsApi + userLimitsApi, + queryTablesApi ).register(app); new controller.NamedMapsAdmin(app, templateMaps, authApi).register(app);