Comparing version 0.3.0-beta to 0.3.1
@@ -7,2 +7,3 @@ module.exports = { | ||
MemcacheConnection: require('./lib/MemcacheConnection'), | ||
MultiplexingCache: require('./lib/MultiplexingCache'), | ||
InMemoryCache: require('./lib/InMemoryCache'), | ||
@@ -9,0 +10,0 @@ RedisConnection: require('./lib/RedisConnection'), |
@@ -16,9 +16,7 @@ // Copyright 2014 The Obvious Corporation. | ||
/** | ||
* @param {Object=} options Additional options for this connection. | ||
* 'requestTimeoutMs' specifies the timeout of a cache request. | ||
* @constructor | ||
* @extends {CacheInstance} | ||
*/ | ||
function CacheCluster(options) { | ||
CacheInstance.call(this, options) | ||
function CacheCluster() { | ||
CacheInstance.call(this) | ||
@@ -70,3 +68,3 @@ this._isConnected = false | ||
/** @inheritDoc */ | ||
/** @override */ | ||
CacheCluster.prototype.isAvailable = function () { | ||
@@ -79,3 +77,3 @@ for (var uri in this._servers) { | ||
/** @inheritDoc */ | ||
/** @override */ | ||
CacheCluster.prototype.connect = function () { | ||
@@ -90,3 +88,3 @@ this._isConnected = true | ||
/** @inheritDoc */ | ||
/** @override */ | ||
CacheCluster.prototype.disconnect = function () { | ||
@@ -101,3 +99,3 @@ this._isConnected = false | ||
/** @inheritDoc */ | ||
/** @override */ | ||
CacheCluster.prototype.destroy = function () { | ||
@@ -112,3 +110,3 @@ this.disconnect() | ||
/** @inheritDoc */ | ||
/** @override */ | ||
CacheCluster.prototype.mget = function (keys) { | ||
@@ -141,3 +139,3 @@ var keysPerInstance = {} | ||
} else { | ||
self.getPartialFailureCount('mget').inc() | ||
self._getPartialFailureCounter('mget').inc() | ||
throw new PartialResultError(values, errors) | ||
@@ -147,18 +145,18 @@ } | ||
return this._wrapPromiseWithTimeout(promise, 'mget') | ||
return this._wrapPromiseWithProfiling(promise, 'mget') | ||
} | ||
/** @inheritDoc */ | ||
/** @override */ | ||
CacheCluster.prototype.get = function (key) { | ||
var cacheInstance = this._servers[this._hashRing.get(key)] | ||
return this._wrapPromiseWithTimeout(cacheInstance.get(key), 'get') | ||
return this._wrapPromiseWithProfiling(cacheInstance.get(key), 'get') | ||
} | ||
/** @inheritDoc */ | ||
/** @override */ | ||
CacheCluster.prototype.set = function (key, val, maxAgeMs) { | ||
var cacheInstance = this._servers[this._hashRing.get(key)] | ||
return this._wrapPromiseWithTimeout(cacheInstance.set(key, val, maxAgeMs), 'set') | ||
return this._wrapPromiseWithProfiling(cacheInstance.set(key, val, maxAgeMs), 'set') | ||
} | ||
/** @inheritDoc */ | ||
/** @override */ | ||
CacheCluster.prototype.mset = function (items, maxAgeMs) { | ||
@@ -189,3 +187,3 @@ var itemsPerInstance = {} | ||
if (Object.keys(errors).length > 0) { | ||
self.getPartialFailureCount('mset').inc() | ||
self._getPartialFailureCounter('mset').inc() | ||
throw new PartialResultError({}, errors) | ||
@@ -195,6 +193,6 @@ } | ||
return this._wrapPromiseWithTimeout(promise, 'mset') | ||
return this._wrapPromiseWithProfiling(promise, 'mset') | ||
} | ||
/** @inheritDoc */ | ||
/** @override */ | ||
CacheCluster.prototype.del = function (key) { | ||
@@ -206,8 +204,45 @@ var cacheInstance = this._servers[this._hashRing.get(key)] | ||
/** | ||
* Get the sum of timeout count from all servers in the cluster. | ||
* | ||
* @param {string} op The name of the operation, e.g., get, set, etc. | ||
* @return {number} | ||
*/ | ||
CacheCluster.prototype.getTimeoutCount = function (op) { | ||
var total = 0 | ||
for (var uri in this._servers) total += this._servers[uri].getTimeoutCount(op) | ||
return total | ||
} | ||
/** @override */ | ||
CacheCluster.prototype.resetTimeoutCount = function (op) { | ||
for (var uri in this._servers) this._servers[uri].resetTimeoutCount(op) | ||
} | ||
/** | ||
* Get the partial failure count of a certain operation. | ||
* | ||
* @param {string} op The name of the operation, e.g., mget or mset. | ||
* @return {number} | ||
*/ | ||
CacheCluster.prototype.getPartialFailureCount = function (op) { | ||
return this._getPartialFailureCounter(op).count | ||
} | ||
/** | ||
* Reset the partial failure count of a certain operation. | ||
* | ||
* @param {string} op The name of the operation, e.g., mget or mset. | ||
* @return {number} | ||
*/ | ||
CacheCluster.prototype.resetPartialFailureCount = function (op) { | ||
return this._getPartialFailureCounter(op).clear() | ||
} | ||
/** | ||
* Get the Counter object that tracks the number of partial failures of a certain operation. | ||
* | ||
* @param {string} op The name of the operation, e.g., mget or mset. | ||
* @return {metrics.Counter} | ||
*/ | ||
CacheCluster.prototype.getPartialFailureCount = function (op) { | ||
CacheCluster.prototype._getPartialFailureCounter = function (op) { | ||
if (!this._partialFailureCount[op]) this._partialFailureCount[op] = new metrics.Counter() | ||
@@ -218,27 +253,17 @@ return this._partialFailureCount[op] | ||
/** | ||
* Wrap a promise and add two more features to it - latency measurement and timeout. | ||
* Wrap a promise and measure how long it takses to resolve/reject. | ||
* | ||
* @param {Promise} origPromise The promise to wrap. | ||
* @param {string} opName The name of the cache operation, e.g., 'get', 'set', etc. | ||
* @param {string=} timeoutMsg A message to put into the timeout error. | ||
* @return {Promise} A promise that behaves the same as the 'origPromise' with its latency | ||
* measured and also timeout. | ||
* measured. | ||
*/ | ||
CacheCluster.prototype._wrapPromiseWithTimeout = function (origPromise, opName, timeoutMsg) { | ||
var promiseWithTimeout = origPromise.timeout(this._reqTimeoutMs, 'Timeout Error') | ||
CacheCluster.prototype._wrapPromiseWithProfiling = function (origPromise, opName) { | ||
var startTime = Date.now() | ||
var self = this | ||
return promiseWithTimeout | ||
return origPromise | ||
.then(function (data) { | ||
self.getStats(opName).update(Date.now() - startTime) | ||
return data | ||
}, | ||
function (err) { | ||
if ('Timeout Error' === err.message) { | ||
self.getTimeoutCount(opName).inc() | ||
throw new TimeoutError(timeoutMsg || opName + ' Request timeout after ' + self._reqTimeoutMs + ' ms') | ||
} else { | ||
throw err | ||
} | ||
}) | ||
@@ -245,0 +270,0 @@ } |
@@ -197,10 +197,18 @@ // Copyright 2014 The Obvious Corporation. | ||
* @param {string} op The name of the operation, e.g., get, set, etc. | ||
* @return {metrics.Count} | ||
* @return {number} | ||
*/ | ||
CacheInstance.prototype.getTimeoutCount = function (op) { | ||
if (!this._timeoutCount[op]) this._timeoutCount[op] = new metrics.Counter() | ||
return this._timeoutCount[op] | ||
return this._getTimeoutCounter(op).count | ||
} | ||
/** | ||
* Reset the timeout count of a certain operation. | ||
* | ||
* @param {string} op The name of the operation, e.g., get, set, etc. | ||
*/ | ||
CacheInstance.prototype.resetTimeoutCount = function (op) { | ||
this._getTimeoutCounter(op).clear() | ||
} | ||
/** | ||
* Get the stats of a certain operation in a human-readable format. | ||
@@ -218,2 +226,14 @@ * | ||
/** | ||
* Get the Counter object that tracks the number of timeouts of a certain | ||
* operations. | ||
* | ||
* @param {string} op The name of the operation, e.g., get, set, etc. | ||
* @return {metrics.Counter} | ||
*/ | ||
CacheInstance.prototype._getTimeoutCounter = function (op) { | ||
if (!this._timeoutCount[op]) this._timeoutCount[op] = new metrics.Counter() | ||
return this._timeoutCount[op] | ||
} | ||
module.exports = CacheInstance |
@@ -10,2 +10,3 @@ // Copyright 2014 The Obvious Corporation. | ||
var CacheInstance = require('./CacheInstance') | ||
var TimeoutError = require('./TimeoutError') | ||
@@ -20,2 +21,4 @@ var CACHE_LATENCY_MS = 5 | ||
function FakeCache(logger) { | ||
CacheInstance.call(this) | ||
this.flush() | ||
@@ -25,2 +28,3 @@ this._logger = logger | ||
this._latencyMs = CACHE_LATENCY_MS | ||
this._nextFailure = null | ||
} | ||
@@ -31,3 +35,3 @@ util.inherits(FakeCache, CacheInstance) | ||
FakeCache.prototype.mget = function (keys) { | ||
if (this._failureCount > 0) return this._fakeFail() | ||
if (this._failureCount > 0) return this._fakeFail('mget') | ||
@@ -46,3 +50,4 @@ var self = this | ||
FakeCache.prototype.get = function (key) { | ||
if (this._failureCount > 0) return this._fakeFail() | ||
if (this._failureCount > 0) return this._fakeFail('get') | ||
this._logger.fine('FakeCache - get', key) | ||
@@ -61,7 +66,13 @@ var self = this | ||
FakeCache.prototype.del = function (key) { | ||
if (this._failureCount > 0) return this._fakeFail() | ||
if (this._failureCount > 0) return this._fakeFail('del') | ||
this._logger.fine('FakeCache - del', key) | ||
this._requestCounts.del += 1 | ||
delete this._data[key] | ||
return Q.resolve() | ||
var self = this | ||
// Add an artificial delay to mimic real world cache latency. | ||
return Q.delay(this._latencyMs) | ||
.then(function actualDel() { | ||
self._requestCounts.del += 1 | ||
delete self._data[key] | ||
return Q.resolve() | ||
}) | ||
} | ||
@@ -71,3 +82,3 @@ | ||
FakeCache.prototype.set = function (key, value) { | ||
if (this._failureCount > 0) return this._fakeFail() | ||
if (this._failureCount > 0) return this._fakeFail('set') | ||
this._logger.fine('FakeCache - set', key, value) | ||
@@ -87,3 +98,3 @@ | ||
FakeCache.prototype.mset = function (items) { | ||
if (this._failureCount > 0) return this._fakeFail() | ||
if (this._failureCount > 0) return this._fakeFail('mset') | ||
@@ -174,2 +185,9 @@ var self = this | ||
/** | ||
* Set failure count | ||
*/ | ||
FakeCache.prototype.setNextFailure = function (err) { | ||
this._nextFailure = err | ||
} | ||
/** | ||
* Set the latency for all cache operations. | ||
@@ -196,9 +214,17 @@ * | ||
FakeCache.prototype._fakeFail = function () { | ||
FakeCache.prototype._fakeFail = function (op) { | ||
this._failureCount -= 1 | ||
return Q.reject(new Error('Fake Error')) | ||
var failure | ||
if (this._nextFailure) { | ||
if (this._nextFailure instanceof TimeoutError) this._getTimeoutCounter(op).inc() | ||
failure = this._nextFailure | ||
this._nextFailure = null | ||
} else { | ||
failure = new Error('Fake Error') | ||
} | ||
return Q.reject(failure) | ||
} | ||
FakeCache.prototype._getInternal = function (key) { | ||
this._logger.fine('FakeCache - get', key, (typeof this._data[key] !== 'undefined') ? '[HIT]' : '[MISS]') | ||
this._logger.fine('FakeCache - getInternal', key, (typeof this._data[key] !== 'undefined') ? '[HIT]' : '[MISS]') | ||
var val = this._data[key] | ||
@@ -205,0 +231,0 @@ if (typeof val === 'undefined') { |
@@ -107,2 +107,7 @@ // Copyright 2014 The Obvious Corporation. | ||
/** @inheritDoc */ | ||
MultiWriteCacheGroup.prototype.resetTimeoutCount = function (op) { | ||
return this._instance.resetTimeoutCount(op) | ||
} | ||
/** | ||
@@ -109,0 +114,0 @@ * A helper function that does write operations, set, mset and del, to all |
@@ -192,3 +192,3 @@ var redis = require('redis') | ||
isTimeout = true | ||
self.getTimeoutCount(opName).inc() | ||
self._getTimeoutCounter(opName).inc() | ||
}, this._reqTimeoutMs) | ||
@@ -195,0 +195,0 @@ |
{ | ||
"name": "zcache", | ||
"description": "AWS zone-aware multi-layer cache", | ||
"version": "0.3.0-beta", | ||
"version": "0.3.1", | ||
"homepage": "https://github.com/Obvious/zcache", | ||
@@ -22,2 +22,3 @@ "authors": [ | ||
"redis": "0.8.2", | ||
"hiredis": "0.1.16", | ||
"metrics": "0.1.6", | ||
@@ -24,0 +25,0 @@ "hashring": "1.0.3" |
@@ -107,3 +107,3 @@ // Copyright 2014 The Obvious Corporation. | ||
test.ok(err instanceof PartialResultError) | ||
test.equal(1, cluster.getPartialFailureCount('mget').count) | ||
test.equal(1, cluster.getPartialFailureCount('mget')) | ||
var data = err.getData() | ||
@@ -147,3 +147,3 @@ var error = err.getError() | ||
test.ok(err instanceof PartialResultError) | ||
test.equal(1, cluster.getPartialFailureCount('mset').count) | ||
test.equal(1, cluster.getPartialFailureCount('mset')) | ||
var data = err.getData() | ||
@@ -162,31 +162,2 @@ var error = err.getError() | ||
builder.add(function testTimeoutFailure(test) { | ||
var cluster = new zcache.CacheCluster({requestTimeoutMs: 10}) | ||
var fakeCache1 = new zcache.FakeCache(logger) | ||
cluster.addNode('FakeCache1', fakeCache1, 1, 0) | ||
cluster.addNode('FakeCache2', new zcache.FakeCache(logger), 1, 0) | ||
cluster.addNode('FakeCache3', new zcache.FakeCache(logger), 1, 0) | ||
cluster.connect() | ||
var items = [] | ||
for (var i = 0; i < 100; i++) { | ||
items.push({ | ||
key: 'key' + i, | ||
value: 'value' + i | ||
}) | ||
} | ||
fakeCache1.setLatencyMs(20) | ||
return cluster.mset(items) | ||
.then(function () { | ||
test.fail('The mget() call is supposed to fail') | ||
}) | ||
.fail(function (err) { | ||
test.ok(err instanceof TimeoutError) | ||
test.equals('mset Request timeout after 10 ms', err.message) | ||
test.equals(1, cluster.getTimeoutCount('mset').count, 'The timeout count should be 1') | ||
test.equals(0, cluster.getStats('mset').count(), 'There should be no stats data') | ||
}) | ||
}) | ||
builder.add(function testDel(test) { | ||
@@ -295,3 +266,3 @@ var cluster = new zcache.CacheCluster() | ||
test.ok(cluster.getStats('set').mean() > 28) | ||
test.ok(cluster.getStats('set').mean() < 33) | ||
test.ok(cluster.getStats('set').mean() < 35) | ||
@@ -307,3 +278,3 @@ var getPromises = [] | ||
test.ok(cluster.getStats('get').mean() > 28) | ||
test.ok(cluster.getStats('get').mean() < 33) | ||
test.ok(cluster.getStats('get').mean() < 35) | ||
@@ -322,3 +293,3 @@ var items = [] | ||
test.ok(cluster.getStats('mset').mean() > 28) | ||
test.ok(cluster.getStats('mset').mean() < 33) | ||
test.ok(cluster.getStats('mset').mean() < 35) | ||
@@ -334,3 +305,3 @@ var keys = [] | ||
test.ok(cluster.getStats('mget').mean() > 28) | ||
test.ok(cluster.getStats('mget').mean() < 33) | ||
test.ok(cluster.getStats('mget').mean() < 35) | ||
}) | ||
@@ -372,1 +343,38 @@ | ||
}) | ||
builder.add(function testTimeoutFailure(test) { | ||
var cluster = new zcache.CacheCluster() | ||
var fakeCache1 = new zcache.FakeCache(logger) | ||
var fakeCache2 = new zcache.FakeCache(logger) | ||
cluster.addNode('FakeCache1', fakeCache1, 1, 0) | ||
cluster.addNode('FakeCache2', fakeCache2, 1, 0) | ||
cluster.addNode('FakeCache3', new zcache.FakeCache(logger), 1, 0) | ||
cluster.connect() | ||
var items = [] | ||
for (var i = 0; i < 100; i++) { | ||
items.push({ | ||
key: 'key' + i, | ||
value: 'value' + i | ||
}) | ||
} | ||
// Both fakeCache1 and fakeCache2 will timeout in the next requests. | ||
// We should see timeout count to be 2. | ||
fakeCache1.setFailureCount(1) | ||
fakeCache1.setNextFailure(new TimeoutError()) | ||
fakeCache2.setFailureCount(1) | ||
fakeCache2.setNextFailure(new TimeoutError()) | ||
return cluster.mset(items) | ||
.then(function () { | ||
test.fail('The mget() call is supposed to fail') | ||
}) | ||
.fail(function (err) { | ||
test.equal(1, cluster.getPartialFailureCount('mset'), 'One partail failure from mset') | ||
test.equal(2, cluster.getTimeoutCount('mset'), 'Two timeouts from all cache servers') | ||
cluster.resetTimeoutCount('mset') | ||
test.equal(0, cluster.getTimeoutCount('mset'), 'Return 0 after reset') | ||
}) | ||
}) |
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
126648
29
3205
7
1
+ Addedhiredis@0.1.16
+ Addedhiredis@0.1.16(transitive)