Skip to content

Commit

Permalink
add namespace support
Browse files Browse the repository at this point in the history
  • Loading branch information
Shouvik Roy committed May 21, 2020
1 parent be0f326 commit 04bc9f2
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 5 deletions.
97 changes: 92 additions & 5 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ function validateKey(key, operation) {
misc.assert(key.length < 250, 'Key must be less than 250 characters long');
}

function validateNamespaceKey(key, operation) {
validateKey(key, operation);
misc.assert(key.length < 230, 'Key must be less than 230 characters long when using namespaces');
}

function keyWithPrefix(key, prefix) {
if (_.isUndefined(prefix)) {
// only prefix was supplied
// return a curried funtion that expects the key
return function(key) {
return _.toString(prefix) + '_' + key;
};
}
return _.toString(prefix) + '_' + key;
}

/**
* Constructor - Initiate client
*/
Expand Down Expand Up @@ -324,14 +340,26 @@ Client.prototype.deleteMulti = function(keys, cb) {
* @returns {Promise}
*/
Client.prototype.set = function(key, val, ttl, cb) {
validateKey(key, 'set');

var command = 'set';
var self = this;
if (typeof ttl === 'function') {
cb = ttl;
ttl = 0;
}

if (_.isPlainObject(ttl) && _.isString(ttl.namespace)) {
var namespace = ttl.namespace;
validateNamespaceKey(key, command);

return this.getNamespacePrefix(namespace).then(function (prefix) {
key = keyWithPrefix(key, prefix);
return self.run(command, [key, val, ttl], cb);
});
}

return this.run('set', [key, val, ttl], cb);
validateKey(key, command);

return this.run(command, [key, val, ttl], cb);
};

/**
Expand Down Expand Up @@ -384,16 +412,34 @@ Client.prototype.gets = function(key, opts, cb) {
* @returns {Promise}
*/
Client.prototype.get = function(key, opts, cb) {
var command = 'get';
var self = this;
if (typeof opts === 'function' && typeof cb === 'undefined') {
cb = opts;
opts = {};
}

if (_.isPlainObject(opts) && _.isString(opts.namespace)) {
var namespace = opts.namespace;
validateNamespaceKey(key, command);

return this.getNamespacePrefix(namespace).then(function (prefix) {

if(_.isArray(key)) {
key = _.map(key, keyWithPrefix(prefix));
return this.getMulti(key, opts, cb);
} else {
key = keyWithPrefix(key, prefix);
return self.run(command, [key, opts], cb);
}
});
}

if (_.isArray(key)) {
return this.getMulti(key, opts, cb);
} else {
validateKey(key, 'get');
return this.run('get', [key, opts], cb);
validateKey(key, command);
return this.run(command, [key, opts], cb);
}
};

Expand Down Expand Up @@ -584,6 +630,47 @@ Client.prototype.cachedump = function(slabsId, limit, cb) {
return this.run('stats cachedump', [slabsId, limit], cb);
};

/**
* getNamespacePrefix() - Get prefix value for the provided namespace
*
* @param {String} namespace - The namespace for which prefix is to be fetched
* @param {Function} [cb] - The (optional) callback called on completion
* @returns {Promise}
*/
Client.prototype.getNamespacePrefix = function(namespace, cb) {
validateKey(namespace);
var self = this;
var timestamp = _.toNumber(new Date());
var prefix = this.get(namespace);

return prefix.then(function (value) {
if (!value) {
// the namespace is not set
return self.add(namespace, timestamp);
}
return value;
}).then(function (value) {
// if value is undefined it means we just
// added the namespace to cache.
return value || timestamp;
}).nodeify(cb);
};


/**
* invalidateNamespace() - Invalidate all data for a namespace.
* Warning! This does not flush the cache, but instead
* relies on the unused values to expire on their own
*
* @param {String} namespace - The namespace to invalidate
* @param {Function} [cb] - The (optional) callback called on completion
* @returns {Promise}
*/
Client.prototype.invalidateNamespace = function(namespace, cb) {
validateKey(namespace);
return this.incr(namespace, cb);
};

/**
* version() - Get current Memcached version from the server
* @param {Function} [cb] - The (optional) callback called on completion
Expand Down
71 changes: 71 additions & 0 deletions test/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,20 @@ describe('Client', function() {
});
});

describe('with namespace', function () {
it('should work', function () {
var key = getKey(), ns = getKey(), val = chance.word();

return cache.set(key, val, { namespace: ns })
.then(function () {
return cache.get(key, { namespace: ns });
})
.then(function (v) {
val.should.equal(v);
});
});
});

it('does not throw an error when setting a value number', function() {
var key = chance.guid(), val = chance.natural();

Expand Down Expand Up @@ -1277,6 +1291,63 @@ describe('Client', function() {
});
});


describe('namespace', function () {
var cache;
var savedPrefix;
var namespace = getKey();
beforeEach(function () {
cache = new Client();
});


describe('getNamespacePrefix', function () {
it('exists', function () {
return cache.should.have.property('getNamespacePrefix');
});

it('when namespace is not already set', function () {
return cache.getNamespacePrefix(namespace).then(function(prefix) {
expect(prefix).to.be.a('number');
savedPrefix = prefix;
});
});

it('when namespace is already set', function () {
return cache.getNamespacePrefix(namespace).then(function(prefix) {
expect(prefix).to.equal(savedPrefix);
});
});
});

describe('invalidateNamespace', function () {
it('exists', function () {
return cache.should.have.property('invalidateNamespace');
});

it('should invalidate previously stored keys', function () {
var key = getKey(), ns = getKey(), val = chance.word();

return cache.set(key, val, { namespace: ns })
.then(function () {
return cache.get(key, { namespace: ns });
})
.then(function (v) {
val.should.equal(v);
})
.then(function() {
return cache.invalidateNamespace(ns);
})
.then(function () {
return cache.get(key, { namespace: ns });
})
.then(function (v) {
expect(v).to.be.null;
});
});
});
});

describe('version', function () {
var cache;
beforeEach(function() {
Expand Down

0 comments on commit 04bc9f2

Please sign in to comment.