Add TTL for KeyValue related features
This commit is contained in:
parent
d7cf478b52
commit
1c20cc83aa
|
@ -64,19 +64,23 @@ KeyValueMemoryConnector.prototype._getStoreForModel = function(modelName) {
|
|||
return this._store[modelName];
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.get =
|
||||
function(modelName, key, options, callback) {
|
||||
KeyValueMemoryConnector.prototype._removeIfExpired = function(modelName, key) {
|
||||
var store = this._getStoreForModel(modelName);
|
||||
var item = store[key];
|
||||
|
||||
if (item && item.isExpired()) {
|
||||
debug('Removing expired key', key);
|
||||
delete store[key];
|
||||
item = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.get =
|
||||
function(modelName, key, options, callback) {
|
||||
this._removeIfExpired(modelName, key);
|
||||
|
||||
var store = this._getStoreForModel(modelName);
|
||||
var item = store[key];
|
||||
var value = item ? item.value : null;
|
||||
|
||||
debug('GET %j %j -> %s', modelName, key, value);
|
||||
|
||||
if (/^buffer:/.test(value)) {
|
||||
|
@ -127,6 +131,29 @@ function(modelName, key, ttl, options, callback) {
|
|||
process.nextTick(callback);
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.ttl =
|
||||
function(modelName, key, options, callback) {
|
||||
this._removeIfExpired(modelName, key);
|
||||
|
||||
var store = this._getStoreForModel(modelName);
|
||||
|
||||
// key is unknown
|
||||
if (!(key in store)) {
|
||||
return process.nextTick(function() {
|
||||
var err = new Error('Cannot get TTL for unknown key ' + key);
|
||||
err.statusCode = 404;
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
var ttl = store[key].getTtl();
|
||||
debug('TTL %j %j -> %s', modelName, key, ttl);
|
||||
|
||||
process.nextTick(function() {
|
||||
callback(null, ttl);
|
||||
});
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.disconnect = function(callback) {
|
||||
if (this._cleanupTimer)
|
||||
clearInterval(this._cleanupTimer);
|
||||
|
@ -150,3 +177,7 @@ StoreItem.prototype.setTtl = function(ttl) {
|
|||
this.expires = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
StoreItem.prototype.getTtl = function() {
|
||||
return !this.expires ? undefined : this.expires - Date.now();
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ module.exports = KeyValueAccessObject;
|
|||
KeyValueAccessObject.get = require('./get');
|
||||
KeyValueAccessObject.set = require('./set');
|
||||
KeyValueAccessObject.expire = require('./expire');
|
||||
KeyValueAccessObject.ttl = require('./ttl');
|
||||
|
||||
KeyValueAccessObject.getConnector = function() {
|
||||
return this.getDataSource().connector;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Get remaining expiration (TTL) for a given key.
|
||||
*
|
||||
* @param {String} key
|
||||
* @param {Object} options
|
||||
* @callback cb
|
||||
* @param {Error} error
|
||||
* @param {Number} ttl The remaining TTL for the given key. `undefined` if TTL
|
||||
* was not initially set.
|
||||
*
|
||||
* @header KVAO.ttl(key, cb)
|
||||
*/
|
||||
module.exports = function keyValueTtl(key, options, callback) {
|
||||
if (callback == undefined && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
assert(typeof key === 'string' && key, 'key must be a non-empty string');
|
||||
assert(typeof options === 'object', 'options must be an object');
|
||||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
this.getConnector().ttl(this.modelName, key, options, callback);
|
||||
return callback.promise;
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
'use strict';
|
||||
|
||||
var should = require('should');
|
||||
var helpers = require('./_helpers');
|
||||
var Promise = require('bluebird');
|
||||
|
||||
module.exports = function(dataSourceFactory, connectorCapabilities) {
|
||||
describe('ttl', function() {
|
||||
var CacheItem;
|
||||
beforeEach(function unpackContext() {
|
||||
CacheItem = helpers.givenCacheItem(dataSourceFactory);
|
||||
});
|
||||
|
||||
it('returns an error when key does not exist', function() {
|
||||
return CacheItem.ttl('key-does-not-exist').then(
|
||||
function() { throw new Error('ttl() should have failed'); },
|
||||
function(err) {
|
||||
err.message.should.match(/key-does-not-exist/);
|
||||
err.should.have.property('statusCode', 404);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns `undefined` when key does not expire', function() {
|
||||
return CacheItem.set('a-key', 'a-value')
|
||||
.then(function() { return CacheItem.ttl('a-key'); })
|
||||
.then(function(ttl) { should.not.exist(ttl); });
|
||||
});
|
||||
|
||||
context('existing key with expire before expiration time', function() {
|
||||
it('returns ttl - Callback API', function(done) {
|
||||
CacheItem.set('a-key', 'a-value', 10, function(err) {
|
||||
if (err) return done(err);
|
||||
CacheItem.ttl('a-key', function(err, ttl) {
|
||||
if (err) return done(err);
|
||||
ttl.should.be.within(0, 10);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('returns ttl - Promise API', function() {
|
||||
return CacheItem.set('a-key', 'a-value', 10)
|
||||
.delay(1)
|
||||
.then(function() { return CacheItem.ttl('a-key'); })
|
||||
.then(function(ttl) { ttl.should.be.within(0, 10); });
|
||||
});
|
||||
});
|
||||
|
||||
context('existing key with expire after expiration time', function(done) {
|
||||
it('returns an error', function() {
|
||||
return CacheItem.set('key-does-not-exist', 'a-value', 10)
|
||||
.delay(20)
|
||||
.then(function() {
|
||||
return CacheItem.ttl('key-does-not-exist');
|
||||
})
|
||||
.then(
|
||||
function() { throw new Error('ttl() should have failed'); },
|
||||
function(err) {
|
||||
err.message.should.match(/key-does-not-exist/);
|
||||
err.should.have.property('statusCode', 404);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
Loading…
Reference in New Issue