Comparing version 0.3.6 to 0.4.0-alpha
@@ -14,3 +14,3 @@ module.exports = { | ||
PartialResultError: require('./lib/PartialResultError'), | ||
MultiWriteCacheGroup: require('./lib/MultiWriteCacheGroup') | ||
CachePair: require('./lib/CachePair') | ||
} |
@@ -143,2 +143,7 @@ // Copyright 2014 The Obvious Corporation. | ||
/** @override */ | ||
CacheCluster.prototype.getUrisByKey = function (key) { | ||
return [this._hashRing.get(key)] | ||
} | ||
/** @override */ | ||
CacheCluster.prototype.get = function (key) { | ||
@@ -276,5 +281,5 @@ var cacheInstance = this._servers[this._hashRing.get(key)] | ||
* | ||
* @param {Promise} origPromise The promise to wrap. | ||
* @param {Q.Promise} origPromise The promise to wrap. | ||
* @param {string} opName The name of the cache operation, e.g., 'get', 'set', etc. | ||
* @return {Promise} A promise that behaves the same as the 'origPromise' with its latency | ||
* @return {Q.Promise} A promise that behaves the same as the 'origPromise' with its latency | ||
* measured. | ||
@@ -343,3 +348,3 @@ */ | ||
* @param {Array.<string>} values The values that have been fetched | ||
* @return {Promise.<Array.<string>>} The passed in "values", for easy chaining. | ||
* @return {Q.Promise.<Array.<string>>} The passed in "values", for easy chaining. | ||
*/ | ||
@@ -356,3 +361,3 @@ function setValues(valueMap, keys, values) { | ||
* | ||
* @param {Object.<string, string>} map The key-value map of fetched cache entries. | ||
* @param {Object} errorMap The key-value map of fetched cache entries. | ||
* @param {Array.<string>} keys The keys that have been fetched | ||
@@ -369,2 +374,2 @@ * @param {Object} err The error object | ||
module.exports = CacheCluster | ||
module.exports = CacheCluster |
@@ -10,10 +10,9 @@ // Copyright 2014 The Obvious Corporation. | ||
var metrics = require('metrics') | ||
var util = require('util') | ||
var Q = require('kew') | ||
/** | ||
* Default timeout for cache request. Notice, not the timeout for connection. | ||
* @const | ||
* @type {number} | ||
*/ | ||
* Default timeout for cache request. Notice, not the timeout for connection. | ||
* @const | ||
* @type {number} | ||
*/ | ||
var DEFAULT_REQ_TIMEOUT_MS = 50 | ||
@@ -24,5 +23,6 @@ | ||
* | ||
* @param {Object=} options Additional options for this connection. | ||
* @param {{requestTimeoutMs: (number|undefined)}=} options Additional options for this connection. | ||
* 'requestTimeoutMs' specifies the timeout of a Redis request. | ||
* @constructor | ||
* @extends {events.EventEmitter} | ||
*/ | ||
@@ -72,3 +72,3 @@ function CacheInstance(options) { | ||
* @param {Array.<string>} keys A list of keys | ||
* @return {Promise.<Array.<string>>} The fetched values. For keys that do not exist, | ||
* @return {Q.Promise.<Array.<string>>} The fetched values. For keys that do not exist, | ||
* returns 'undefined's. | ||
@@ -83,8 +83,9 @@ */ | ||
* | ||
* @param {Array.<{key: string, value: string}} items Key-value pairs to set. | ||
* @param {number=} maxAgeMs The living time of keys, in milliseconds. | ||
* @return {Promise} | ||
* @param {Array.<{key: string, value: string}>} items Key-value pairs to set. | ||
* @param {number} maxAgeMs The living time of keys, in milliseconds. | ||
* @param {boolean=} setWhenNotExist Only set the keys that do not exist yet. | ||
* @return {Q.Promise} | ||
*/ | ||
CacheInstance.prototype.mset = function (items, maxAgeMs) { | ||
throw new Error("mget() must be implemented by any class extending CacheInstance") | ||
CacheInstance.prototype.mset = function (items, maxAgeMs, setWhenNotExist) { | ||
throw new Error("mset() must be implemented by any class extending CacheInstance") | ||
} | ||
@@ -96,3 +97,3 @@ | ||
* @param {string} key The key to get value of. | ||
* @return {Promise.<string>} The fetched value. Returns 'undefined' if the doesn't exist. | ||
* @return {Q.Promise.<string>} The fetched value. Returns 'undefined' if the doesn't exist. | ||
*/ | ||
@@ -106,8 +107,9 @@ CacheInstance.prototype.get = function (key) { | ||
* | ||
* @param {string} key | ||
* @param {string} value | ||
* @param {number=} maxAgeMs The living time of this key, in milliseconds. | ||
* @return {Promise} | ||
* @param {string} key The key to set | ||
* @param {string} val The value to set | ||
* @param {number} maxAgeMs The living time of this key, in milliseconds. | ||
* @param {boolean=} setWhenNotExist Only set the key when it doesn't exist. | ||
* @return {Q.Promise} | ||
*/ | ||
CacheInstance.prototype.set = function (key, val, maxAgeMs) { | ||
CacheInstance.prototype.set = function (key, val, maxAgeMs, setWhenNotExist) { | ||
throw new Error("set() must be implemented by any class extending CacheInstance") | ||
@@ -120,3 +122,3 @@ } | ||
* @param {string} key | ||
* @param {Promise} | ||
* @return {Q.Promise} | ||
*/ | ||
@@ -128,2 +130,12 @@ CacheInstance.prototype.del = function (key) { | ||
/** | ||
* Get the uri of the cache server(s) that is in charge of the given key. | ||
* | ||
* @param {string} key | ||
* @return {Array.<string>} | ||
*/ | ||
CacheInstance.prototype.getUrisByKey = function (key) { | ||
return ['UnknownUri'] | ||
} | ||
/** | ||
* Get the number of cache accesses. | ||
@@ -148,4 +160,2 @@ * | ||
* Reset the access count and hit count to get counts during time intervals. | ||
* | ||
* @return {number} | ||
*/ | ||
@@ -161,3 +171,3 @@ CacheInstance.prototype.resetCount = function () { | ||
* @param {string} key | ||
* @param {Promise.<ServerInfo>} A promise that returns the server information. Returns | ||
* @return {Q.Promise.<ServerInfo>} A promise that returns the server information. Returns | ||
* null if the server info is not available. | ||
@@ -172,7 +182,6 @@ */ | ||
* | ||
* @param {Array.<Object>|Object} data The data fetched by get or mget. | ||
* @return {Function} A function that updates the counts and returns the parameter | ||
* that is passed into it. It is handy to chain to a promise. | ||
*/ | ||
CacheInstance.prototype.updateCount = function() { | ||
CacheInstance.prototype.getCountUpdater = function() { | ||
var self = this | ||
@@ -237,4 +246,5 @@ return function (data) { | ||
/** | ||
* Gets a map of cacheInstance uri and the requestCounts | ||
* @return {Object} | ||
* Gets a map of cacheInstance uri and the requestCounts. | ||
* | ||
* @return {Array.<Object>} | ||
*/ | ||
@@ -241,0 +251,0 @@ CacheInstance.prototype.getPendingRequestsCount = function () { |
@@ -9,5 +9,5 @@ // Copyright 2014 A Medium Corporation. | ||
* Merges pending requests count for a list of cache instances to a list | ||
* @param {Object} requestCounts map of cacheInstance uri and requests count | ||
* @param {Array<Object>} cacheInstances | ||
* @return {Array<Object>} | ||
* | ||
* @param {Array.<Object>} cacheInstances | ||
* @return {Array.<Object>} A list of pending requests count objects | ||
*/ | ||
@@ -14,0 +14,0 @@ function mergePendingRequestCounts(cacheInstances) { |
@@ -7,2 +7,6 @@ var poolModule = require('generic-pool') | ||
/** | ||
* @constructor | ||
* @extends {CacheInstance} | ||
*/ | ||
function ConnectionPool(opts) { | ||
@@ -120,3 +124,3 @@ CacheInstance.call(this) | ||
* @param {Object} context the query context | ||
* @return {Promise.<Array.<string>>} the cached response | ||
* @return {Q.Promise.<Array.<string>>} the cached response | ||
*/ | ||
@@ -142,3 +146,3 @@ function callMget(client, context) { | ||
* @param {Object} context the query context | ||
* @return {Promise.<string>} the cached response | ||
* @return {Q.Promise.<string>} the cached response | ||
*/ | ||
@@ -164,3 +168,3 @@ function callGet(client, context) { | ||
* @param {Object} context the query context | ||
* @return {Promise.<string>} the cached response | ||
* @return {Q.Promise.<string>} the cached response | ||
*/ | ||
@@ -186,3 +190,3 @@ function callSet(client, context) { | ||
* @param {Object} context the query context | ||
* @return {Promise.<string>} the cached response | ||
* @return {Q.Promise.<string>} the cached response | ||
*/ | ||
@@ -194,2 +198,2 @@ function callDel(client, context) { | ||
module.exports = ConnectionPool | ||
module.exports = ConnectionPool |
@@ -5,2 +5,6 @@ var util = require('util') | ||
/** | ||
* @constructor | ||
* @extends {CacheInstance} | ||
*/ | ||
function ConnectionWrapper(cacheInstance, opts) { | ||
@@ -40,3 +44,3 @@ CacheInstance.call(this) | ||
this._state.shouldConnect = true | ||
this._connectDelayed(0) | ||
this._connectDelayed() | ||
} | ||
@@ -85,2 +89,2 @@ | ||
module.exports = ConnectionWrapper | ||
module.exports = ConnectionWrapper |
@@ -14,2 +14,5 @@ // Copyright 2014 The Obvious Corporation. | ||
/** @typedef {{fine: !Function}} */ | ||
var Logger | ||
/** | ||
@@ -19,4 +22,5 @@ * @constructor | ||
* @param {Logger} logger A logger for logging debug information. | ||
* @param {string=} optUri Give an URI to this fake cache instance | ||
*/ | ||
function FakeCache(logger) { | ||
function FakeCache(logger, optUri) { | ||
CacheInstance.call(this) | ||
@@ -29,6 +33,7 @@ | ||
this._nextFailure = null | ||
this._uri = optUri || 'FakeCache' | ||
} | ||
util.inherits(FakeCache, CacheInstance) | ||
/** @inheritDoc */ | ||
/** @override */ | ||
FakeCache.prototype.mget = function (keys) { | ||
@@ -47,3 +52,3 @@ if (this._failureCount > 0) return this._fakeFail('mget') | ||
/** @inheritDoc */ | ||
/** @override */ | ||
FakeCache.prototype.get = function (key) { | ||
@@ -63,3 +68,3 @@ if (this._failureCount > 0) return this._fakeFail('get') | ||
/** @inheritDoc */ | ||
/** @override */ | ||
FakeCache.prototype.del = function (key) { | ||
@@ -75,8 +80,7 @@ if (this._failureCount > 0) return this._fakeFail('del') | ||
delete self._data[key] | ||
return Q.resolve() | ||
}) | ||
} | ||
/** @inheritDoc */ | ||
FakeCache.prototype.set = function (key, value) { | ||
/** @override */ | ||
FakeCache.prototype.set = function (key, value, maxAgeMs, setWhenNotExist) { | ||
if (this._failureCount > 0) return this._fakeFail('set') | ||
@@ -90,9 +94,8 @@ this._logger.fine('FakeCache - set', key, value) | ||
self._requestCounts.set += 1 | ||
self._data[key] = value | ||
return Q.resolve() | ||
if (!setWhenNotExist || !(key in self._data)) self._data[key] = value | ||
}) | ||
} | ||
/** @inheritDoc */ | ||
FakeCache.prototype.mset = function (items) { | ||
/** @override */ | ||
FakeCache.prototype.mset = function (items, maxAgeMs, setWhenNotExist) { | ||
if (this._failureCount > 0) return this._fakeFail('mset') | ||
@@ -105,10 +108,15 @@ | ||
self._requestCounts.mset += 1 | ||
self._requestCounts.msetItemCount.push(items.length) | ||
for (var i = 0; i < items.length; i++) { | ||
var item = items[i] | ||
self._data[item.key] = item.value | ||
if (!setWhenNotExist || !(item.key in self._data)) self._data[item.key] = item.value | ||
} | ||
return Q.resolve() | ||
}) | ||
} | ||
/** @override */ | ||
FakeCache.prototype.getUrisByKey = function (key) { | ||
return [this._uri] | ||
} | ||
/** | ||
@@ -204,2 +212,3 @@ * A sync version of the mget(). | ||
mget: 0, | ||
mset: 0, | ||
get: 0, | ||
@@ -209,2 +218,3 @@ set: 0, | ||
mgetItemCount: [], | ||
msetItemCount: [], | ||
hitCount: 0, | ||
@@ -211,0 +221,0 @@ missCount: 0, |
@@ -73,3 +73,3 @@ // Copyright 2013 The Obvious Corporation. | ||
/** @inheritDoc */ | ||
/** @override */ | ||
InMemoryCache.prototype.isAvailable = function () { | ||
@@ -79,3 +79,3 @@ return this._isAvailable | ||
/** @inheritDoc */ | ||
/** @override */ | ||
InMemoryCache.prototype.connect = function () { | ||
@@ -88,3 +88,3 @@ this._isAvailable = true | ||
/** @inheritDoc */ | ||
/** @override */ | ||
InMemoryCache.prototype.disconnect = function () { | ||
@@ -96,3 +96,3 @@ this._destroyReaper() | ||
/** @inheritDoc */ | ||
/** @override */ | ||
InMemoryCache.prototype.destroy = function () { | ||
@@ -106,3 +106,3 @@ this._destroyReaper() | ||
/** @inheritDoc */ | ||
/** @override */ | ||
InMemoryCache.prototype.get = function (key) { | ||
@@ -114,3 +114,3 @@ return this.mget([key]).then(function (data) { | ||
/** @inheritDoc */ | ||
/** @override */ | ||
InMemoryCache.prototype.mget = function (keys) { | ||
@@ -130,26 +130,47 @@ var ret = [] | ||
/** @inheritDoc */ | ||
InMemoryCache.prototype.set = function (key, val, maxAgeMs) { | ||
if ((maxAgeMs === undefined || maxAgeMs <= 0) && !this._maxAgeOverride) throw new Error('maxAge must either be positive or overriden with a positive overrideMaxAgeMs') | ||
this._expireAt[key] = Date.now() + (this._maxAgeOverride || maxAgeMs ) | ||
return this._data[key] = val | ||
/** @override */ | ||
InMemoryCache.prototype.set = function (key, val, maxAgeMs, setWhenNotExist) { | ||
if ((maxAgeMs === undefined || maxAgeMs <= 0) && !this._maxAgeOverride) { | ||
return Q.reject(new Error('maxAgeMs must either be positive or overriden with a positive overrideMaxAgeMs')) | ||
} | ||
this._setInternal(key, val, maxAgeMs, setWhenNotExist) | ||
return Q.resolve(null) | ||
} | ||
/** @inheritDoc */ | ||
InMemoryCache.prototype.mset = function (items, maxAgeMs) { | ||
/** @override */ | ||
InMemoryCache.prototype.mset = function (items, maxAgeMs, setWhenNotExist) { | ||
if ((maxAgeMs === undefined || maxAgeMs <= 0) && !this._maxAgeOverride) { | ||
return Q.reject(new Error('maxAgeMs must either be positive or overriden with a positive overrideMaxAgeMs')) | ||
} | ||
for (var i = 0; i < items.length; i++) { | ||
this.set(items[i].key, items[i].value, maxAgeMs) | ||
this._setInternal(items[i].key, items[i].value, maxAgeMs, setWhenNotExist) | ||
} | ||
return Q.resolve(null) | ||
} | ||
/** @inheritDoc */ | ||
/** | ||
* A helper function that sets an individual value. | ||
*/ | ||
InMemoryCache.prototype._setInternal = function (key, val, maxAgeMs, setWhenNotExist) { | ||
if (setWhenNotExist && (key in this._data)) { | ||
return | ||
} | ||
this._expireAt[key] = Date.now() + (this._maxAgeOverride || maxAgeMs ) | ||
this._data[key] = val | ||
} | ||
/** @override */ | ||
InMemoryCache.prototype.del = function (key) { | ||
;delete this._data[key] | ||
;delete this._expireAt[key] | ||
return true | ||
return Q.resolve(null) | ||
} | ||
/** @inheritDoc */ | ||
CacheInstance.prototype.getPendingRequestsCount = function () { | ||
/** @override */ | ||
InMemoryCache.prototype.getUrisByKey = function (key) { | ||
return ['InMemoryCache'] | ||
} | ||
/** @override */ | ||
InMemoryCache.prototype.getPendingRequestsCount = function () { | ||
var requestCounts = {} | ||
@@ -156,0 +177,0 @@ requestCounts['count'] = 0 |
@@ -7,2 +7,6 @@ var memc = require('node-memcache-parser-obvfork').client | ||
/** | ||
* @constructor | ||
* @extends {CacheInstance} | ||
*/ | ||
function MemcacheConnection(host, port, encoding) { | ||
@@ -125,2 +129,2 @@ CacheInstance.call(this) | ||
module.exports = MemcacheConnection | ||
module.exports = MemcacheConnection |
@@ -47,2 +47,3 @@ // Copyright 2014 A Medium Corporation | ||
* @constructor | ||
* @extends {CacheInstance} | ||
*/ | ||
@@ -61,3 +62,3 @@ function MultiplexingCache(delegate) { | ||
* number of promises when doing mgets. | ||
* @type {Object.<Promise.<Object.<*>>} | ||
* @type {Object.<Q.Promise.<Object.<*>>>} | ||
*/ | ||
@@ -119,8 +120,2 @@ this._pendingGets = {} | ||
/** @override */ | ||
MultiplexingCache.prototype.updateCount = function() { | ||
return this._delegate.updateCount() | ||
} | ||
/** @override */ | ||
MultiplexingCache.prototype.getStats = function (op) { | ||
@@ -150,11 +145,18 @@ return this._delegate.getStats(op) | ||
/** @override */ | ||
MultiplexingCache.prototype.mset = function (items, maxAgeMs) { | ||
MultiplexingCache.prototype.getUrisByKey = function (key) { | ||
return this._delegate.getUrisByKey(key) | ||
} | ||
/** @override */ | ||
MultiplexingCache.prototype.mset = function (items, maxAgeMs, setWhenNotExist) { | ||
this._invalidateKeys(items) | ||
return this._delegate.mset(items, maxAgeMs) | ||
return this._delegate.mset(items, maxAgeMs, setWhenNotExist) | ||
} | ||
MultiplexingCache.prototype.set = function (key, val, maxAgeMs) { | ||
/** @override */ | ||
MultiplexingCache.prototype.set = function (key, val, maxAgeMs, setWhenNotExist) { | ||
this._invalidateKeys([key]) | ||
return this._delegate.set(key, val, maxAgeMs) | ||
return this._delegate.set(key, val, maxAgeMs, setWhenNotExist) | ||
} | ||
@@ -169,3 +171,2 @@ | ||
/** @override */ | ||
@@ -228,3 +229,2 @@ MultiplexingCache.prototype.get = function (key) { | ||
/** | ||
@@ -246,3 +246,3 @@ * Deletes the promise for a set of keys or objects. | ||
* @param {Array.<string>} keys | ||
* @return {Promise.<Object>} | ||
* @return {Q.Promise.<Object>} | ||
* @private | ||
@@ -269,2 +269,3 @@ */ | ||
* @private | ||
* @template T | ||
*/ | ||
@@ -280,3 +281,5 @@ MultiplexingCache.prototype._convertToMap = function (keys) { | ||
} | ||
/** @inheritDoc */ | ||
/** @override */ | ||
MultiplexingCache.prototype.getPendingRequestsCount = function () { | ||
@@ -283,0 +286,0 @@ return this._delegate.getPendingRequestsCount() |
@@ -15,2 +15,3 @@ // Copyright 2014 The Obvious Corporation. | ||
* @constructor | ||
* @extends {Error} | ||
*/ | ||
@@ -35,3 +36,3 @@ function PartialResultError(data, err) { | ||
/** | ||
* @param {Object.<string, *>} err The keys that failed to process. | ||
* @return {Object.<string, *>} The keys that failed to process. | ||
* The values in this object are the errors. | ||
@@ -38,0 +39,0 @@ */ |
@@ -14,4 +14,4 @@ var redis = require('redis') | ||
* @param {string} host The host that runs the redis-server | ||
* @param {string} port The port that the redis-server listens to | ||
* @param {Object=} options Additional options for this connection. | ||
* @param {number} port The port that the redis-server listens to | ||
* @param {{requestTimeoutMs: (number|undefined)}=} options Additional options for this connection. | ||
* 'requestTimeoutMs' specifies the timeout of a Redis request. | ||
@@ -27,2 +27,3 @@ * @extends CacheInstance | ||
this._port = port || null | ||
this._uri = this._host + ':' + this._port | ||
this._bound_onConnect = this._onConnect.bind(this) | ||
@@ -34,3 +35,3 @@ this._bound_onError = this._onError.bind(this) | ||
/** @inheritDoc */ | ||
/** @override */ | ||
RedisConnection.prototype.isAvailable = function () { | ||
@@ -40,23 +41,34 @@ return this._isAvailable | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.set = function (key, val, maxAgeMs) { | ||
/** @override */ | ||
RedisConnection.prototype.set = function (key, val, maxAgeMs, setWhenNotExist) { | ||
var deferred = Q.defer() | ||
this._client.setex(key, Math.floor(maxAgeMs / 1000), val, | ||
this._makeNodeResolverWithTimeout(deferred, 'set', 'Redis [set] key: ' + key)) | ||
var params = [key, val, 'PX', maxAgeMs] | ||
if (setWhenNotExist) params.push('NX') | ||
this._client.set(params, this._makeNodeResolverWithTimeout(deferred, 'set', 'Redis [set] key: ' + key)) | ||
return deferred.promise | ||
} | ||
/** @inheritDoc */ | ||
RedisConnection.prototype.mset = function (items, maxAgeMs) { | ||
if (!items || !items.length) return Q.resolve() | ||
/** @override */ | ||
RedisConnection.prototype.mset = function (items, maxAgeMs, setWhenNotExist) { | ||
if (!items || !items.length) return Q.resolve(undefined) | ||
var deferred = Q.defer() | ||
var msetCommand = ["MSET"] | ||
var commands = [msetCommand] | ||
for (var i = 0, l = items.length; i < l; i++) { | ||
var key = items[i].key | ||
// Append key value arguments to the set command. | ||
msetCommand.push(key, items[i].value) | ||
// Append an expire command. | ||
commands.push(["EXPIRE", key, Math.floor(maxAgeMs / 1000)]) | ||
var commands = [] | ||
var i, l | ||
if (setWhenNotExist) { | ||
// Use "SET" to set each key with a "NX" flag. | ||
for (i = 0, l = items.length; i < l; i++) { | ||
commands.push(['set', items[i].key, items[i].value, 'PX', maxAgeMs, 'NX']) | ||
} | ||
} else { | ||
// Use "MSET" to set all the keys and "EXPIRE" to set TTL for each key | ||
var msetCommand = ['MSET'] | ||
commands.push(msetCommand) | ||
for (i = 0, l = items.length; i < l; i++) { | ||
var key = items[i].key | ||
// Append key value arguments to the set command. | ||
msetCommand.push(key, items[i].value) | ||
// Append an expire command. | ||
commands.push(['EXPIRE', key, Math.floor(maxAgeMs / 1000)]) | ||
} | ||
} | ||
@@ -70,3 +82,3 @@ this._client.multi(commands).exec( | ||
/** @inheritDoc */ | ||
/** @override */ | ||
RedisConnection.prototype.del = function (key) { | ||
@@ -79,3 +91,3 @@ var deferred = Q.defer() | ||
/** @inheritDoc */ | ||
/** @override */ | ||
RedisConnection.prototype.get = function (key) { | ||
@@ -86,3 +98,3 @@ return this.mget([key]) | ||
/** @inheritDoc */ | ||
/** @override */ | ||
RedisConnection.prototype.mget = function (keys) { | ||
@@ -107,6 +119,6 @@ if (!keys || !keys.length) return Q.resolve([]) | ||
}) | ||
.then(this.updateCount()) | ||
.then(this.getCountUpdater()) | ||
} | ||
/** @inheritDoc */ | ||
/** @override */ | ||
RedisConnection.prototype.getServerInfo = function () { | ||
@@ -122,3 +134,3 @@ var deferred = Q.defer() | ||
.map(function(item) {items[item[0]] = item[1]}) | ||
var serverInfo = new ServerInfo | ||
var serverInfo = new ServerInfo() | ||
try { | ||
@@ -132,3 +144,3 @@ serverInfo.memoryBytes = parseInt(items['used_memory'], 10) | ||
} catch (e) { | ||
Q.reject('Malformatted output from the "INFO" command of Redis') | ||
Q.reject(new Error('Malformatted output from the "INFO" command of Redis')) | ||
} | ||
@@ -139,3 +151,3 @@ return Q.resolve(serverInfo) | ||
/** @inheritDoc */ | ||
/** @override */ | ||
RedisConnection.prototype.disconnect = function () { | ||
@@ -147,3 +159,3 @@ this._isAvailable = false | ||
/** @inheritDoc */ | ||
/** @override */ | ||
RedisConnection.prototype.destroy = function () { | ||
@@ -155,3 +167,3 @@ this.disconnect() | ||
/** @inheritDoc */ | ||
/** @override */ | ||
RedisConnection.prototype.connect = function () { | ||
@@ -172,17 +184,22 @@ if (this._isAvailable) return | ||
/** @override */ | ||
RedisConnection.prototype.getUrisByKey = function (key) { | ||
return [this._uri] | ||
} | ||
/** | ||
* Gets the uri of the server | ||
* @return {string} | ||
* Return the URI of this Redis server. | ||
* | ||
* @return {string} The URI of this Redis server. | ||
*/ | ||
RedisConnection.prototype.getUri = function () { | ||
return this._host + ':' + this._port | ||
return this._uri | ||
} | ||
/** @inheritDoc */ | ||
/** @override */ | ||
RedisConnection.prototype.getPendingRequestsCount = function () { | ||
var requestCounts = { | ||
'uri': this.getUri(), | ||
return [{ | ||
'uri': this._uri, | ||
'count': this._client.command_queue.length | ||
} | ||
return [requestCounts] | ||
}] | ||
} | ||
@@ -208,3 +225,3 @@ | ||
* | ||
* @param {Promise} deferred A deferred promise. | ||
* @param {Q.Promise} deferred A deferred promise. | ||
* @param {string} opName The name of the operation. It should be one of these: 'get', | ||
@@ -211,0 +228,0 @@ * 'mget', 'set', 'mset' and 'del'. |
@@ -7,2 +7,6 @@ var util = require('util') | ||
/** | ||
* @constructor | ||
* @extends {CacheInstance} | ||
*/ | ||
function RedundantCacheGroup() { | ||
@@ -55,3 +59,3 @@ CacheInstance.call(this) | ||
RedundantCacheGroup.prototype.isAvailable = function () { | ||
return !!this._getAvailableInstance() | ||
return !!this._getAvailableInstance(0) | ||
} | ||
@@ -82,2 +86,6 @@ | ||
/** | ||
* @param {number=} start | ||
* @override | ||
*/ | ||
RedundantCacheGroup.prototype.mget = function (keys, start) { | ||
@@ -114,2 +122,6 @@ var instanceIndex = this._getIndexOfFirstAvailableInstance(start || 0) | ||
/** | ||
* @param {number=} start | ||
* @override | ||
*/ | ||
RedundantCacheGroup.prototype.get = function (key, start) { | ||
@@ -128,3 +140,3 @@ var instanceIndex = this._getIndexOfFirstAvailableInstance(start || 0) | ||
RedundantCacheGroup.prototype.mset = function (items, maxAgeMs) { | ||
RedundantCacheGroup.prototype.mset = function (items, maxAgeMs, setWhenNotExist) { | ||
var instances = this._getAllInstances() | ||
@@ -134,3 +146,3 @@ var promises = [] | ||
for (var i = 0; i < instances.length; i++) { | ||
promises.push(instances[i].mset(items, maxAgeMs)) | ||
promises.push(instances[i].mset(items, maxAgeMs, setWhenNotExist)) | ||
} | ||
@@ -142,3 +154,4 @@ | ||
RedundantCacheGroup.prototype.set = function (key, val, maxAgeMs) { | ||
/** @override */ | ||
RedundantCacheGroup.prototype.set = function (key, val, maxAgeMs, setWhenNotExist) { | ||
var instances = this._getAllInstances() | ||
@@ -148,3 +161,3 @@ var promises = [] | ||
for (var i = 0; i < instances.length; i++) { | ||
promises.push(instances[i].set(key, val, maxAgeMs)) | ||
promises.push(instances[i].set(key, val, maxAgeMs, setWhenNotExist)) | ||
} | ||
@@ -169,4 +182,17 @@ | ||
/** @override */ | ||
RedundantCacheGroup.prototype.getUrisByKey = function (key) { | ||
var uris = [] | ||
this._getAllInstances().forEach(function (instance) { | ||
instance.getUrisByKey(key).forEach(function (uri) { | ||
if (uris.indexOf(uri) < 0) uris.push(uri) | ||
}) | ||
}) | ||
return uris | ||
} | ||
/** @override */ | ||
RedundantCacheGroup.prototype.getPendingRequestsCount = function () { | ||
return cacheUtils.mergePendingRequestCounts(cacheInstnaces.map(function (obj) { return obj.instance })) | ||
return cacheUtils.mergePendingRequestCounts(this._getAllInstances()) | ||
} | ||
@@ -173,0 +199,0 @@ |
@@ -7,2 +7,3 @@ // Copyright 2013 The Obvious Corporation. | ||
* @constructor | ||
* @extends {Error} | ||
*/ | ||
@@ -9,0 +10,0 @@ function TimeoutError(msg) { |
{ | ||
"name": "zcache", | ||
"description": "AWS zone-aware multi-layer cache", | ||
"version": "0.3.6", | ||
"homepage": "https://github.com/Obvious/zcache", | ||
"version": "0.4.0-alpha", | ||
"homepage": "https://github.com/Medium/zcache", | ||
"authors": [ | ||
@@ -12,6 +12,6 @@ "Jeremy Stanley <github@azulus.com> (https://github.com/azulus)", | ||
"keywords": ["zcache", "cache", "redis"], | ||
"main": "lib/zcache.js", | ||
"main": "index.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/Obvious/zcache.git" | ||
"url": "https://github.com/Medium/zcache.git" | ||
}, | ||
@@ -21,3 +21,3 @@ "dependencies": { | ||
"generic-pool": "2.0.3", | ||
"kew": "0.3.3", | ||
"kew": "0.4.0", | ||
"redis": "0.8.2", | ||
@@ -31,7 +31,16 @@ "hiredis": "0.1.16", | ||
"nodeunitq": "0.0.3", | ||
"logg": "0.2.2" | ||
"logg": "0.2.2", | ||
"closure-npc": "0.1.3" | ||
}, | ||
"externDependencies": { | ||
"redis": "./externs/redis.js", | ||
"xxhash": "./externs/xxhash.js", | ||
"hashring": "./externs/hashring.js", | ||
"node-memcache-parser-obvfork": "./externs/node-memcache-parser-obvfork.js", | ||
"generic-pool": "./externs/generic-pool.js", | ||
"metrics": "./externs/metrics.js" | ||
}, | ||
"scripts": { | ||
"test": "./node_modules/nodeunit/bin/nodeunit test" | ||
"test": "./node_modules/.bin/closure-npc --jscomp_error=checkTypes && ./node_modules/nodeunit/bin/nodeunit test" | ||
} | ||
} |
@@ -233,2 +233,14 @@ // Copyright 2014 The Obvious Corporation. | ||
builder.add(function testGetUri(test) { | ||
var cluster = new zcache.CacheCluster() | ||
cluster.addNode('FakeCache1', new zcache.FakeCache(logger), 1, 0) | ||
cluster.addNode('FakeCache2', new zcache.FakeCache(logger), 1, 0) | ||
cluster.addNode('FakeCache3', new zcache.FakeCache(logger), 1, 0) | ||
cluster.connect() | ||
test.equal('FakeCache2', cluster.getUrisByKey('foo'), 'Key "foo" should be on cache 2') | ||
test.equal('FakeCache1', cluster.getUrisByKey('bar'), 'Key "foo" should be on cache 1') | ||
test.done() | ||
}) | ||
builder.add(function testCornerCaseWithOnlyOneNode(test) { | ||
@@ -235,0 +247,0 @@ var cluster = new zcache.CacheCluster() |
var zcache = require('../index') | ||
var Q = require('kew') | ||
// TODO: these test cases should be using nodeunitq | ||
exports.setUp = function (callback) { | ||
@@ -23,18 +25,39 @@ this.cI = new zcache.InMemoryCache() | ||
exports.testCacheSet = function (test) { | ||
var self = this | ||
test.equal(0, this.cI.getKeyCount(), 'There is no key in cache') | ||
this.cI.set('foo', 'bar', 10000) | ||
test.equal(this.cI._data['foo'], 'bar', 'bar should be returned') | ||
test.equal(1, this.cI.getKeyCount(), 'There is 1 key in cache') | ||
test.done() | ||
.then(function() { | ||
test.equal(self.cI._data['foo'], 'bar', 'bar should be returned') | ||
test.equal(1, self.cI.getKeyCount(), 'There is 1 key in cache') | ||
test.done() | ||
}) | ||
} | ||
exports.testCacheSetImproperMaxAge = function (test) { | ||
var client = this | ||
test.throws(function () { | ||
client.cI.set('foo', 'bar') | ||
}) | ||
exports.testCacheMset = function (test) { | ||
var self = this | ||
var items = [ | ||
{key: 'key1', value: 'value1'}, | ||
{key: 'key2', value: 'value2'} | ||
] | ||
test.done() | ||
test.equal(0, this.cI.getKeyCount(), 'There is no key in cache') | ||
this.cI.mset(items, 10000) | ||
.then(function() { | ||
test.equal('value1', self.cI._data['key1'], '"key1" should be set') | ||
test.equal('value2', self.cI._data['key2'], '"key2" should be set') | ||
test.equal(2, self.cI.getKeyCount(), 'There should be two keys in cache') | ||
test.done() | ||
}) | ||
} | ||
exports.testCacheSetImproperMaxAge = function (test) { | ||
this.cI.set('foo', 'bar') | ||
.then(function () { | ||
test.fail('Invalide max age parameter should fail the test') | ||
}) | ||
.fail(function () { | ||
test.done() | ||
}) | ||
} | ||
exports.testCacheOverrideMaxAgeMs = function (test) { | ||
@@ -114,23 +137,13 @@ this.cI.overrideMaxAgeMs(1) // super short | ||
exports.testCacheDel = function (test) { | ||
this.cI.set('foo', 1, 1000) | ||
this.cI.del('foo') | ||
test.deepEqual(this.cI._data['foo'], undefined, 'foo should have been deleted') | ||
test.done() | ||
var self = this | ||
self.cI.set('foo', 1, 1000) | ||
.then(function () { | ||
return self.cI.del('foo') | ||
}) | ||
.then(function () { | ||
test.deepEqual(self.cI._data['foo'], undefined, 'foo should have been deleted') | ||
test.done() | ||
}) | ||
} | ||
exports.testCacheMset = function (test) { | ||
var sampleKeys = [ | ||
{key: 'a', value: 1}, | ||
{key: 'b', value: 2}, | ||
{key: 'c', value: 3} | ||
] | ||
this.cI.mset(sampleKeys, 1000) | ||
test.equal(this.cI._data['a'], 1, 'a should be 1') | ||
test.equal(this.cI._data['b'], 2, 'b should be 2') | ||
test.equal(this.cI._data['c'], 3, 'c should be 3') | ||
test.equal(3, this.cI.getKeyCount(), 'There are 3 keys in cache') | ||
test.done() | ||
} | ||
exports.testCacheMget = function (test) { | ||
@@ -192,1 +205,37 @@ this.cI.mset([{key: 'a', value: 1}, {key: 'b', value: 2}, {key: 'c', value: 3}], 1000) | ||
} | ||
exports.testSetNotExist = function (test) { | ||
var cacheInstance = this.cI | ||
cacheInstance.set('abc', '123', 30000) | ||
cacheInstance.set('abc', '456', 30000, true) | ||
cacheInstance.get('abc') | ||
.then(function (val) { | ||
test.equals('123', val, 'The value should still be "123"') | ||
test.done() | ||
}) | ||
} | ||
exports.testMsetNotExist = function (test) { | ||
var cacheInstance = this.cI | ||
var items = [ | ||
{key: 'key1', value: 'value1'}, | ||
{key: 'key3', value: 'value3'} | ||
] | ||
cacheInstance.mset(items, 30000) | ||
items = [ | ||
{key: 'key1', value: 'value1_new'}, | ||
{key: 'key2', value: 'value2'}, | ||
{key: 'key3', value: 'value3_new'}, | ||
{key: 'key4', value: 'value4'} | ||
] | ||
cacheInstance.mset(items, 30000, true) | ||
cacheInstance.mget(['key1', 'key2', 'key3', 'key4']) | ||
.then(function (values) { | ||
test.deepEqual(['value1', 'value2', 'value3', 'value4'], values, 'key2 and key4 should be set') | ||
test.done() | ||
}) | ||
} |
@@ -106,1 +106,215 @@ var zcache = require('../index') | ||
} | ||
exports.testSetNotExist = function (test) { | ||
var cacheInstance = new zcache.RedisConnection('localhost', 6379) | ||
cacheInstance.on('connect', function () { | ||
cacheInstance.removeAllListeners('connect') | ||
test.equal(cacheInstance.isAvailable(), true, 'Connection should be available') | ||
cacheInstance.set('abc', '123', 300000) | ||
.then(function () { | ||
return cacheInstance.set('abc', '456', 300000, true) | ||
}) | ||
.then(function (val) { | ||
return cacheInstance.get('abc') | ||
}) | ||
.then(function (val) { | ||
test.equal(val, '123') | ||
cacheInstance.destroy() | ||
}) | ||
.fail(function (e) { | ||
console.error(e) | ||
test.fail(e.message) | ||
test.done() | ||
}) | ||
}) | ||
cacheInstance.on('destroy', function () { | ||
test.done() | ||
}) | ||
cacheInstance.connect() | ||
} | ||
exports.testMsetNotExist = function (test) { | ||
var cacheInstance = new zcache.RedisConnection('localhost', 6379) | ||
cacheInstance.on('connect', function () { | ||
cacheInstance.removeAllListeners('connect') | ||
test.equal(cacheInstance.isAvailable(), true, 'Connection should be available') | ||
var items = [ | ||
{key: 'key1', value: 'value1'}, | ||
{key: 'key3', value: 'value3'} | ||
] | ||
Q.all([cacheInstance.del('key1'), cacheInstance.del('key2'), cacheInstance.del('key3'), cacheInstance.del('key4')]) | ||
.then(function () { | ||
cacheInstance.mset(items, 300000) | ||
}) | ||
.then(function () { | ||
var items = [ | ||
{key: 'key1', value: 'value1_new'}, | ||
{key: 'key2', value: 'value2'}, | ||
{key: 'key3', value: 'value3_new'}, | ||
{key: 'key4', value: 'value4'} | ||
] | ||
return cacheInstance.mset(items, 300000, true) | ||
}) | ||
.then(function (val) { | ||
return cacheInstance.mget(['key1', 'key2', 'key3', 'key4']) | ||
}) | ||
.then(function (vals) { | ||
test.deepEqual(['value1', 'value2', 'value3', 'value4'], vals) | ||
cacheInstance.destroy() | ||
}) | ||
.fail(function (e) { | ||
console.error(e) | ||
test.fail(e.message) | ||
test.done() | ||
}) | ||
}) | ||
cacheInstance.on('destroy', function () { | ||
test.done() | ||
}) | ||
cacheInstance.connect() | ||
} | ||
// Test .set() with TTL | ||
// (1) set a key with 1 sec TTL | ||
// (2) wait for 1.05 sec | ||
// (3) get the key, and it should return 'undefined'. | ||
exports.testSetTimeout = function (test) { | ||
var cacheInstance = new zcache.RedisConnection('localhost', 6379) | ||
cacheInstance.on('connect', function () { | ||
cacheInstance.removeAllListeners('connect') | ||
test.equal(cacheInstance.isAvailable(), true, 'Connection should be available') | ||
cacheInstance.set('abc', '123', 1000) | ||
.then(function () { | ||
return Q.delay(1050) | ||
}) | ||
.then(function (val) { | ||
return cacheInstance.get('abc') | ||
}) | ||
.then(function (val) { | ||
test.deepEqual(undefined, val, 'The "abc" key should have been expired after 1.05 sec') | ||
cacheInstance.destroy() | ||
}) | ||
.fail(function (e) { | ||
console.error(e) | ||
test.fail(e.message) | ||
test.done() | ||
}) | ||
}) | ||
cacheInstance.on('destroy', function () { | ||
test.done() | ||
}) | ||
cacheInstance.connect() | ||
} | ||
// Test .mset() with 'setWhenNotExist' set and TTL | ||
// (1) set two keys with a long TTL | ||
// (2) set two existing keys plus two more new keys with 'setWhenNotExist' set and with 1 sec TTL | ||
// (3) wait for 1.05 sec. | ||
// (4) the two new keys should have expired and the two old keys should still exist and have the old value | ||
exports.testMsetNotExistTimeout = function (test) { | ||
var cacheInstance = new zcache.RedisConnection('localhost', 6379) | ||
cacheInstance.on('connect', function () { | ||
cacheInstance.removeAllListeners('connect') | ||
test.equal(cacheInstance.isAvailable(), true, 'Connection should be available') | ||
var items = [ | ||
{key: 'key1', value: 'value1'}, | ||
{key: 'key3', value: 'value3'} | ||
] | ||
Q.all([cacheInstance.del('key1'), cacheInstance.del('key2'), cacheInstance.del('key3'), cacheInstance.del('key4')]) | ||
.then(function () { | ||
cacheInstance.mset(items, 300000) | ||
}) | ||
.then(function () { | ||
var items = [ | ||
{key: 'key1', value: 'value1_new'}, | ||
{key: 'key2', value: 'value2'}, | ||
{key: 'key3', value: 'value3_new'}, | ||
{key: 'key4', value: 'value4'} | ||
] | ||
return cacheInstance.mset(items, 1000, true) | ||
}) | ||
.then(function (val) { | ||
return Q.delay(1050) | ||
}) | ||
.then(function (val) { | ||
return cacheInstance.mget(['key1', 'key2', 'key3', 'key4']) | ||
}) | ||
.then(function (vals) { | ||
test.deepEqual(['value1', undefined, 'value3', undefined], vals, '"key2" and "key4" should have been expired at this moment') | ||
cacheInstance.destroy() | ||
}) | ||
.fail(function (e) { | ||
console.error(e) | ||
test.fail(e.message) | ||
test.done() | ||
}) | ||
}) | ||
cacheInstance.on('destroy', function () { | ||
test.done() | ||
}) | ||
cacheInstance.connect() | ||
} | ||
exports.testCounts = function (test) { | ||
var cacheInstance = new zcache.RedisConnection('localhost', 6379) | ||
cacheInstance.on('connect', function () { | ||
cacheInstance.removeAllListeners('connect') | ||
test.equal(cacheInstance.isAvailable(), true, 'Connection should be available') | ||
var items = [ | ||
{key: 'key1', value: 'value1'}, | ||
{key: 'key3', value: 'value3'} | ||
] | ||
Q.all([cacheInstance.del('key1'), cacheInstance.del('key2'), cacheInstance.del('key3'), cacheInstance.del('key4')]) | ||
.then(function () { | ||
cacheInstance.mset(items, 300000) | ||
}) | ||
.then(function () { | ||
return cacheInstance.mget(['key1', 'key2', 'key3', 'key4']) | ||
}) | ||
.then(function (vals) { | ||
test.deepEqual(['value1', undefined, 'value3', undefined], vals, '"key2" and "key4" should have been expired at this moment') | ||
test.equals(4, cacheInstance.getAccessCount()) | ||
test.equals(2, cacheInstance.getHitCount()) | ||
cacheInstance.destroy() | ||
}) | ||
.fail(function (e) { | ||
console.error(e) | ||
test.fail(e.message) | ||
test.done() | ||
}) | ||
}) | ||
cacheInstance.on('destroy', function () { | ||
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
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
165250
38
4127
0
4