add explain cache and cache status route

This commit is contained in:
Simon Tokumine 2012-05-01 16:46:30 +01:00
parent 977f7d0e79
commit 7e074bbc98

View File

@ -20,17 +20,23 @@ var express = require('express')
})) }))
, Step = require('step') , Step = require('step')
, csv = require('csv') , csv = require('csv')
, crypto = require('crypto')
, Meta = require(global.settings.app_root + '/app/models/metadata') , Meta = require(global.settings.app_root + '/app/models/metadata')
, oAuth = require(global.settings.app_root + '/app/models/oauth') , oAuth = require(global.settings.app_root + '/app/models/oauth')
, PSQL = require(global.settings.app_root + '/app/models/psql') , PSQL = require(global.settings.app_root + '/app/models/psql')
, ApiKeyAuth = require(global.settings.app_root + '/app/models/apikey_auth') , ApiKeyAuth = require(global.settings.app_root + '/app/models/apikey_auth')
, _ = require('underscore'); , _ = require('underscore')
, tableCache = {};
app.use(express.bodyParser()); app.use(express.bodyParser());
app.enable('jsonp callback'); app.enable('jsonp callback');
// basic routing
app.all('/api/v1/sql', function(req, res) { handleQuery(req, res) } ); app.all('/api/v1/sql', function(req, res) { handleQuery(req, res) } );
app.all('/api/v1/sql.:f', function(req, res) { handleQuery(req, res) } ); app.all('/api/v1/sql.:f', function(req, res) { handleQuery(req, res) } );
app.get('/api/v1/cachestatus', function(req, res) { handleCacheStatus(req, res) } );
// request handlers
function handleQuery(req, res){ function handleQuery(req, res){
// sanitize input // sanitize input
@ -54,6 +60,9 @@ function handleQuery(req, res){
// setup step run // setup step run
var start = new Date().getTime(); var start = new Date().getTime();
// initialise MD5 key of sql for cache lookups
var sql_md5 = generateMD5(sql);
try { try {
if (!_.isString(sql)) throw new Error("You must indicate a sql query"); if (!_.isString(sql)) throw new Error("You must indicate a sql query");
var pg, explain_result; var pg, explain_result;
@ -81,14 +90,23 @@ function handleQuery(req, res){
// store postgres connection // store postgres connection
pg = new PSQL(user_id, database, limit, offset); pg = new PSQL(user_id, database, limit, offset);
// get all the tables // get all the tables from Cache or SQL
if (!_.isNull(tableCache[sql_md5]) && !_.isUndefined(tableCache[sql_md5])){
tableCache[sql_md5].hits++;
return true;
} else{
pg.query("SELECT CDB_QueryTables($$" + sql + "$$)", this); pg.query("SELECT CDB_QueryTables($$" + sql + "$$)", this);
}
}, },
function queryResult(err, result){ function queryResult(err, result){
if (err) throw err; if (err) throw err;
// store explain result // store explain result in local Cache
explain_result = result; if (_.isUndefined(tableCache[sql_md5])){
tableCache[sql_md5] = result;
tableCache[sql_md5].hits = 1; //initialise hit counter
}
// TODO: refactor formats to external object // TODO: refactor formats to external object
if (format === 'geojson'){ if (format === 'geojson'){
@ -110,7 +128,7 @@ function handleQuery(req, res){
// set cache headers // set cache headers
res.header('Last-Modified', new Date().toUTCString()); res.header('Last-Modified', new Date().toUTCString());
res.header('Cache-Control', 'no-cache,max-age=3600,must-revalidate, public'); res.header('Cache-Control', 'no-cache,max-age=3600,must-revalidate, public');
res.header('X-Cache-Channel', generateCacheKey(database,explain_result)); res.header('X-Cache-Channel', generateCacheKey(database, tableCache[sql_md5]));
return result; return result;
}, },
@ -147,6 +165,15 @@ function handleQuery(req, res){
} }
} }
function handleCacheStatus(req, res){
var tableCacheValues = _.values(tableCache);
var totalExplainHits = _.reduce(tableCacheValues, function(memo, res) { return memo + res.hits}, 0);
var totalExplainKeys = tableCacheValues.length;
res.send({explain: {hits: totalExplainHits, keys : totalExplainKeys }});
}
// helper functions
function toGeoJSON(data, res, callback){ function toGeoJSON(data, res, callback){
try{ try{
var out = { var out = {
@ -218,6 +245,12 @@ function generateCacheKey(database,tables){
return database + ":" + tables.rows[0].cdb_querytables.split(/^\{(.*)\}$/)[1]; return database + ":" + tables.rows[0].cdb_querytables.split(/^\{(.*)\}$/)[1];
} }
function generateMD5(data){
var hash = crypto.createHash('md5');
hash.update(data);
return hash.digest('hex');
}
function handleException(err, res){ function handleException(err, res){
var msg = (global.settings.environment == 'development') ? {error:[err.message], stack: err.stack} : {error:[err.message]} var msg = (global.settings.environment == 'development') ? {error:[err.message], stack: err.stack} : {error:[err.message]}
if (global.settings.environment !== 'test'){ if (global.settings.environment !== 'test'){