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];
|
return this._store[modelName];
|
||||||
};
|
};
|
||||||
|
|
||||||
KeyValueMemoryConnector.prototype.get =
|
KeyValueMemoryConnector.prototype._removeIfExpired = function(modelName, key) {
|
||||||
function(modelName, key, options, callback) {
|
|
||||||
var store = this._getStoreForModel(modelName);
|
var store = this._getStoreForModel(modelName);
|
||||||
var item = store[key];
|
var item = store[key];
|
||||||
|
|
||||||
if (item && item.isExpired()) {
|
if (item && item.isExpired()) {
|
||||||
debug('Removing expired key', key);
|
debug('Removing expired key', key);
|
||||||
delete store[key];
|
delete store[key];
|
||||||
item = undefined;
|
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;
|
var value = item ? item.value : null;
|
||||||
|
|
||||||
debug('GET %j %j -> %s', modelName, key, value);
|
debug('GET %j %j -> %s', modelName, key, value);
|
||||||
|
|
||||||
if (/^buffer:/.test(value)) {
|
if (/^buffer:/.test(value)) {
|
||||||
|
@ -127,6 +131,29 @@ function(modelName, key, ttl, options, callback) {
|
||||||
process.nextTick(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) {
|
KeyValueMemoryConnector.prototype.disconnect = function(callback) {
|
||||||
if (this._cleanupTimer)
|
if (this._cleanupTimer)
|
||||||
clearInterval(this._cleanupTimer);
|
clearInterval(this._cleanupTimer);
|
||||||
|
@ -150,3 +177,7 @@ StoreItem.prototype.setTtl = function(ttl) {
|
||||||
this.expires = undefined;
|
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.get = require('./get');
|
||||||
KeyValueAccessObject.set = require('./set');
|
KeyValueAccessObject.set = require('./set');
|
||||||
KeyValueAccessObject.expire = require('./expire');
|
KeyValueAccessObject.expire = require('./expire');
|
||||||
|
KeyValueAccessObject.ttl = require('./ttl');
|
||||||
|
|
||||||
KeyValueAccessObject.getConnector = function() {
|
KeyValueAccessObject.getConnector = function() {
|
||||||
return this.getDataSource().connector;
|
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