Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StaleIn can be function. #105

Merged
merged 1 commit into from
Nov 19, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ The object is constructed using `new Policy(options, [cache, segment])` where:
- `value` - the new value generated.
- `ttl` - the cache ttl value in milliseconds. Set to `0` to skip storing in the cache. Defaults to the cache global policy.
- `staleIn` - number of milliseconds to mark an item stored in cache as stale and attempt to regenerate it when `generateFunc` is
provided. Must be less than `expiresIn`.
provided. Must be less than `expiresIn`. Alternatively function that returns staleIn value in miliseconds. The function signature is `function(stored, ttl)` where:
- `stored` - the timestamp when the item was stored in the cache (in milliseconds).
- `ttl` - the remaining time-to-live (not the original value used when storing the object).
- `staleTimeout` - number of milliseconds to wait before checking if an item is stale.
- `generateTimeout` - number of milliseconds to wait before returning a timeout error when the `generateFunc` function
takes too long to return a value. When the value is eventually returned, it is stored in the cache for future requests.
Expand Down
10 changes: 5 additions & 5 deletions lib/policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ internals.Policy.prototype.get = function (key, callback) { // key: string o
if (cached) {
report.stored = cached.stored;
report.ttl = cached.ttl;

cached.isStale = (self.rule.staleIn ? (Date.now() - cached.stored) >= self.rule.staleIn : false);
var staleIn = typeof self.rule.staleIn === 'function' ? self.rule.staleIn(cached.stored, cached.ttl) : self.rule.staleIn;
cached.isStale = (staleIn ? (Date.now() - cached.stored) >= staleIn : false);
report.isStale = cached.isStale;
}

Expand Down Expand Up @@ -205,7 +205,7 @@ internals.Policy.prototype.ttl = function (created) {
internals.schema = Joi.object({
expiresIn: Joi.number().integer().min(1),
expiresAt: Joi.string().regex(/^\d\d?\:\d\d$/),
staleIn: Joi.number().integer().min(1).max(86400000 - 1), // One day - 1 (max is inclusive)
staleIn: [Joi.number().integer().min(1).max(86400000 - 1), Joi.func()], // One day - 1 (max is inclusive)
staleTimeout: Joi.number().integer().min(1),
generateFunc: Joi.func(),
generateTimeout: Joi.number().integer().min(1),
Expand Down Expand Up @@ -257,10 +257,10 @@ internals.Policy.compile = function (options, serverSide) {

Hoek.assert(!hasExpiresAt || typeof options.expiresAt === 'string', 'expiresAt must be a string', options);
Hoek.assert(!hasExpiresIn || Hoek.isInteger(options.expiresIn), 'expiresIn must be an integer', options);
Hoek.assert(!hasExpiresIn || !options.staleIn || options.staleIn < options.expiresIn, 'staleIn must be less than expiresIn');
Hoek.assert(!hasExpiresIn || !options.staleIn || typeof options.staleIn === 'function' || options.staleIn < options.expiresIn, 'staleIn must be less than expiresIn');
Hoek.assert(!options.staleIn || serverSide, 'Cannot use stale options without server-side caching');
Hoek.assert(!options.staleTimeout || !hasExpiresIn || options.staleTimeout < options.expiresIn, 'staleTimeout must be less than expiresIn');
Hoek.assert(!options.staleTimeout || !hasExpiresIn || options.staleTimeout < (options.expiresIn - options.staleIn), 'staleTimeout must be less than the delta between expiresIn and staleIn');
Hoek.assert(!options.staleTimeout || !hasExpiresIn || typeof options.staleIn === 'function' || options.staleTimeout < (options.expiresIn - options.staleIn), 'staleTimeout must be less than the delta between expiresIn and staleIn');

// Expiration

Expand Down
74 changes: 74 additions & 0 deletions test/policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,57 @@ describe('Policy', function () {
});
});

it('returns stale object then fresh object based on timing using staleIn function', function (done) {

var staleIn = function (stored, ttl) {

var expiresIn = (Date.now() - stored) + ttl;
expect(expiresIn).to.be.about(100, 5);
return expiresIn - 80;
};

var rule = {
expiresIn: 100,
staleIn: staleIn,
staleTimeout: 5,
generateFunc: function (id, next) {

setTimeout(function () {

return next(null, { gen: ++gen }, 100);
}, 6);
}
};

var client = new Catbox.Client(Import, { partition: 'test-partition' });
var policy = new Catbox.Policy(rule, client, 'test-segment');

var gen = 0;

client.start(function () {

policy.get('test', function (err, value1, cached, report) {

expect(value1.gen).to.equal(1); // Fresh
setTimeout(function () {

policy.get('test', function (err, value2, cached, report) {

expect(value2.gen).to.equal(1); // Stale
setTimeout(function () {

policy.get('test', function (err, value3, cached, report) {

expect(value3.gen).to.equal(2); // Fresh
done();
});
}, 3);
});
}, 21);
});
});
});

it('returns stale object then invalidate cache on error', function (done) {

var rule = {
Expand Down Expand Up @@ -1272,6 +1323,29 @@ describe('Policy', function () {
done();
});

it('allows combination of expiresIn, staleTimeout and staleIn function', function (done) {

var staleIn = function (stored, ttl) {

return 1000;
};

var config = {
expiresIn: 500000,
staleIn: staleIn,
staleTimeout: 500,
generateFunc: function () { }
};

var fn = function () {

Catbox.policy.compile(config, true);
};

expect(fn).to.not.throw();
done();
});

it('throws an error when staleIn is greater than expiresIn', function (done) {

var config = {
Expand Down