Comparing version 0.2.2 to 0.2.5
module.exports = { | ||
CacheCluster: require('./lib/CacheCluster'), | ||
CacheInstance: require('./lib/CacheInstance'), | ||
ConnectionPool: require('./lib/ConnectionPool'), | ||
ConnectionWrapper: require('./lib/ConnectionWrapper'), | ||
MemcacheConnection: require('./lib/MemcacheConnection'), | ||
InMemoryCache: require('./lib/InMemoryCache'), | ||
RedisConnection: require('./lib/RedisConnection'), | ||
RedundantCacheGroup: require('./lib/RedundantCacheGroup') | ||
} | ||
} |
var events = require('events') | ||
var util = require('util') | ||
var metrics = require('metrics') | ||
var util = require('util') | ||
var Q = require('kew') | ||
function CacheInstance() {} | ||
/** | ||
* A generic cache instance. | ||
* @constructor | ||
*/ | ||
function CacheInstance() { | ||
this._stats = { | ||
get: new metrics.Timer, | ||
set: new metrics.Timer, | ||
mget: new metrics.Timer, | ||
mset: new metrics.Timer, | ||
del: new metrics.Timer | ||
} | ||
} | ||
util.inherits(CacheInstance, events.EventEmitter) | ||
/** | ||
* Indicate if this cache instance is available. | ||
* | ||
* @return{boolean} | ||
*/ | ||
CacheInstance.prototype.isAvailable = function () { | ||
@@ -11,2 +31,5 @@ throw new Error("isAvailable() must be implemented by any class extending CacheInstance") | ||
/** | ||
* Connect this cache instance. | ||
*/ | ||
CacheInstance.prototype.connect = function () { | ||
@@ -16,2 +39,5 @@ throw new Error("connect() must be implemented by any class extending CacheInstance") | ||
/** | ||
* Disconnect this cache instance. | ||
*/ | ||
CacheInstance.prototype.disconnect = function () { | ||
@@ -21,2 +47,5 @@ throw new Error("disconnect() must be implemented by any class extending CacheInstance") | ||
/** | ||
* Destroy this cache instance. | ||
*/ | ||
CacheInstance.prototype.destroy = function () { | ||
@@ -26,2 +55,9 @@ throw new Error("destroy() must be implemented by any class extending CacheInstance") | ||
/** | ||
* Get the values of multiple keys. | ||
* | ||
* @param {Array.<string>} keys A list of keys | ||
* @return {Promise.<Array.<string>>} The fetched values. For keys that do not exist, | ||
* returns 'undefined's. | ||
*/ | ||
CacheInstance.prototype.mget = function (keys) { | ||
@@ -31,2 +67,19 @@ throw new Error("mget() must be implemented by any class extending CacheInstance") | ||
/** | ||
* Set values to multiple keys. | ||
* | ||
* @param {Array.<{key: string, value: string}} keys Key-value pairs to set. | ||
* @param {number=} maxAgeMs The living time of keys, in milliseconds. | ||
* @return {Promise} | ||
*/ | ||
CacheInstance.prototype.mset = function (keys, maxAgeMs) { | ||
throw new Error("mget() must be implemented by any class extending CacheInstance") | ||
} | ||
/** | ||
* Get the value of a given key. | ||
* | ||
* @param {string} key The key to get value of. | ||
* @return {Promise.<string>} The fetched value. Returns 'undefined' if the doesn't exist. | ||
*/ | ||
CacheInstance.prototype.get = function (key) { | ||
@@ -36,2 +89,10 @@ throw new Error("get() must be implemented by any class extending CacheInstance") | ||
/** | ||
* Set a key. | ||
* | ||
* @param {string} key | ||
* @param {string} value | ||
* @param {number=} maxAgeMs The living time of this key, in milliseconds. | ||
* @return {Promise} | ||
*/ | ||
CacheInstance.prototype.set = function (key, val, maxAgeMs) { | ||
@@ -41,2 +102,8 @@ throw new Error("set() must be implemented by any class extending CacheInstance") | ||
/** | ||
* Delete a key. | ||
* | ||
* @param {string} key | ||
* @param {Promise} | ||
*/ | ||
CacheInstance.prototype.del = function (key) { | ||
@@ -46,2 +113,52 @@ throw new Error("del() must be implemented by any class extending CacheInstance") | ||
module.exports = CacheInstance | ||
/** | ||
* Get service information about this cache instance if it is a standlone service. | ||
* | ||
* @param {string} key | ||
* @param {Promise.<ServerInfo>} A promise that returns the server information. Returns | ||
* null if the server info is not available. | ||
*/ | ||
CacheInstance.prototype.getServerInfo = function (key) { | ||
return Q.resolve(null) | ||
} | ||
/** | ||
* Update the stats of a certain operation on this cache. | ||
* | ||
* @param {string} op The name of the operation, e.g., get, set, etc. | ||
* @return {Function} A function that updates the stats and returns the parameter | ||
* that is passed into it. It is handy to chain to a promise. | ||
*/ | ||
CacheInstance.prototype.updateStats = function(op) { | ||
var startTime = Date.now() | ||
var self = this | ||
return function (result) { | ||
if (self._stats[op]) self._stats[op].update(Date.now() - startTime) | ||
return result | ||
} | ||
} | ||
/** | ||
* Get the stats of a certain operation. | ||
* | ||
* @param {string} op The name of the operation, e.g., get, set, etc. | ||
* @return {metrics.Timer} | ||
*/ | ||
CacheInstance.prototype.getStats = function (op) { | ||
return this._stats[op] | ||
} | ||
/** | ||
* Get the stats of a certain operation in a human-readable format. | ||
* | ||
* @param {string} op The name of the operation, e.g., get, set, etc. | ||
* @return {string} | ||
*/ | ||
CacheInstance.prototype.getPrettyStatsString = function (op) { | ||
var m = this.getStats(op) | ||
return util.format('%d ops min/max/avg %d/%d/%d 1min/5min/15min %d/%d/%d', | ||
m.count(), m.min(), m.max(), m.mean(), | ||
m.oneMinuteRate(), m.fiveMinuteRate(), m.fifteenMinuteRate()) | ||
} | ||
module.exports = CacheInstance |
@@ -6,3 +6,12 @@ var redis = require('redis') | ||
var CacheInstance = require('./CacheInstance') | ||
var ServerInfo = require('./ServerInfo') | ||
/** | ||
* A connection to a Redis server. | ||
* | ||
* @constructor | ||
* @param {string} host The host that runs the redis-server | ||
* @param {string} port The port that the redis-server listens to | ||
* @implements {CacheInstance} | ||
*/ | ||
function RedisConnection(host, port) { | ||
@@ -13,6 +22,4 @@ CacheInstance.call(this) | ||
this._client = null | ||
this._host = host || null | ||
this._port = port || null | ||
this._bound_onConnect = this._onConnect.bind(this) | ||
@@ -24,2 +31,3 @@ this._bound_onError = this._onError.bind(this) | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.isAvailable = function () { | ||
@@ -29,10 +37,12 @@ return this._isAvailable | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.set = function (key, val, maxAgeMs) { | ||
var deferred = Q.defer() | ||
this._client.setex(key, Math.floor(maxAgeMs / 1000), val, deferred.makeNodeResolver()) | ||
return deferred.promise | ||
.fail(warnOnError) | ||
.then(this.updateStats('set')) | ||
} | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.mset = function (items, maxAgeMs) { | ||
@@ -53,17 +63,22 @@ var deferred = Q.defer() | ||
.fail(warnOnError) | ||
.then(this.updateStats('mset')) | ||
} | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.del = function (key) { | ||
var deferred = Q.defer() | ||
this._client.del(key, deferred.makeNodeResolver()) | ||
return deferred.promise | ||
.fail(warnOnError) | ||
.then(this.updateStats('del')) | ||
} | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.get = function (key) { | ||
return this.mget([key]) | ||
.then(returnFirstResult) | ||
.then(this.updateStats('get')) | ||
} | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.mget = function (keys) { | ||
@@ -73,7 +88,34 @@ if (!keys || !keys.length) return Q.resolve([]) | ||
var deferred = Q.defer() | ||
var args = keys.concat(deferred.makeNodeResolver()) | ||
this._client.mget.apply(this._client, args) | ||
this._client.mget(keys, deferred.makeNodeResolver()) | ||
return deferred.promise | ||
.then(this.updateStats('mget')) | ||
} | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.getServerInfo = function () { | ||
var deferred = Q.defer() | ||
this._client.info(deferred.makeNodeResolver()) | ||
return deferred.promise | ||
.then(function (infoCmdOutput) { | ||
var items = {} | ||
infoCmdOutput.split('\n') | ||
.filter(function(str) {return str.indexOf(':') > 0}) | ||
.map(function(str) {return str.trim().split(':')}) | ||
.map(function(item) {items[item[0]] = item[1]}) | ||
var serverInfo = new ServerInfo | ||
try { | ||
serverInfo.memoryBytes = parseInt(items['used_memory'], 10) | ||
serverInfo.memoryRssBytes = parseInt(items['used_memory_rss'], 10) | ||
serverInfo.evictedKeys = parseInt(items['evicted_keys'], 10) | ||
serverInfo.numOfConnections = parseInt(items['connected_clients'], 10) | ||
// The db0 key's value is something like: 'keys=12,expires=20' | ||
serverInfo.numOfKeys = parseInt(items['db0'].split(',')[0].split('=')[1], 10) | ||
} catch (e) { | ||
Q.reject('Malformatted output from the "INFO" command of Redis') | ||
} | ||
return Q.resolve(serverInfo) | ||
}) | ||
} | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.disconnect = function () { | ||
@@ -85,2 +127,3 @@ this._isAvailable = false | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.destroy = function () { | ||
@@ -92,2 +135,3 @@ this.disconnect() | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.connect = function () { | ||
@@ -94,0 +138,0 @@ if (this._isAvailable) return |
@@ -27,8 +27,13 @@ var util = require('util') | ||
RedundantCacheGroup.prototype._getAvailableInstance = function () { | ||
for (var i = 0; i < this._cacheInstances.length; i++) { | ||
RedundantCacheGroup.prototype._getAvailableInstance = function (start) { | ||
var instanceIndex = this._getIndexOfFirstAvailableInstance(start) | ||
if (this._cacheInstances[instanceIndex]) return this._cacheInstances[instanceIndex].instance | ||
return null | ||
} | ||
RedundantCacheGroup.prototype._getIndexOfFirstAvailableInstance = function (start) { | ||
for (var i = (start || 0); i < this._cacheInstances.length; i++) { | ||
var instance = this._cacheInstances[i].instance | ||
if (instance.isAvailable()) { | ||
return instance | ||
} | ||
if (instance.isAvailable()) return i | ||
} | ||
@@ -75,20 +80,46 @@ return null | ||
RedundantCacheGroup.prototype.mget = function (keys) { | ||
var instance = this._getAvailableInstance() | ||
if (!instance) return Q.resolve([]) | ||
var instanceIndex = this._getIndexOfFirstAvailableInstance() | ||
if (instanceIndex == null) return Q.resolve([]) | ||
return instance.mget(keys) | ||
// try the first cache | ||
var ret = this._getAvailableInstance(instanceIndex).mget(keys) | ||
var nextInstance = this._getAvailableInstance(instanceIndex + 1) | ||
return ret.then(function (res) { | ||
for (var i = 0; i < res.length; i++) { | ||
if (!res[i] && nextInstance) { | ||
var rollUp = [] | ||
while (!res[i] && i < res.length) rollUp.push(keys[i++]) | ||
nextInstance.mget(rollUp) | ||
.then(function (data) { | ||
for (var k = 0; k < data.length; k++) res[k] = data[k] | ||
}) | ||
} | ||
} | ||
return res | ||
}.bind(this)) | ||
} | ||
RedundantCacheGroup.prototype.get = function (key) { | ||
var instance = this._getAvailableInstance() | ||
if (!instance) return Q.resolve(undefined) | ||
var instanceIndex = this._getIndexOfFirstAvailableInstance() | ||
return instance.get(key) | ||
for (var i = instanceIndex, instance; instance = this._getAvailableInstance(i); i++) { | ||
var ret = instance.get(key) | ||
if (ret) return Q.resolve(ret) | ||
} | ||
return Q.resolve(undefined) | ||
} | ||
RedundantCacheGroup.prototype.mset = function (items, maxAgeMs) { | ||
var instance = this._getAvailableInstance() | ||
if (!instance) return Q.resolve(undefined) | ||
var instances = this._getAllInstances() | ||
var promises = [] | ||
return instance.mset(items, maxAgeMs) | ||
for (var i = 0; i < instances.length; i++) { | ||
promises.push(instances[i].mset(items, maxAgeMs)) | ||
} | ||
return Q.all(promises) | ||
.then(returnTrue) | ||
} | ||
@@ -99,2 +130,3 @@ | ||
var promises = [] | ||
for (var i = 0; i < instances.length; i++) { | ||
@@ -128,2 +160,2 @@ promises.push(instances[i].set(key, val, maxAgeMs)) | ||
module.exports = RedundantCacheGroup | ||
module.exports = RedundantCacheGroup |
{ | ||
"name": "zcache" | ||
, "description": "AWS zone-aware caching" | ||
, "version": "0.2.2" | ||
, "homepage": "https://github.com/azulus/zcache" | ||
, "authors": [ | ||
"Jeremy Stanley <github@azulus.com> (https://github.com/azulus)" | ||
] | ||
, "contributors": [ | ||
] | ||
, "keywords": ["zcache"] | ||
, "main": "lib/zcache.js" | ||
, "repository": { | ||
"type": "git" | ||
, "url": "https://github.com/azulus/zcache.git" | ||
"name": "zcache", | ||
"description": "AWS zone-aware multi-layer cache", | ||
"version": "0.2.5", | ||
"homepage": "https://github.com/Obvious/zcache", | ||
"authors": [ | ||
"Jeremy Stanley <github@azulus.com> (https://github.com/azulus)", | ||
"Artem Titoulenko <artem@medium.com> (https://github.com/ArtemTitoulenko)", | ||
"Xiao Ma <x@medium.com> (https://github.com/x-ma)" | ||
], | ||
"keywords": ["zcache", "cache", "redis"], | ||
"main": "lib/zcache.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/Obvious/zcache.git" | ||
}, | ||
"dependencies": { | ||
"node-memcache-parser-obvfork": "0.1.1", | ||
"generic-pool": "2.0.3", | ||
"kew": "0.1.7", | ||
"redis": "0.8.2", | ||
"metrics": "0.1.6" | ||
}, | ||
"devDependencies": { | ||
"nodeunit": "0.7.4" | ||
}, | ||
"scripts": { | ||
"test": "./node_modules/nodeunit/bin/nodeunit test" | ||
} | ||
, "dependencies": { | ||
"node-memcache-parser-obvfork": "0.1.1", | ||
"generic-pool": "2.0.3", | ||
"kew": "0.1.7", | ||
"redis": "0.8.2" | ||
} | ||
, "devDependencies": { | ||
"nodeunit": "0.7.4" | ||
} | ||
, "scripts": { | ||
"test": "./node_modules/nodeunit/bin/nodeunit test" | ||
} | ||
} |
var zcache = require('../index') | ||
var ServerInfo = require('../lib/ServerInfo') | ||
var Q = require('kew') | ||
exports.testRedisConnection = function (test) { | ||
var cacheInstance = new zcache.RedisConnection("localhost", 6379) | ||
var cacheInstance = new zcache.RedisConnection('localhost', 6379) | ||
test.equal(cacheInstance.isAvailable(), false, "Connection should not be available") | ||
test.equal(cacheInstance.isAvailable(), false, 'Connection should not be available') | ||
@@ -12,3 +13,3 @@ cacheInstance.on('connect', function () { | ||
test.equal(cacheInstance.isAvailable(), true, "Connection should be available") | ||
test.equal(cacheInstance.isAvailable(), true, 'Connection should be available') | ||
@@ -62,5 +63,14 @@ cacheInstance.set('abc', '123', 300000) | ||
.then(function (vals) { | ||
test.equal(vals.length, 2, "Should have precisely 2 results") | ||
test.equal(vals.length, 2, 'Should have precisely 2 results') | ||
test.equal(vals[0], '456') | ||
test.equal(vals[1], '789') | ||
test.equal(1, cacheInstance.getStats('set').count(), 'set() is called for once') | ||
test.equal(1, cacheInstance.getStats('mset').count(), 'mset() is called for once') | ||
test.equal(0, cacheInstance.getStats('get').count(), 'get() is not called') | ||
test.equal(3, cacheInstance.getStats('mget').count(), 'mget() is called for three times') | ||
test.equal(1, cacheInstance.getStats('del').count(), 'del() is call for once') | ||
return cacheInstance.getServerInfo() | ||
}) | ||
.then(function (info) { | ||
test.ok(info instanceof ServerInfo, 'The returned object should be a ServerInfo') | ||
cacheInstance.destroy() | ||
@@ -76,3 +86,3 @@ }) | ||
cacheInstance.on('destroy', function () { | ||
test.equal(cacheInstance.isAvailable(), false, "Connection should not be available") | ||
test.equal(cacheInstance.isAvailable(), false, 'Connection should not be available') | ||
test.done() | ||
@@ -79,0 +89,0 @@ }) |
var zcache = require('../index') | ||
var Q = require('kew') | ||
exports.testRedundantCacheGroup = function (test) { | ||
var memcacheCluster = new zcache.CacheCluster({ | ||
create: function (uri, opts, callback) { | ||
var parts = uri.split(':') | ||
var host = parts[0] | ||
var port = parseInt(parts[1], 10) | ||
module.exports = { | ||
setUp: function (callback) { | ||
this.memoryInstance1 = new zcache.InMemoryCache() | ||
this.memoryInstance1.connect() | ||
this.memoryInstance2 = new zcache.InMemoryCache() | ||
this.memoryInstance2.connect() | ||
var poolInstance = new zcache.ConnectionPool({ | ||
create: function (callback) { | ||
var wrappedCacheInstance = new zcache.MemcacheConnection(host, port) | ||
var wrapperCacheInstance = new zcache.ConnectionWrapper(wrappedCacheInstance) | ||
wrapperCacheInstance.on('connect', function () { | ||
callback(null, wrapperCacheInstance) | ||
}) | ||
this.cacheInstance = new zcache.RedundantCacheGroup() | ||
this.cacheInstance.add(this.memoryInstance1, 2) | ||
this.cacheInstance.add(this.memoryInstance2, 1) | ||
callback() | ||
}, | ||
wrapperCacheInstance.connect() | ||
}, | ||
destroy: function (client) { | ||
client.destroy() | ||
} | ||
}) | ||
tearDown: function (callback) { | ||
if (this.cacheInstance) this.cacheInstance.destroy() | ||
callback() | ||
}, | ||
poolInstance.on('connect', function () { | ||
callback(null, poolInstance) | ||
}) | ||
testCacheWriteThrough: function (test) { | ||
this.cacheInstance.set('foo', 'bar', 10000) | ||
test.equal(this.memoryInstance1.get('foo'), 'bar', 'bar should be in memory') | ||
test.equal(this.memoryInstance2.get('foo'), 'bar', 'bar should be in memory') | ||
test.done() | ||
}, | ||
poolInstance.connect() | ||
} | ||
}) | ||
memcacheCluster.setNodeCapacity('localhost:11212', 5, {memcache: true}, 0) | ||
memcacheCluster.setNodeCapacity('localhost:11213', 5, {memcache: true}, 0) | ||
testCacheGetStagger: function (test) { | ||
this.cacheInstance.set('foo', 'bar', 10000) | ||
this.memoryInstance1.del('foo') | ||
test.equal(this.memoryInstance1._data['foo'], undefined, 'foo should have been deleted in memoryInstance1') | ||
test.equal(this.memoryInstance2._data['foo'], 'bar', 'foo should still be in memoryInstance2') | ||
var redisInstance = new zcache.RedisConnection('localhost', 6379) | ||
this.cacheInstance.get('foo') | ||
.then(function (data) { | ||
test.equal(data, 'bar', 'bar should have been read from memoryInstance2') | ||
test.done() | ||
}) | ||
}, | ||
var cacheInstance = new zcache.RedundantCacheGroup() | ||
cacheInstance.add(redisInstance, 1) | ||
cacheInstance.add(memcacheCluster, 2) | ||
testCacheMgetStagger: function (test) { | ||
this.cacheInstance.set('foo', 'bar', 10000) | ||
this.cacheInstance.set('fah', 'baz', 10000) | ||
this.memoryInstance1.del('foo') | ||
test.equal(this.memoryInstance1._data['foo'], undefined, 'foo should have been deleted in memoryInstance1') | ||
test.equal(this.memoryInstance2._data['foo'], 'bar', 'foo should still be in memoryInstance2') | ||
test.equal(cacheInstance.isAvailable(), false, "Connection should not be available") | ||
this.cacheInstance.mget(['foo', 'fah']) | ||
.then(function (results) { | ||
test.equal(results[0], 'bar', 'bar should have been returned') | ||
test.equal(results[1], 'baz', 'baz should have been returned') | ||
test.done() | ||
}) | ||
}, | ||
cacheInstance.on('connect', function () { | ||
cacheInstance.removeAllListeners('connect') | ||
testCacheMsetStagger: function (test) { | ||
this.cacheInstance.mset([{key: 'foo', value: 'bar'}, {key: 'fah', value: 'bah'}]) | ||
var defer = Q.defer() | ||
setTimeout(function () { | ||
defer.resolve(true) | ||
}, 100) | ||
test.equal(this.memoryInstance1._data['foo'], 'bar', 'bar should be in memoryInstance1') | ||
test.equal(this.memoryInstance2._data['foo'], 'bar', 'bar should be in memoryInstance2') | ||
defer | ||
.then(function () { | ||
test.equal(cacheInstance.isAvailable(), true, "Connection should be available") | ||
test.equal(this.memoryInstance1._data['fah'], 'bah', 'bah should be in memoryInstance1') | ||
test.equal(this.memoryInstance2._data['fah'], 'bah', 'bah should be in memoryInstance2') | ||
var promises = [] | ||
promises.push(cacheInstance.set('abc', '123', 300000)) | ||
promises.push(cacheInstance.set('def', '456', 300000)) | ||
promises.push(cacheInstance.set('ghi', '789', 300000)) | ||
promises.push(cacheInstance.set('jkl', '234', 300000)) | ||
promises.push(cacheInstance.set('mno', '567', 300000)) | ||
test.done() | ||
}, | ||
return Q.all(promises) | ||
testCacheDelStagger: function (test) { | ||
this.cacheInstance.set('foo', 'bar', 10000) | ||
this.cacheInstance.get('foo') | ||
.then(function (data) { | ||
test.equal(data, 'bar', 'bar should be in the cache') | ||
}) | ||
.then(function () { | ||
return cacheInstance.mget(['abc', 'def', 'ghi', 'jkl', 'mno']) | ||
}) | ||
.then(function (vals) { | ||
test.equal(vals[0], '123') | ||
test.equal(vals[1], '456') | ||
test.equal(vals[2], '789') | ||
test.equal(vals[3], '234') | ||
test.equal(vals[4], '567') | ||
this.cacheInstance.del('foo') | ||
//disconnect the memcache cluster | ||
memcacheCluster.disconnect() | ||
// wait to ensure disconnection | ||
var defer = Q.defer() | ||
setTimeout(function () { | ||
defer.resolve(true) | ||
}, 100) | ||
return defer.promise | ||
}) | ||
.then(function () { | ||
return cacheInstance.del('abc') | ||
}) | ||
.then(function () { | ||
return cacheInstance.mget(['abc']) | ||
}) | ||
.then(function (vals) { | ||
test.equal(vals[0], undefined) | ||
// reconnect the memcache cluster | ||
memcacheCluster.connect() | ||
// wait to ensure reconnection | ||
var defer = Q.defer() | ||
setTimeout(function () { | ||
defer.resolve(true) | ||
}, 200) | ||
return defer.promise | ||
}) | ||
.then(function () { | ||
return cacheInstance.mget(['abc']) | ||
}) | ||
.then(function (val) { | ||
test.equal(val, '123') | ||
cacheInstance.destroy() | ||
}) | ||
.fail(function (e) { | ||
console.error(e) | ||
test.fail(e.message, e.stack) | ||
this.cacheInstance.get('foo') | ||
.then(function (data) { | ||
test.equal(data, undefined, 'bar should not be in the cache') | ||
test.done() | ||
}) | ||
}) | ||
cacheInstance.on('destroy', function () { | ||
test.equal(cacheInstance.isAvailable(), false, "Connection should not be available") | ||
test.done() | ||
}) | ||
cacheInstance.connect() | ||
} | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
84070
1961
1
5
23
1
+ Addedmetrics@0.1.6
+ Addedmetrics@0.1.6(transitive)