Skip to content

Commit 26f9664

Browse files
author
Joseph Lokahi Podwys
committed
Initial commit.
0 parents  commit 26f9664

File tree

13 files changed

+716
-0
lines changed

13 files changed

+716
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# cache-service
2+
A multi-tiered caching solution.
3+
4+
##What Does cache-service Do?
5+
cache-service allows you to create redundant, cache-agnostic caching configurations. By default, it supports redis and node-cach, but you can add any cache you want as long as you follow the same interface.
6+
7+
8+
##Why Would I Want Redundant Caching?
9+
Let's say you want to store your most used data in an in-memory cache with a 5-minute expiration while storing a larger pool of data in a redis cache with a 15-minute expiration. You could easily do so on your own, but by using cache-service, you only have to execute one cache query to search both caches.
10+
11+
##Installation
12+
```javascript
13+
npm install cache-service
14+
```
15+
16+
##Basic Usage
17+
Require and instantiate cache-service as follows:
18+
```javascript
19+
var cs = require('cache-service').cacheService;
20+
var cacheService = new cs();
21+
```
22+
##More Documentation Coming Soon

bin/tests

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#/usr/bin/env bash
2+
3+
node_modules/.bin/mocha test/server/*.js -R spec --globals CookieAccessInfo,Cookie,CookieJar,page,assets,js,css,img$MOCHA_GLOBALS

index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
exports.cacheService = require('./modules/cacheService');
2+
exports.cacheCollection = require('./modules/cacheCollection');
3+
exports.cacheModule = require('./modules/cacheModules/cacheModule');
4+
exports.nodeCacheModule = require('./modules/cacheModules/nodeCacheModule');
5+
exports.redisCacheModule = require('./modules/cacheModules/redisCacheModule');

modules/cacheCollection.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
var cacheModule = require('./cacheModules/cacheModule');
2+
var redisCacheModule = require('./cacheModules/redisCacheModule');
3+
var nodeCacheModule = require('./cacheModules/nodeCacheModule');
4+
5+
function cacheCollection(cacheModuleConfig){
6+
var self = this;
7+
self.supportedCaches = ['redis', 'node-cache', 'custom'];
8+
9+
self.init = function(){
10+
if(cacheModuleConfig && !isEmpty(cacheModuleConfig)){
11+
self.cacheModuleConfig = cacheModuleConfig;
12+
}
13+
else{
14+
self.cacheModuleConfig = [{type: 'redis'}, {type: 'node-cache'}];
15+
}
16+
self.preApi = [];
17+
self.postApi = [];
18+
19+
for(var i = 0; i < self.cacheModuleConfig.length; i++){
20+
var cacheConfig = validateCacheConfig(self.cacheModuleConfig[i]);
21+
if(cacheConfig.postApi && self.postApi.length > 0){
22+
continue;
23+
}
24+
var cache = null;
25+
if(cacheConfig.type != 'custom'){
26+
try {
27+
cache = getCacheModule(cacheConfig).cache;
28+
} catch (err) {
29+
self.log(true, 'Failed to get requested cache module with config ' + JSON.stringify(cacheConfig) + ' :', err);
30+
}
31+
if(cache && cache.db){
32+
var preOrPostApi = (!cacheConfig.postApi) ? 'preApi' : 'postApi';
33+
self[preOrPostApi].push(cache);
34+
}
35+
}
36+
else{
37+
var preOrPostApi = (!cacheConfig.postApi) ? 'preApi' : 'postApi';
38+
self[preOrPostApi].push(self.cacheModuleConfig[i].cache);
39+
}
40+
}
41+
if(self.preApi.length < 1){
42+
throw new exception('NoCacheException', 'No pre-api caches were succesfully initialized.');
43+
}
44+
}
45+
46+
function validateCacheConfig(cacheConfig){
47+
if(!cacheConfig.type || self.supportedCaches.indexOf(cacheConfig.type) < 0){
48+
throw new exception('BadCacheTypeException', 'You either did not set a cache type or you spelled it wrong.');
49+
}
50+
if(cacheConfig.type != 'custom'){
51+
cacheConfig.nameSpace = self.nameSpace;
52+
cacheConfig.verbose = self.verbose;
53+
}
54+
return cacheConfig;
55+
}
56+
57+
function getCacheModule(cacheConfig){
58+
if(cacheConfig.type === 'redis'){
59+
return new redisCacheModule(cacheConfig);
60+
}
61+
else if(cacheConfig.type === 'node-cache'){
62+
return new nodeCacheModule(cacheConfig);
63+
}
64+
}
65+
66+
function isEmpty (val) {
67+
return (val === false || val === null || (typeof val == 'object' && Object.keys(val).length == 0));
68+
}
69+
70+
71+
72+
self.init();
73+
}
74+
75+
module.exports = cacheCollection;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
function cacheModule(config){
2+
var self = this;
3+
config = config || {};
4+
self.verbose = config.verbose || false;
5+
self.expiration = config.defaultExpiration || 900;
6+
//self.cacheWhenEmpty = (typeof config.cacheWhenEmpty === 'boolean') ? config.cacheWhenEmpty : true;
7+
self.readOnly = (typeof config.readOnly === 'boolean') ? config.readOnly : false;
8+
self.checkOnPreviousEmpty = (typeof config.checkOnPreviousEmpty === 'boolean') ? config.checkOnPreviousEmpty : false;
9+
10+
self.get = function(key, cb, cleanKey){
11+
try {
12+
cacheKey = (cleanKey) ? cleanKey : key;
13+
self.log(false, 'Attempting to get key:', {key: cacheKey});
14+
self.db.get(cacheKey, function(err, result){
15+
try {
16+
result = JSON.parse(result);
17+
} catch (err) {
18+
//Do nothing
19+
}
20+
cb(err, result);
21+
});
22+
} catch (err) {
23+
cb({name: 'GetException', message: err}, null);
24+
}
25+
}
26+
27+
self.log = function (isError, message, data){
28+
var indentifier = 'cacheService: ';
29+
if(self.verbose || isError){
30+
if(data) console.log(indentifier + message, data);
31+
else console.log(indentifier + message);
32+
}
33+
}
34+
}
35+
36+
module.exports = cacheModule;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
var cacheModule = require('./cacheModule');
2+
var nodeCache = require('node-cache');
3+
4+
function nodeCacheModule(config){
5+
6+
config = config || {};
7+
this.cache = new cacheModule(config);
8+
9+
this.cache.init = function(){
10+
try {
11+
this.db = new nodeCache();
12+
this.log(false, 'Node-cache client created with the following defaults:', {expiration: this.expiration, verbose: this.verbose, readOnly: this.readOnly});
13+
} catch (err) {
14+
this.log(true, 'Node-cache client not created:', err);
15+
this.db = false;
16+
}
17+
this.type = config.type || 'node-cache-standalone';
18+
}
19+
20+
this.cache.get = function(key, cb, cleanKey){
21+
try {
22+
cacheKey = (cleanKey) ? cleanKey : key;
23+
this.log(false, 'Attempting to get key:', {key: cacheKey});
24+
this.db.get(cacheKey, function(err, result){
25+
try {
26+
result = JSON.parse(result);
27+
} catch (err) {
28+
//Do nothing
29+
}
30+
if(typeof result[cacheKey] !== 'undefined'){
31+
cb(err, result[cacheKey]);
32+
}
33+
else{
34+
cb(err, null);
35+
}
36+
});
37+
} catch (err) {
38+
cb({name: 'GetException', message: err}, null);
39+
}
40+
}
41+
42+
this.cache.set = function(key, value, expiration, cb){
43+
try {
44+
if(!this.readOnly){
45+
expiration = expiration || this.expiration;
46+
cb = cb || noop;
47+
this.db.set(key, value, expiration, cb);
48+
49+
}
50+
} catch (err) {
51+
this.log(true, 'Set failed for cache of type ' + this.type, {name: 'NodeCacheSetException', message: err});
52+
}
53+
}
54+
55+
this.cache.delete = function(keys, cb){
56+
try {
57+
this.db.del(keys, function (err, count){
58+
if(cb){
59+
cb(err, count);
60+
}
61+
});
62+
} catch (err) {
63+
this.log(true, 'Delete failed for cache of type ' + this.type, err);
64+
}
65+
}
66+
67+
this.cache.flushAll = function(){
68+
try {
69+
this.db.flushAll();
70+
this.log(false, 'Flushing all data from cache of type ' + this.type);
71+
} catch (err) {
72+
this.log(true, 'Flush failed for cache of type ' + this.type, err);
73+
}
74+
}
75+
76+
var noop = function(){}
77+
78+
this.cache.init();
79+
}
80+
81+
module.exports = nodeCacheModule;
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
var cacheModule = require('./cacheModule');
2+
var redis = require('redis');
3+
4+
function redisCacheModule(config){
5+
6+
config = config || {};
7+
this.cache = new cacheModule(config);
8+
9+
this.cache.init = function(){
10+
this.type = config.type || 'redis-standalone';
11+
if(config.redisMock){
12+
this.db = config.redisMock;
13+
}
14+
else{
15+
if(config.redisUrl){
16+
this.redisData = config.redisUrl || null;
17+
}
18+
else if(config.redisEnv){
19+
this.redisData = process.env[redisEnv] || null;
20+
}
21+
else if(config.redisData){
22+
this.redisData = config.redisData
23+
}
24+
this.readOnly = (typeof config.readOnly === 'boolean') ? config.readOnly : false;
25+
try {
26+
if (this.redisData) {
27+
if(typeof redisData === 'string'){
28+
var redisURL = require('url').parse(this.redisData);
29+
this.db = redis.createClient(redisURL.port, redisURL.hostname, {no_ready_check: true, max_attempts: 5});
30+
this.db.auth(redisURL.auth.split(":")[1]);
31+
}
32+
else{
33+
this.db = redis.createClient(this.redisData.port, this.redisData.hostname, {no_ready_check: true, max_attempts: 5});
34+
this.db.auth(this.redisData.auth);
35+
}
36+
this.db.on('error', function(err) {
37+
console.log("Error " + err);
38+
});
39+
process.on('SIGTERM', function() {
40+
this.db.quit();
41+
});
42+
this.log(false, 'Redis client created with the following defaults:', {expiration: this.expiration, verbose: this.verbose, readOnly: this.readOnly});
43+
} else {
44+
this.db = false;
45+
this.log(false, 'Redis client not created: no redis config provided');
46+
}
47+
} catch (err) {
48+
this.db = false;
49+
this.log(true, 'Redis client not created:', err);
50+
}
51+
}
52+
}
53+
54+
this.cache.set = function(key, value, expiration, cb){
55+
try {
56+
if(!this.readOnly){
57+
expiration = expiration || this.expiration;
58+
try {
59+
value = JSON.stringify(value);
60+
} catch (err) {
61+
//Do nothing
62+
}
63+
cb = cb || noop;
64+
this.db.setex(key, expiration, value, cb);
65+
}
66+
}catch (err) {
67+
this.log(true, 'Set failed for cache of type ' + this.type, {name: 'RedisSetException', message: err});
68+
}
69+
}
70+
71+
this.cache.delete = function(keys, cb){
72+
try {
73+
this.db.del(keys, function (err, count){
74+
if(cb){
75+
cb(err, count);
76+
}
77+
});
78+
} catch (err) {
79+
this.log(true, 'Delete failed for cache of type ' + this.type, err);
80+
}
81+
}
82+
83+
this.cache.flushAll = function(){
84+
try {
85+
this.db.flushall();
86+
this.log(false, 'Flushing all data from cache of type ' + this.type);
87+
} catch (err) {
88+
this.log(true, 'Flush failed for cache of type ' + this.type, err);
89+
}
90+
}
91+
92+
var noop = function(){}
93+
94+
this.cache.init();
95+
}
96+
97+
module.exports = redisCacheModule;

0 commit comments

Comments
 (0)