cache-service-node-cache
Advanced tools
+105
-18
@@ -7,7 +7,10 @@ var nodeCache = require('node-cache'); | ||
| * @param config: { | ||
| * type: {string | 'node-cache'} | ||
| * verbose: {boolean | false}, | ||
| * expiration: {integer | 900}, | ||
| * readOnly: {boolean | false}, | ||
| * checkOnPreviousEmpty {boolean | true} | ||
| * type: {string | 'node-cache'} | ||
| * verbose: {boolean | false}, | ||
| * expiration: {integer | 900}, | ||
| * readOnly: {boolean | false}, | ||
| * checkOnPreviousEmpty {boolean | true}, | ||
| * backgroundRefreshIntervalCheck {boolean | true}, | ||
| * backgroundRefreshInterval {integer | 60000}, | ||
| * backgroundRefreshMinTtl {integer | 70000} | ||
| * } | ||
@@ -22,2 +25,6 @@ */ | ||
| self.checkOnPreviousEmpty = (typeof config.checkOnPreviousEmpty === 'boolean') ? config.checkOnPreviousEmpty : true; | ||
| self.backgroundRefreshInterval = config.backgroundRefreshInterval || 60000; | ||
| self.backgroundRefreshMinTtl = config.backgroundRefreshMinTtl || 70000; | ||
| var refreshKeys = {}; | ||
| var backgroundRefreshEnabled = false; | ||
@@ -35,6 +42,8 @@ /** | ||
| self.get = function(key, cb, cleanKey){ | ||
| log(false, 'Attempting to get key:', {key: key}); | ||
| if(arguments.length < 2){ | ||
| throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.get() requires 2 arguments.'); | ||
| } | ||
| log(false, 'get() called:', {key: key}); | ||
| try { | ||
| var cacheKey = (cleanKey) ? cleanKey : key; | ||
| log(false, 'Attempting to get key:', {key: cacheKey}); | ||
| self.db.get(cacheKey, function(err, result){ | ||
@@ -55,3 +64,6 @@ cb(err, result); | ||
| self.mget = function(keys, cb, index){ | ||
| log(false, 'Attempting to mget keys:', {keys: keys}); | ||
| if(arguments.length < 2){ | ||
| throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.mget() requires 2 arguments.'); | ||
| } | ||
| log(false, '.mget() called:', {keys: keys}); | ||
| self.db.mget(keys, function (err, response){ | ||
@@ -67,14 +79,28 @@ cb(err, response, index); | ||
| * @param {integer} expiration | ||
| * @param {function} refresh | ||
| * @param {function} cb | ||
| */ | ||
| self.set = function(key, value, expiration, cb){ | ||
| log(false, 'Attempting to set key:', {key: key, value: value}); | ||
| self.set = function(){ | ||
| if(arguments.length < 2){ | ||
| throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.set() requires a minimum of 2 arguments.'); | ||
| } | ||
| var key = arguments[0]; | ||
| var value = arguments[1]; | ||
| var expiration = arguments[2] || null; | ||
| var refresh = (arguments.length == 5) ? arguments[3] : null; | ||
| var cb = (arguments.length == 5) ? arguments[4] : arguments[3]; | ||
| cb = cb || noop; | ||
| log(false, '.set() called:', {key: key, value: value}); | ||
| try { | ||
| if(!self.readOnly){ | ||
| expiration = expiration || self.defaultExpiration; | ||
| cb = cb || noop; | ||
| var exp = (expiration * 1000) + Date.now(); | ||
| self.db.set(key, value, expiration, cb); | ||
| if(refresh){ | ||
| refreshKeys[key] = {expiration: exp, lifeSpan: expiration, refresh: refresh}; | ||
| backgroundRefreshInit(); | ||
| } | ||
| } | ||
| } catch (err) { | ||
| log(true, 'Set failed for cache of type ' + self.type, {name: 'NodeCacheSetException', message: err}); | ||
| log(true, '.set() failed for cache of type ' + self.type, {name: 'NodeCacheSetException', message: err}); | ||
| } | ||
@@ -90,3 +116,6 @@ } | ||
| self.mset = function(obj, expiration, cb){ | ||
| log(false, 'Attempting to mset data:', {data: obj}); | ||
| if(arguments.length < 1){ | ||
| throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.mset() requires a minimum of 1 argument.'); | ||
| } | ||
| log(false, '.mset() called:', {data: obj}); | ||
| for(key in obj){ | ||
@@ -112,3 +141,6 @@ if(obj.hasOwnProperty(key)){ | ||
| self.del = function(keys, cb){ | ||
| log(false, 'Attempting to delete keys:', {keys: keys}); | ||
| if(arguments.length < 1){ | ||
| throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.del() requires a minimum of 1 argument.'); | ||
| } | ||
| log(false, '.del() called:', {keys: keys}); | ||
| try { | ||
@@ -120,4 +152,13 @@ self.db.del(keys, function (err, count){ | ||
| }); | ||
| if(typeof keys === 'object'){ | ||
| for(var i = 0; i < keys.length; i++){ | ||
| var key = keys[i]; | ||
| delete refreshKeys[key]; | ||
| } | ||
| } | ||
| else{ | ||
| delete refreshKeys[keys]; | ||
| } | ||
| } catch (err) { | ||
| log(true, 'Delete failed for cache of type ' + this.type, err); | ||
| log(true, '.del() failed for cache of type ' + this.type, err); | ||
| } | ||
@@ -131,8 +172,8 @@ } | ||
| self.flush = function(cb){ | ||
| log(false, 'Attempting to flush all data.'); | ||
| log(false, '.flush() called'); | ||
| try { | ||
| self.db.flushAll(); | ||
| log(false, 'Flushing all data from cache of type ' + this.type); | ||
| refreshKeys = {}; | ||
| } catch (err) { | ||
| log(true, 'Flush failed for cache of type ' + this.type, err); | ||
| log(true, '.flush() failed for cache of type ' + this.type, err); | ||
| } | ||
@@ -161,2 +202,48 @@ if(cb) cb(); | ||
| /** | ||
| * Initialize background refresh | ||
| */ | ||
| function backgroundRefreshInit(){ | ||
| if(!backgroundRefreshEnabled){ | ||
| backgroundRefreshEnabled = true; | ||
| if(self.backgroundRefreshIntervalCheck){ | ||
| if(self.backgroundRefreshInterval > self.backgroundRefreshMinTtl){ | ||
| throw new exception('BACKGROUND_REFRESH_INTERVAL_EXCEPTION', 'backgroundRefreshInterval cannot be greater than backgroundRefreshMinTtl.'); | ||
| } | ||
| } | ||
| setInterval(function(){ | ||
| backgroundRefresh(); | ||
| }, self.backgroundRefreshInterval); | ||
| } | ||
| } | ||
| /** | ||
| * Refreshes all keys that were set with a refresh function | ||
| */ | ||
| function backgroundRefresh(){ | ||
| for(key in refreshKeys){ | ||
| if(refreshKeys.hasOwnProperty(key)){ | ||
| var data = refreshKeys[key]; | ||
| if(data.expiration - Date.now() < self.backgroundRefreshMinTtl){ | ||
| data.refresh(key, function (err, response){ | ||
| if(!err){ | ||
| self.set(key, response, data.lifeSpan, data.refresh, noop); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Instantates an exception to be thrown | ||
| * @param {string} name | ||
| * @param {string} message | ||
| * @return {exception} | ||
| */ | ||
| function exception(name, message){ | ||
| this.name = name; | ||
| this.message = message; | ||
| } | ||
| /** | ||
| * Error logging logic | ||
@@ -163,0 +250,0 @@ * @param {boolean} isError |
+1
-1
| { | ||
| "name": "cache-service-node-cache", | ||
| "version": "1.0.1", | ||
| "version": "1.1.0", | ||
| "description": "A node-cache plugin for cache-service.", | ||
@@ -5,0 +5,0 @@ "main": "nodeCacheModule.js", |
+81
-14
@@ -6,2 +6,11 @@ # cache-service-node-cache | ||
| #### Features | ||
| * Background refresh | ||
| * Robust API | ||
| * Built-in logging with a `verbose` flag. | ||
| * Compatible with `cache-service` and `superagent-cache` | ||
| * Public access to the underlying `node-cache` instance | ||
| * Excellent `.mset()` implementation which allows you to set expirations on a per key, per function call, and/or per `cache-service-cache-module` instance basis. | ||
| # Basic Usage | ||
@@ -22,11 +31,6 @@ | ||
| # Benefits of Using `cache-service-node-cache` | ||
| # Cache Module Configuration Options | ||
| If you're using `cache-service-node-cache` with `cache-service`, the benefits are obvious. However, there are also a couple of reasons you might prefer it to using vanilla [node-cache](https://www.npmjs.com/package/node-cache): | ||
| `cache-service-node-cache`'s constructor takes an optional config object with any number of the following properties: | ||
| * It adds an excellent `.mset()` implementation which allow you to set expirations on a per key, per function call, and/or per `cache-service-node-cache` instance basis (Vanilla node-cache does not offer `.mset()` at all). | ||
| * Built-in logging with a `verbose` flag. | ||
| # Cache Module Configuration Options | ||
| ## type | ||
@@ -47,2 +51,25 @@ | ||
| ## backgroundRefreshInterval | ||
| How frequently should all background refresh-enabled keys be scanned to determine whether they should be refreshed. For a more thorough explanation on `background refresh`, see the [Using Background Refresh](#using-background-refresh) section. | ||
| * type: int | ||
| * default: 60000 | ||
| * measure: milliseconds | ||
| ## backgroundRefreshMinTtl | ||
| The maximum ttl a scanned background refresh-enabled key can have without triggering a refresh. This number should always be greater than `backgroundRefreshInterval`. | ||
| * type: int | ||
| * default: 70000 | ||
| * measure: milliseconds | ||
| ## backgroundRefreshIntervalCheck | ||
| Whether to throw an exception if `backgroundRefreshInterval` is greater than `backgroundRefreshMinTtl`. Setting this property to false is highly discouraged. | ||
| * type: boolean | ||
| * default: true | ||
| ## verbose | ||
@@ -79,4 +106,6 @@ | ||
| ## .set(key, value [, expiraiton, callback]) | ||
| ## .set(key, value, [expiraiton], [refresh(key, cb)], [callback]) | ||
| > See the [Using Background Refresh](#using-background-refresh) section for more about the `refresh` and `callback` params. | ||
| Set a value by a given key. | ||
@@ -87,5 +116,6 @@ | ||
| * expiration: type: int, measure: seconds | ||
| * refresh: type: function | ||
| * callback: type: function | ||
| ## .mset(obj [, expiration, callback]) | ||
| ## .mset(obj, [expiration], [callback]) | ||
@@ -102,3 +132,3 @@ Set multiple values to multiple keys | ||
| ## .del(keys [, callback (err, count)]) | ||
| ## .del(keys, [callback (err, count)]) | ||
@@ -118,9 +148,46 @@ Delete a key or an array of keys and their associated values. | ||
| # More Node-Cache Methods | ||
| ## .db | ||
| If you need access to one of node-cache's other functions, you can get at the underlying [`node-cache` instance](https://github.com/tcs-de/nodecache) by tapping into the `.db` property like so: | ||
| This is the underlying [`node-cache` instance](https://github.com/tcs-de/nodecache). If needed, you can access `node-cache` functions I haven't abstracted. | ||
| # Using Background Refresh | ||
| With a typical cache setup, you're left to find the perfect compromise between having a long expiration so that users don't have to suffer through the worst case load time, and a short expiration so data doesn't get stale. `cache-service-cache-module` eliminates the need to worry about users suffering through the longest wait time by automatically refreshing keys for you. Here's how it works: | ||
| #### How do I turn it on? | ||
| By default, background refresh is off. It will turn itself on the first time you pass a `refresh` param to `.set()`. | ||
| #### Configure | ||
| There are three options you can manipulate. See the API section for more information about them. | ||
| * `backgroundRefreshInterval` | ||
| * `backgroundRefreshMinTtl` | ||
| * `backgroundRefreshIntervalCheck` | ||
| #### Use | ||
| Background refresh is exposed via the `.set()` command as follows: | ||
| ```javascript | ||
| var underlyingNodeCacheInstance = nodeCacheModule.db; | ||
| underlyingNodeCacheInstance.SOME_OTHER_NODE_CACHE_FUNCTION(); | ||
| cacheModule.set('key', 'value', 300, refresh, cb); | ||
| ``` | ||
| If you want to pass `refresh`, you must also pass `cb` because if only four params are passed, `cache-service-node-cache` will assume the fourth param is `cb`. | ||
| #### The Refresh Param | ||
| ###### refresh(key, cb(err, response)) | ||
| * key: type: string: this is the key that is being refreshed | ||
| * cb: type: function: you must trigger this function to pass the data that should replace the current key's value | ||
| The `refresh` param MUST be a function that accepts `key` and a callback function that accepts `err` and `response` as follows: | ||
| ```javascript | ||
| var refresh = function(key, cb){ | ||
| var response = goGetData(); | ||
| cb(null, response); | ||
| } | ||
| ``` |
| var expect = require('expect'); | ||
| var ncModule = require('../../nodeCacheModule'); | ||
| var nodeCache = new ncModule(); | ||
| var nodeCache = new ncModule({ | ||
| backgroundRefreshInterval: 500 | ||
| }); | ||
@@ -67,2 +69,15 @@ var key = 'key'; | ||
| }); | ||
| it('Using background refresh should reset a nearly expired key', function (done) { | ||
| var refresh = function(key, cb){ | ||
| cb(null, 1); | ||
| } | ||
| nodeCache.set(key, value, 1, refresh, function (err, result){ | ||
| setTimeout(function(){ | ||
| nodeCache.get(key, function (err, response){ | ||
| expect(response).toBe(1); | ||
| done(); | ||
| }); | ||
| }, 1500); | ||
| }); | ||
| }); | ||
| }); |
17548
46.93%319
45%187
55.83%