Caching with Memcached, Express and NodeJS

In a previous post I discussed how you can use Redis to store NodeJS session data. While in many cases Redis is a better product than Memcached, Memcached has one big advantage is that it has least-recently-used eviction. Basically, when the table is full, older data will be removed based on the LRU algorithm.

With NodeJS, there is a great npm package called memcache.

To access the package I created a simple wrapper called cache.js.  Like with all programming languages, some abstract can be a good thing.

var memcache = require(‘memcache’);

var client = undefined;
exports.connect = function() {
client = new memcache.Client();
client.connect();
return client;
}

exports.get = function(key, callback) {
client.get(key, callback);
};
exports.set = function(key, value) {
client.set(key, value, function(error, result){}, 10);
};

The implementation is then very simple:
1) Call cache.connect(); after app.listen(3000);
2) Within your route, you provide the key and have the result value returned in the callback. If the item is undefined then perform the required logic, set the value and continue.

app.get(‘/’, function(req, res){
cache.get(‘key’, function(error, result){
console.log(result);

if(result == null) {
result = microtime();
cache.set(‘key’, result);
}

res.render(‘index’, {title: “Hello Express at ” + result});
});

Simple. Dirty.

Sadly, every time I looked at the above code I felt dirty.

As such, I wondered. How else could you do this?

One approach I thought could be interesting is to take advantage of the middleware pipeline nature of Express and NodeJS. Now the logic and responsibility is separated into their own functions.

function getCache(req, res, next) {
cache.get(req.route.path, function(error, result){
req.params.cachedItem = result;
next();
});
};

function processIndexIfNotInCache(req, res, next) {
if(req.params.cachedItem == undefined) {
req.params.cachedItem = microtime();
cache.set(req.route.path, req.params.cachedItem);
}
next();
}

app.get(‘/’, getCache, processIndexIfNotInCache, function(req, res){
res.render(‘index’, {title: “Hello Express at ” + req.params.cachedItem});
});

However, while getCache is generic and reusable, processIndexIfNotInCache is a bit of a mouthful and you’ll soon start seeing repeating logic.

Dirty in a different way

While we’ve improved the code in some aspects, it’s still not great. In my final attempt, I was left with two generic methods and a hash which pointed the point to where the cacheable logic lived.

function processCache(req, res, next) {
cache.get(req.route.path, function(error, result){
if(result == undefined)
result = executeAndStore(req.route.path);

req.params.cachedItem = result;
next();
});
};

function executeAndStore(path) {
var result = routes[path]();
cache.set(path, result);
return result;
}

var routes = {‘/’: microtime }
app.get(‘/’, processCache, function(req, res){
res.render(‘index’, {title: “Hello Express at ” + req.params.cachedItem});
});

There are still a few refactoring to go, but we have started to start separating the logic of caching from the logic of the method and the actual rendering itself.

A sample, along with many others, can be found at https://github.com/BenHall/memcached_express

I would love to see other examples of how people would solve this – ping me @Ben_Hall

Leave a Reply

Your email address will not be published. Required fields are marked *