diff --git a/NEWS.md b/NEWS.md index 3fdf6a19..92fd58fc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,14 @@ +1.3.3 +------ +* Set Last-Modified header to allow for 304 responses +* Add profiling support (needs useProfiler in env config file) +* Fix double-checking for layergroups with no interactivity +* Log full layergroup config at creation time (#76) + 1.3.2 ------ * Set default layergroup TTL to 2 hours -* Server multilayer tiles and grid with persistent cache control +* Serve multilayer tiles and grid with persistent cache control 1.3.1 ------ diff --git a/config/environments/development.js.example b/config/environments/development.js.example index 9c8c10f8..7a3aaad8 100644 --- a/config/environments/development.js.example +++ b/config/environments/development.js.example @@ -9,7 +9,7 @@ var config = { ,socket_timeout: 600000 ,enable_cors: true ,cache_enabled: false - ,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type]' + ,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler])' ,postgres_auth_user: 'development_cartodb_user_<%= user_id %>' ,postgres: { // Parameters to pass to datasource plugin of mapnik @@ -63,6 +63,10 @@ var config = { port: 6082, ttl: 86400 } + // If useProfiler is true every response will be served with an + // X-Tiler-Profile header containing elapsed timing for various + // steps taken for producing the response. + ,useProfiler:true }; module.exports = config; diff --git a/config/environments/production.js.example b/config/environments/production.js.example index 5ee29407..c80d46f2 100644 --- a/config/environments/production.js.example +++ b/config/environments/production.js.example @@ -9,7 +9,7 @@ var config = { ,socket_timeout: 600000 ,enable_cors: true ,cache_enabled: true - ,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type]' + ,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler])' ,postgres_auth_user: 'cartodb_user_<%= user_id %>' ,postgres: { // Parameters to pass to datasource plugin of mapnik @@ -57,6 +57,10 @@ var config = { port: 6082, ttl: 86400 } + // If useProfiler is true every response will be served with an + // X-Tiler-Profile header containing elapsed timing for various + // steps taken for producing the response. + ,useProfiler:false }; module.exports = config; diff --git a/config/environments/staging.js.example b/config/environments/staging.js.example index 7fc660c5..bdf31fc5 100644 --- a/config/environments/staging.js.example +++ b/config/environments/staging.js.example @@ -9,7 +9,7 @@ var config = { ,socket_timeout: 600000 ,enable_cors: true ,cache_enabled: true - ,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type]' + ,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms (:res[X-Tiler-Profiler]) -> :res[Content-Type]' ,postgres_auth_user: 'cartodb_staging_user_<%= user_id %>' ,postgres: { // Parameters to pass to datasource plugin of mapnik @@ -57,6 +57,10 @@ var config = { port: 6082, ttl: 86400 } + // If useProfiler is true every response will be served with an + // X-Tiler-Profile header containing elapsed timing for various + // steps taken for producing the response. + ,useProfiler:true }; module.exports = config; diff --git a/config/environments/test.js.example b/config/environments/test.js.example index ff636581..a6761972 100644 --- a/config/environments/test.js.example +++ b/config/environments/test.js.example @@ -9,7 +9,7 @@ var config = { ,socket_timeout: 600000 ,enable_cors: true ,cache_enabled: false - ,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type]' + ,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler])' ,postgres_auth_user: 'test_cartodb_user_<%= user_id %>' ,postgres: { // Parameters to pass to datasource plugin of mapnik @@ -59,6 +59,10 @@ var config = { port: null, ttl: 86400 } + // If useProfiler is true every response will be served with an + // X-Tiler-Profile header containing elapsed timing for various + // steps taken for producing the response. + ,useProfiler:false }; module.exports = config; diff --git a/configure b/configure index 3f07bba0..282fd63d 100755 --- a/configure +++ b/configure @@ -18,7 +18,9 @@ # PGPORT=5432 +SQLAPI_PORT=8080 MAPNIK_VERSION= +ENVIRONMENT=development usage() { echo "Usage: $0 [OPTION]" @@ -26,7 +28,9 @@ usage() { echo "Configuration:" echo " --help display this help and exit" echo " --with-pgport=NUM access PostgreSQL server on TCP port NUM [$PGPORT]" + echo " --with-sqlapi-port=NUM access SQL-API server on TCP port NUM [$SQLAPI_PORT]" echo " --with-mapnik-version=STRING set mapnik version string [$MAPNIK_VERSION]" + echo " --environment=STRING set output environment name [$ENVIRONMENT]" } while test -n "$1"; do @@ -38,9 +42,15 @@ while test -n "$1"; do --with-pgport=*) PGPORT=`echo "$1" | cut -d= -f2` ;; + --with-sqlapi-port=*) + SQLAPI_PORT=`echo "$1" | cut -d= -f2` + ;; --with-mapnik-version=*) MAPNIK_VERSION=`echo "$1" | cut -d= -f2` ;; + --environment=*) + ENVIRONMENT=`echo "$1" | cut -d= -f2` + ;; *) echo "Unknown option '$1'" >&2 usage >&2 @@ -50,16 +60,19 @@ while test -n "$1"; do done echo "PGPORT: $PGPORT" +echo "SQLAPI_PORT: $SQLAPI_PORT" echo "MAPNIK_VERSION: $MAPNIK_VERSION" +echo "ENVIRONMENT: $ENVIRONMENT" # TODO: allow specifying configuration settings ! -for f in config/environments/*.example; do +for f in config/environments/${ENVIRONMENT}.js.example; do o=`dirname "$f"`/`basename "$f" .example` echo "Writing $o" # See http://austinmatzko.com/2008/04/26/sed-multi-line-search-and-replace/ sed -n "1h;1!H;\${;g;s/\(,postgres: {[^}]*port: *'\?\)[^',]*\('\?,\)/\1$PGPORT\2/;p;}" < "$f" \ | sed "s/mapnik_version:.*/mapnik_version: '$MAPNIK_VERSION'/" \ + | sed -n "1h;1!H;\${;g;s/\(,sqlapi: {[^}]*port: *'\?\)[^',]*\('\?,\)/\1$SQLAPI_PORT\2/;p;}" \ > "$o" done diff --git a/lib/cartodb/redis_pool.js b/lib/cartodb/redis_pool.js index d2c40cc2..2ef86461 100644 --- a/lib/cartodb/redis_pool.js +++ b/lib/cartodb/redis_pool.js @@ -77,4 +77,4 @@ var RedisPool = function(opts){ return me; }; -module.exports = RedisPool; \ No newline at end of file +module.exports = RedisPool; diff --git a/lib/cartodb/server_options.js b/lib/cartodb/server_options.js index 177d6aab..9957bf8a 100644 --- a/lib/cartodb/server_options.js +++ b/lib/cartodb/server_options.js @@ -37,7 +37,8 @@ module.exports = function(){ varnish_host: global.environment.varnish.host, varnish_port: global.environment.varnish.port, cache_enabled: global.environment.cache_enabled, - log_format: global.environment.log_format + log_format: global.environment.log_format, + useProfiler: global.environment.useProfiler }; // Be nice and warn if configured mapnik version @@ -159,13 +160,10 @@ module.exports = function(){ callback(null, me.channelCache[cacheKey]); return; } - - if ( req.params.token ) { - if ( ! me.channelCache.hasOwnProperty(cacheKey) ) { - callback(new Error('missing channel cache for token ' + req.params.token)); - } else { - callback(null, me.channelCache[cacheKey]); - } + else if ( req.params.token ) { + // cached cache channel for token-based access should be constructed + // at cache creation time + callback(new Error('missing channel cache for token ' + req.params.token)); return; } @@ -215,10 +213,20 @@ module.exports = function(){ res.header('Cache-Control', 'public,max-age=31536000'); // 1 year } else { var ttl = global.environment.varnish.ttl || 86400; - res.header('Last-Modified', new Date().toUTCString()); res.header('Cache-Control', 'no-cache,max-age='+ttl+',must-revalidate, public'); } + // Set Last-Modified header + var lastUpdated; + if ( req.params.cache_buster ) { + // Assuming cache_buster is a timestamp + // FIXME: store lastModified in the cache channel instead + lastUpdated = new Date(parseInt(req.params.cache_buster)); + } else { + lastUpdated = new Date(); + } + res.header('Last-Modified', lastUpdated.toUTCString()); + me.generateCacheChannel(req, function(err, channel){ if ( ! err ) { res.header('X-Cache-Channel', channel); @@ -306,6 +314,7 @@ module.exports = function(){ LZMA.decompress( lzma, function(result) { + if (req.profiler) req.profiler.done('LZMA decompress'); try { delete req.query.lzma _.extend(req.query, JSON.parse(result)) @@ -350,11 +359,14 @@ module.exports = function(){ var that = this; + if (req.profiler) req.profiler.done('req2params.setup'); + Step( function getPrivacy(){ cartoData.authorize(req, this); }, function gatekeep(err, data){ + if (req.profiler) req.profiler.done('cartoData.authorize'); if(err) throw err; if(data === "0") throw new Error("Sorry, you are unauthorized (permission denied)"); return data; @@ -365,18 +377,21 @@ module.exports = function(){ cartoData.getDatabase(req, this); }, function getGeometryType(err, data){ + if (req.profiler) req.profiler.done('cartoData.getDatabase'); if (err) throw err; _.extend(req.params, {dbname:data}); cartoData.getGeometryType(req, this); }, function finishSetup(err, data){ + if (req.profiler) req.profiler.done('cartoData.getGeometryType'); if ( err ) { callback(err, req); return; } if (!_.isNull(data)) _.extend(req.params, {geom_type: data}); that.addCacheChannel(req, function(err) { + if (req.profiler) req.profiler.done('addCacheChannel'); callback(err, req); }); } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 68e156f4..9e6de77d 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "windshaft-cartodb", - "version": "1.3.1", + "version": "1.3.3", "dependencies": { "node-varnish": { "version": "0.1.1" @@ -9,10 +9,10 @@ "version": "1.3.3" }, "windshaft": { - "version": "0.12.9", + "version": "0.13.1", "dependencies": { "grainstore": { - "version": "0.13.5", + "version": "0.13.6", "dependencies": { "carto": { "version": "0.9.3-cdb3", @@ -72,47 +72,7 @@ "version": "0.3.4" }, "sqlite3": { - "version": "2.1.10", - "dependencies": { - "tar.gz": { - "version": "0.1.1", - "dependencies": { - "fstream": { - "version": "0.1.22", - "dependencies": { - "rimraf": { - "version": "2.2.0" - }, - "graceful-fs": { - "version": "1.2.2" - }, - "inherits": { - "version": "1.0.0" - } - } - }, - "tar": { - "version": "0.1.17", - "dependencies": { - "inherits": { - "version": "1.0.0" - }, - "block-stream": { - "version": "0.0.6" - } - } - }, - "commander": { - "version": "1.1.1", - "dependencies": { - "keypress": { - "version": "0.1.0" - } - } - } - } - } - } + "version": "2.1.14" }, "mime": { "version": "1.2.9" diff --git a/package.json b/package.json index ff1670fb..b6a9a535 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,27 @@ { "private": true, "name": "windshaft-cartodb", - "version": "1.3.2", + "version": "1.3.3", "description": "A map tile server for CartoDB", - "url": "https://github.com/Vizzuality/Windshaft-cartodb", + "url": "https://github.com/CartoDB/Windshaft-cartodb", "licenses": [{ "type": "BSD", - "url": "https://github.com/Vizzuality/Windshaft-cartodb/blob/master/LICENCE" + "url": "https://github.com/CartoDB/Windshaft-cartodb/blob/master/LICENCE" }], - "repositories": [{ + "repository": { "type": "git", - "url": "git://github.com/Vizzuality/Windshaft-cartodb.git" - }], - "author": { - "name": "Simon Tokumine, Javi Santana, Vizzuality", - "url": "http://vizzuality.com", - "email": "simon@vizzuality.com" + "url": "git://github.com/CartoDB/Windshaft-cartodb.git" }, + "author": "Vizzuality (http://vizzuality.com)", + "contributors": [ + "Simon Tokumine ", + "Javi Santana ", + "Sandro Santilli " + ], "dependencies": { "node-varnish": "0.1.1", "underscore" : "~1.3.3", - "windshaft" : "~0.12.9", + "windshaft" : "~0.13.1", "step": "0.0.x", "generic-pool": "~2.0.3", "redis": "~0.8.3",