Skip to content

Commit 827f03a

Browse files
authored
Merge pull request #1030 from strongloop/ttl
Ttl
2 parents d7cf478 + 1c20cc8 commit 827f03a

File tree

4 files changed

+133
-4
lines changed

4 files changed

+133
-4
lines changed

lib/connectors/kv-memory.js

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,23 @@ KeyValueMemoryConnector.prototype._getStoreForModel = function(modelName) {
6464
return this._store[modelName];
6565
};
6666

67-
KeyValueMemoryConnector.prototype.get =
68-
function(modelName, key, options, callback) {
67+
KeyValueMemoryConnector.prototype._removeIfExpired = function(modelName, key) {
6968
var store = this._getStoreForModel(modelName);
7069
var item = store[key];
71-
7270
if (item && item.isExpired()) {
7371
debug('Removing expired key', key);
7472
delete store[key];
7573
item = undefined;
7674
}
75+
};
7776

78-
var value = item ? item.value : null;
77+
KeyValueMemoryConnector.prototype.get =
78+
function(modelName, key, options, callback) {
79+
this._removeIfExpired(modelName, key);
7980

81+
var store = this._getStoreForModel(modelName);
82+
var item = store[key];
83+
var value = item ? item.value : null;
8084
debug('GET %j %j -> %s', modelName, key, value);
8185

8286
if (/^buffer:/.test(value)) {
@@ -127,6 +131,29 @@ function(modelName, key, ttl, options, callback) {
127131
process.nextTick(callback);
128132
};
129133

134+
KeyValueMemoryConnector.prototype.ttl =
135+
function(modelName, key, options, callback) {
136+
this._removeIfExpired(modelName, key);
137+
138+
var store = this._getStoreForModel(modelName);
139+
140+
// key is unknown
141+
if (!(key in store)) {
142+
return process.nextTick(function() {
143+
var err = new Error('Cannot get TTL for unknown key ' + key);
144+
err.statusCode = 404;
145+
callback(err);
146+
});
147+
}
148+
149+
var ttl = store[key].getTtl();
150+
debug('TTL %j %j -> %s', modelName, key, ttl);
151+
152+
process.nextTick(function() {
153+
callback(null, ttl);
154+
});
155+
};
156+
130157
KeyValueMemoryConnector.prototype.disconnect = function(callback) {
131158
if (this._cleanupTimer)
132159
clearInterval(this._cleanupTimer);
@@ -150,3 +177,7 @@ StoreItem.prototype.setTtl = function(ttl) {
150177
this.expires = undefined;
151178
}
152179
};
180+
181+
StoreItem.prototype.getTtl = function() {
182+
return !this.expires ? undefined : this.expires - Date.now();
183+
};

lib/kvao/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = KeyValueAccessObject;
88
KeyValueAccessObject.get = require('./get');
99
KeyValueAccessObject.set = require('./set');
1010
KeyValueAccessObject.expire = require('./expire');
11+
KeyValueAccessObject.ttl = require('./ttl');
1112

1213
KeyValueAccessObject.getConnector = function() {
1314
return this.getDataSource().connector;

lib/kvao/ttl.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
3+
var assert = require('assert');
4+
var utils = require('../utils');
5+
6+
/**
7+
* Get remaining expiration (TTL) for a given key.
8+
*
9+
* @param {String} key
10+
* @param {Object} options
11+
* @callback cb
12+
* @param {Error} error
13+
* @param {Number} ttl The remaining TTL for the given key. `undefined` if TTL
14+
* was not initially set.
15+
*
16+
* @header KVAO.ttl(key, cb)
17+
*/
18+
module.exports = function keyValueTtl(key, options, callback) {
19+
if (callback == undefined && typeof options === 'function') {
20+
callback = options;
21+
options = {};
22+
} else if (!options) {
23+
options = {};
24+
}
25+
26+
assert(typeof key === 'string' && key, 'key must be a non-empty string');
27+
assert(typeof options === 'object', 'options must be an object');
28+
29+
callback = callback || utils.createPromiseCallback();
30+
this.getConnector().ttl(this.modelName, key, options, callback);
31+
return callback.promise;
32+
};

test/kvao/ttl.suite.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'use strict';
2+
3+
var should = require('should');
4+
var helpers = require('./_helpers');
5+
var Promise = require('bluebird');
6+
7+
module.exports = function(dataSourceFactory, connectorCapabilities) {
8+
describe('ttl', function() {
9+
var CacheItem;
10+
beforeEach(function unpackContext() {
11+
CacheItem = helpers.givenCacheItem(dataSourceFactory);
12+
});
13+
14+
it('returns an error when key does not exist', function() {
15+
return CacheItem.ttl('key-does-not-exist').then(
16+
function() { throw new Error('ttl() should have failed'); },
17+
function(err) {
18+
err.message.should.match(/key-does-not-exist/);
19+
err.should.have.property('statusCode', 404);
20+
});
21+
});
22+
23+
it('returns `undefined` when key does not expire', function() {
24+
return CacheItem.set('a-key', 'a-value')
25+
.then(function() { return CacheItem.ttl('a-key'); })
26+
.then(function(ttl) { should.not.exist(ttl); });
27+
});
28+
29+
context('existing key with expire before expiration time', function() {
30+
it('returns ttl - Callback API', function(done) {
31+
CacheItem.set('a-key', 'a-value', 10, function(err) {
32+
if (err) return done(err);
33+
CacheItem.ttl('a-key', function(err, ttl) {
34+
if (err) return done(err);
35+
ttl.should.be.within(0, 10);
36+
done();
37+
});
38+
});
39+
});
40+
41+
it('returns ttl - Promise API', function() {
42+
return CacheItem.set('a-key', 'a-value', 10)
43+
.delay(1)
44+
.then(function() { return CacheItem.ttl('a-key'); })
45+
.then(function(ttl) { ttl.should.be.within(0, 10); });
46+
});
47+
});
48+
49+
context('existing key with expire after expiration time', function(done) {
50+
it('returns an error', function() {
51+
return CacheItem.set('key-does-not-exist', 'a-value', 10)
52+
.delay(20)
53+
.then(function() {
54+
return CacheItem.ttl('key-does-not-exist');
55+
})
56+
.then(
57+
function() { throw new Error('ttl() should have failed'); },
58+
function(err) {
59+
err.message.should.match(/key-does-not-exist/);
60+
err.should.have.property('statusCode', 404);
61+
});
62+
});
63+
});
64+
});
65+
};

0 commit comments

Comments
 (0)