apostrophe-caches-redis
Advanced tools
Comparing version 2.0.0 to 2.1.0
140
index.js
@@ -6,2 +6,3 @@ // TODO: | ||
var redis = require("redis"); | ||
var Promise = require('bluebird'); | ||
@@ -30,62 +31,105 @@ module.exports = { | ||
return { | ||
// Fetch an item from the cache. If the item is in the | ||
// cache, the callback receives (null, item). If the | ||
// item is not in the cache the callback receives (null). | ||
// If an error occurs the callback receives (err). | ||
// If there is no callback a promise is returned. | ||
get: function(key, callback) { | ||
key = self.prefix + name + ':' + key; | ||
return self.client.get(key, function(err, json) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (json === null) { | ||
return callback(null); | ||
} | ||
var data; | ||
try { | ||
data = JSON.parse(json); | ||
} catch (e) { | ||
return callback(e); | ||
} | ||
return callback(null, data); | ||
}); | ||
if (callback) { | ||
return body(callback); | ||
} else { | ||
return Promise.promisify(body)(); | ||
} | ||
function body(callback) { | ||
key = self.prefix + name + ':' + key; | ||
return self.client.get(key, function(err, json) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (json === null) { | ||
return callback(null); | ||
} | ||
var data; | ||
try { | ||
data = JSON.parse(json); | ||
} catch (e) { | ||
return callback(e); | ||
} | ||
return callback(null, data); | ||
}); | ||
} | ||
}, | ||
// Store an item in the cache. `value` may be any JSON-friendly | ||
// value, including an object. `lifetime` is in seconds. | ||
// | ||
// The callback receives (err). | ||
// | ||
// You may also call with just three arguments: | ||
// key, value, callback. In that case there is no hard limit | ||
// on the lifetime, however NEVER use a cache for PERMANENT | ||
// storage of data. It might be cleared at any time. | ||
// | ||
// If there is no callback a promise is returned. | ||
set: function(key, value, lifetime, callback) { | ||
if (!callback) { | ||
callback = lifetime; | ||
if (arguments.length === 2) { | ||
lifetime = 0; | ||
} | ||
key = self.prefix + name + ':' + key; | ||
if (lifetime) { | ||
return self.client.setex(key, lifetime, JSON.stringify(value), callback); | ||
return Promise.promisify(body)(); | ||
} else if (arguments.length === 3) { | ||
if (typeof(lifetime) === 'function') { | ||
callback = lifetime; | ||
lifetime = 0; | ||
return body(callback); | ||
} else { | ||
return Promise.promisify(body)(); | ||
} | ||
} else { | ||
return self.client.set(key, JSON.stringify(value), callback); | ||
return body(callback); | ||
} | ||
function body(callback) { | ||
key = self.prefix + name + ':' + key; | ||
if (lifetime) { | ||
return self.client.setex(key, lifetime, JSON.stringify(value), callback); | ||
} else { | ||
return self.client.set(key, JSON.stringify(value), callback); | ||
} | ||
} | ||
}, | ||
// Empty the cache. If there is no callback a promise is returned. | ||
clear: function(callback) { | ||
// This is not as simple as it sounds: | ||
// | ||
// https://stackoverflow.com/questions/4006324/how-to-atomically-delete-keys-matching-a-pattern-using-redis | ||
// | ||
// I'm avoiding Lua because of comments in that article that it might not play nice | ||
// with Redis clustering. | ||
// | ||
// Use of `keys` is not deprecated as long as it's for a special-purpose, occasional operation, | ||
// and clearing an entire cache qualifies. | ||
return self.client.keys(self.prefix + name + ':*', function(err, keys) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
removeNextBatch(); | ||
function removeNextBatch() { | ||
if (!keys.length) { | ||
return callback(null); | ||
if (callback) { | ||
return body(callback); | ||
} else { | ||
return Promise.promisify(body)(); | ||
} | ||
function body(callback) { | ||
// This is not as simple as it sounds: | ||
// | ||
// https://stackoverflow.com/questions/4006324/how-to-atomically-delete-keys-matching-a-pattern-using-redis | ||
// | ||
// I'm avoiding Lua because of comments in that article that it might not play nice | ||
// with Redis clustering. | ||
// | ||
// Use of `keys` is not deprecated as long as it's for a special-purpose, occasional operation, | ||
// and clearing an entire cache qualifies. | ||
return self.client.keys(self.prefix + name + ':*', function(err, keys) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
return self.client.del(keys.slice(0, 1000), function(err) { | ||
if (err) { | ||
return callback(err); | ||
removeNextBatch(); | ||
function removeNextBatch() { | ||
if (!keys.length) { | ||
return callback(null); | ||
} | ||
keys = keys.slice(1000); | ||
return removeNextBatch(); | ||
}); | ||
} | ||
}); | ||
return self.client.del(keys.slice(0, 1000), function(err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
keys = keys.slice(1000); | ||
return removeNextBatch(); | ||
}); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -92,0 +136,0 @@ }; |
{ | ||
"name": "apostrophe-caches-redis", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Redis-based cache for the Apostrophe CMS", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "mocha test/test.js" | ||
}, | ||
@@ -25,2 +25,3 @@ "repository": { | ||
"dependencies": { | ||
"bluebird": "^3.5.1", | ||
"redis": "^2.7.1" | ||
@@ -31,6 +32,3 @@ }, | ||
"apostrophe": "^2.0.0" | ||
}, | ||
"scripts": { | ||
"test": "mocha test/test.js" | ||
} | ||
} |
@@ -32,1 +32,5 @@ This module enhances `apostrophe-caches`, the standard caching mechanism of Apostrophe, to use Redis rather than MongoDB. | ||
You can do that too, and it greatly reduces the load on MongoDB. You don't need this module for that. See [storing sessions in Redis](http://apostrophecms.org/docs/tutorials/howtos/storing-sessions-in-redis.html). | ||
## Changelog | ||
2.1.0: promise support. The `get`, `set` and `clear` methods of caches now return promises if no callback is given, matching the behavior of Apostrophe's core cache. |
var assert = require('assert'); | ||
var _ = require('lodash'); | ||
var async = require('async'); | ||
var Promise = require('bluebird'); | ||
@@ -10,2 +11,3 @@ describe('Apostrophe cache implementation in redis', function() { | ||
it('initializes apostrophe', function(done) { | ||
this.timeout(5000); | ||
apos = require('apostrophe')({ | ||
@@ -33,2 +35,14 @@ testModule: true, | ||
}); | ||
it('can clear cache 1', function(done) { | ||
return cache1.clear(function(err) { | ||
assert(!err); | ||
done(); | ||
}); | ||
}); | ||
it('can clear cache 2', function(done) { | ||
return cache2.clear(function(err) { | ||
assert(!err); | ||
done(); | ||
}); | ||
}); | ||
it('can store 2000 keys in cache 1', function(done) { | ||
@@ -114,3 +128,73 @@ var vals = _.range(0, 2000); | ||
}); | ||
it('can clear cache 1 with promises', function() { | ||
return cache1.clear(); | ||
}); | ||
it('can clear cache 2 with promises', function() { | ||
return cache2.clear(); | ||
}); | ||
it('can store 2000 keys in cache 1 with promises', function() { | ||
var vals = _.range(0, 2000); | ||
return Promise.each(vals, function(val) { | ||
return cache1.set(val, val); | ||
}); | ||
}); | ||
it('can store 2000 keys in cache 2 with promises', function() { | ||
var vals = _.range(2000, 4000); | ||
return Promise.each(vals, function(val) { | ||
return cache2.set(val, val); | ||
}); | ||
}); | ||
it('can retrieve key from cache 1 with promises', function() { | ||
return cache1.get(1000) | ||
.then(function(val) { | ||
assert(val === 1000); | ||
}); | ||
}); | ||
it('can retrieve key from cache 2 with promises', function() { | ||
return cache2.get(3000) | ||
.then(function(val) { | ||
assert(val === 3000); | ||
}); | ||
}); | ||
it('cannot retrieve cache 2 key from key 1 (namespacing) with promises', function() { | ||
return cache1.get(3000) | ||
.then(function(val) { | ||
assert(!val); | ||
}); | ||
}); | ||
it('can clear a cache with promises', function() { | ||
return cache1.clear(); | ||
}); | ||
it('cannot fetch a key from a cleared cache with promises', function() { | ||
return cache1.get(1000) | ||
.then(function(val) { | ||
assert(!val); | ||
}); | ||
}); | ||
it('can fetch a key from an uncleared cache with promises', function() { | ||
return cache2.get(3000) | ||
.then(function(val) { | ||
assert(val === 3000); | ||
}); | ||
}); | ||
it('can store a key with a 1-second timeout with promises', function() { | ||
return cache1.set('timeout', 'timeout', 1); | ||
}); | ||
it('can fetch that key within the 1-second timeout with promises', function() { | ||
return cache1.get('timeout', function(value) { | ||
assert(value === 'timeout'); | ||
done(); | ||
}); | ||
}); | ||
it('cannot fetch that key after 2 seconds with promises', function() { | ||
this.timeout(5000); | ||
return Promise.delay(2000) | ||
.then(function() { | ||
return cache1.get('timeout') | ||
}) | ||
.then(function(val) { | ||
assert(!val); | ||
}); | ||
}); | ||
}); | ||
12609
326
35
2
5
+ Addedbluebird@^3.5.1
+ Addedbluebird@3.7.2(transitive)