Comparing version 3.4.3 to 4.0.0
@@ -31,3 +31,3 @@ // Load modules | ||
internals.Policy.prototype.get = function (key, callback, _generateFunc) { // key: string or { id: 'id' } | ||
internals.Policy.prototype.get = function (key, callback) { // key: string or { id: 'id' } | ||
@@ -51,4 +51,15 @@ var self = this; | ||
// Prepare report | ||
var report = { | ||
msec: timer.elapsed(), | ||
error: err | ||
}; | ||
if (cached) { | ||
report.stored = cached.stored; | ||
report.ttl = cached.ttl; | ||
cached.isStale = (self.rule.staleIn ? (Date.now() - cached.stored) >= self.rule.staleIn : false); | ||
report.isStale = cached.isStale; | ||
} | ||
@@ -58,6 +69,4 @@ | ||
if (!self.rule.generateFunc && | ||
!_generateFunc) { | ||
return self._finalize(id, err, cached); // Pass 'cached' as 'value' and omit other arguments for backwards compatibility | ||
if (!self.rule.generateFunc) { | ||
return self._finalize(id, err, cached ? cached.item : null, cached, report); | ||
} | ||
@@ -68,14 +77,5 @@ | ||
if (err || !cached) { | ||
return self._generate(id, key, null, { msec: timer.elapsed(), error: err }, callback, _generateFunc); | ||
return self._generate(id, key, null, report, callback); | ||
} | ||
// Found | ||
var report = { | ||
msec: timer.elapsed(), | ||
stored: cached.stored, | ||
ttl: cached.ttl, | ||
isStale: cached.isStale | ||
}; | ||
// Check if found and fresh | ||
@@ -87,3 +87,3 @@ | ||
return self._generate(id, key, cached, report, callback, _generateFunc); | ||
return self._generate(id, key, cached, report, callback); | ||
}); | ||
@@ -103,3 +103,3 @@ }; | ||
internals.Policy.prototype._generate = function (id, key, cached, report, callback, _generateFunc) { | ||
internals.Policy.prototype._generate = function (id, key, cached, report, callback) { | ||
@@ -139,13 +139,13 @@ var self = this; | ||
try { | ||
(this.rule.generateFunc || _generateFunc).call(null, key, function (err, value, ttl) { | ||
this.rule.generateFunc.call(null, key, function (err, value, ttl) { | ||
// Error or not cached | ||
// Error (if dropOnError is not set to false) or not cached | ||
if (err || | ||
ttl === 0) { // null or undefined means use policy | ||
if ((err && self.rule.dropOnError) || | ||
ttl === 0) { // null or undefined means use policy | ||
self.drop(id); // Invalidate cache | ||
self.drop(id); // Invalidate cache | ||
} | ||
else { | ||
self.set(id, value, ttl); // Lazy save (replaces stale cache copy with late-coming fresh copy) | ||
else if (!err) { | ||
self.set(id, value, ttl); // Lazy save (replaces stale cache copy with late-coming fresh copy) | ||
} | ||
@@ -173,15 +173,2 @@ | ||
internals.Policy.prototype.getOrGenerate = function (id, generateFunc, callback) { // For backwards compatibility | ||
var self = this; | ||
var generateFuncWrapper = function (id, next) { | ||
return generateFunc(next); | ||
}; | ||
return this.get(id, callback, generateFuncWrapper); | ||
}; | ||
internals.Policy.prototype.set = function (key, value, ttl, callback) { | ||
@@ -228,3 +215,4 @@ | ||
staleIn: 20000, | ||
staleTimeout: 500 | ||
staleTimeout: 500, | ||
dropOnError: true | ||
} | ||
@@ -248,11 +236,12 @@ */ | ||
Hoek.assert(!hasExpiresIn || Hoek.isInteger(options.expiresIn), 'expiresIn must be an integer', options); | ||
Hoek.assert(!hasExpiresIn || !hasExpiresAt, 'Rule cannot include both expiresIn and expiresAt', options); // XOR | ||
Hoek.assert(!hasExpiresIn || !hasExpiresAt, 'Rule cannot include both expiresIn and expiresAt', options); // XOR | ||
Hoek.assert(!hasExpiresAt || !options.staleIn || options.staleIn < 86400000, 'staleIn must be less than 86400000 milliseconds (one day) when using expiresAt'); | ||
Hoek.assert(!hasExpiresIn || !options.staleIn || 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.staleIn ^ !!options.staleTimeout), 'Rule must include both of staleIn and staleTimeout or none'); // XNOR | ||
Hoek.assert(!options.generateFunc || typeof options.generateFunc === 'function', 'generateFunc must be a function'); | ||
Hoek.assert(options.generateFunc || !options.staleIn, 'Cannot use stale options without generateFunc'); | ||
Hoek.assert(options.generateFunc || !options.generateTimeout, 'Rule cannot include generateTimeout without generateFunc'); | ||
Hoek.assert(!(!!options.staleIn ^ !!options.staleTimeout), 'Rule must include both of staleIn and staleTimeout or none'); // XNOR | ||
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.generateFunc || !options.generateTimeout, 'Rule cannot include generateTimeout without generateFunc'); // Disabled for backwards compatibility | ||
Hoek.assert(!options.generateFunc || typeof options.generateFunc === 'function', 'generateFunc must be a function'); | ||
@@ -284,13 +273,12 @@ // Expiration | ||
rule.generateFunc = options.generateFunc; | ||
} | ||
if (options.generateTimeout) { // Keep outside options.generateFunc condition for backwards compatibility | ||
rule.generateTimeout = options.generateTimeout; | ||
} | ||
// Stale | ||
// Stale | ||
if (options.staleIn) { // Keep outside options.generateFunc condition for backwards compatibility | ||
rule.staleIn = options.staleIn; | ||
rule.staleTimeout = options.staleTimeout; | ||
if (options.staleIn) { | ||
rule.staleIn = options.staleIn; | ||
rule.staleTimeout = options.staleTimeout; | ||
} | ||
rule.dropOnError = options.dropOnError !== undefined ? options.dropOnError : true; // Defaults to true | ||
} | ||
@@ -297,0 +285,0 @@ |
{ | ||
"name": "catbox", | ||
"description": "Multi-strategy object caching service", | ||
"version": "3.4.3", | ||
"version": "4.0.0", | ||
"repository": "git://github.com/hapijs/catbox", | ||
@@ -6,0 +6,0 @@ "main": "index", |
![catbox Logo](https://raw.github.com/hapijs/catbox/master/images/catbox.png) | ||
Multi-strategy object caching service | ||
Version: **3.x** | ||
Version: **4.x** | ||
@@ -105,4 +105,3 @@ [![Build Status](https://secure.travis-ci.org/hapijs/catbox.png)](http://travis-ci.org/hapijs/catbox) | ||
- `id` - the unique item identifier (within the policy segment). Can be a string or an object with the required 'id' key. | ||
- `callback` - the return function. The function signature is based on the `generateFunc` settings. If the `generateFunc` is not set, | ||
the signature is `function(err, cached)`. Otherwise, the signature is `function(err, value, cached, report)` where: | ||
- `callback` - the return function. The function signature is `function(err, value, cached, report)` where: | ||
- `err` - any errors encountered. | ||
@@ -109,0 +108,0 @@ - `value` - the fetched or generated value. |
@@ -36,6 +36,6 @@ // Load modules | ||
policy.get('x', function (err, result) { | ||
policy.get('x', function (err, value, cached, report) { | ||
expect(err).to.not.exist; | ||
expect(result.item).to.equal('123'); | ||
expect(value).to.equal('123'); | ||
done(); | ||
@@ -59,6 +59,6 @@ }); | ||
policy.get('x', function (err, result) { | ||
policy.get('x', function (err, value, cached, report) { | ||
expect(err).to.not.exist; | ||
expect(result).to.not.exist; | ||
expect(value).to.not.exist; | ||
done(); | ||
@@ -82,6 +82,6 @@ }); | ||
policy.get('x', function (err, result) { | ||
policy.get('x', function (err, value, cached, report) { | ||
expect(err).to.not.exist; | ||
expect(result.item).to.equal('123'); | ||
expect(value).to.equal('123'); | ||
done(); | ||
@@ -97,6 +97,6 @@ }); | ||
policy.get('x', function (err, result) { | ||
policy.get('x', function (err, value, cached, report) { | ||
expect(err).to.not.exist; | ||
expect(result).to.not.exist; | ||
expect(value).to.not.exist; | ||
done(); | ||
@@ -138,6 +138,6 @@ }); | ||
client.get(key, function (err, result) { | ||
client.get(key, function (err, value, cached, report) { | ||
expect(err).to.not.exist; | ||
expect(result).to.not.exist; | ||
expect(value).to.not.exist; | ||
done(); | ||
@@ -165,6 +165,6 @@ }); | ||
policy.get({ id: 'x' }, function (err, result) { | ||
policy.get({ id: 'x' }, function (err, value, cached, report) { | ||
expect(err).to.not.exist; | ||
expect(result.item).to.equal('123'); | ||
expect(value).to.equal('123'); | ||
done(); | ||
@@ -190,3 +190,3 @@ }); | ||
policy.get(null, function (err, result) { | ||
policy.get(null, function (err, value, cached, report) { | ||
@@ -228,6 +228,6 @@ expect(err).to.exist; | ||
policy.get('test1', function (err, result) { | ||
policy.get('test1', function (err, value, cached, report) { | ||
expect(err).to.be.instanceOf(Error); | ||
expect(result).to.not.exist; | ||
expect(value).to.not.exist; | ||
done(); | ||
@@ -267,6 +267,6 @@ }); | ||
policy.get('test1', function (err, result) { | ||
policy.get('test1', function (err, value, cached, report) { | ||
expect(result.item).to.equal('item'); | ||
expect(result.isStale).to.be.false; | ||
expect(value).to.equal('item'); | ||
expect(cached.isStale).to.be.false; | ||
done(); | ||
@@ -312,6 +312,6 @@ }); | ||
policy.get('test1', function (err, item) { | ||
policy.get('test1', function (err, value, cached, report) { | ||
expect(err).to.equal(null); | ||
expect(item).to.equal(false); | ||
expect(value).to.equal(false); | ||
done(); | ||
@@ -331,3 +331,3 @@ }); | ||
policy.get('test', function (err, value, cached) { | ||
policy.get('test', function (err, value, cached, report) { | ||
@@ -360,3 +360,3 @@ expect(err).to.not.exist; | ||
policy.get('test', function (err, value, cached) { | ||
policy.get('test', function (err, value, cached, report) { | ||
@@ -389,3 +389,3 @@ expect(value.gen).to.equal(1); | ||
policy.get('test', function (err, value, cached) { | ||
policy.get('test', function (err, value, cached, report) { | ||
@@ -420,3 +420,3 @@ expect(value.gen).to.equal(1); | ||
policy.get('test', function (err, value1, cached) { | ||
policy.get('test', function (err, value1, cached, report) { | ||
@@ -426,3 +426,3 @@ expect(value1.gen).to.equal(1); // Fresh | ||
policy.get('test', function (err, value2, cached) { | ||
policy.get('test', function (err, value2, cached, report) { | ||
@@ -459,3 +459,3 @@ expect(value2.gen).to.equal(1); // Stale | ||
policy.get('test', function (err, value1, cached) { | ||
policy.get('test', function (err, value1, cached, report) { | ||
@@ -465,3 +465,3 @@ expect(value1.gen).to.equal(1); // Fresh | ||
policy.get('test', function (err, value2, cached) { | ||
policy.get('test', function (err, value2, cached, report) { | ||
@@ -471,3 +471,3 @@ expect(value2.gen).to.equal(1); // Stale | ||
policy.get('test', function (err, value3, cached) { | ||
policy.get('test', function (err, value3, cached, report) { | ||
@@ -512,3 +512,3 @@ expect(value3.gen).to.equal(2); // Fresh | ||
policy.get('test', function (err, value1, cached) { | ||
policy.get('test', function (err, value1, cached, report) { | ||
@@ -518,3 +518,3 @@ expect(value1.gen).to.equal(1); // Fresh | ||
policy.get('test', function (err, value2, cached) { | ||
policy.get('test', function (err, value2, cached, report) { | ||
@@ -526,3 +526,3 @@ // Generates a new one in background which will produce Error and clear the cache | ||
policy.get('test', function (err, value3, cached) { | ||
policy.get('test', function (err, value3, cached, report) { | ||
@@ -539,2 +539,160 @@ expect(value3.gen).to.equal(3); // Fresh | ||
it('returns stale object then invalidate cache on error when dropOnError is true', function (done) { | ||
var rule = { | ||
expiresIn: 100, | ||
staleIn: 20, | ||
staleTimeout: 5, | ||
dropOnError: true, | ||
generateFunc: function (id, next) { | ||
++gen; | ||
setTimeout(function () { | ||
if (gen === 1) { | ||
return next(null, { gen: gen }); | ||
} | ||
return next(new Error()); | ||
}, 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) { | ||
// Generates a new one in background which will produce Error and clear the cache | ||
expect(value2.gen).to.equal(1); // Stale | ||
setTimeout(function () { | ||
policy.get('test', function (err, value3, cached, report) { | ||
expect(err).to.be.instanceof(Error); // Stale | ||
done(); | ||
}); | ||
}, 3); | ||
}); | ||
}, 21); | ||
}); | ||
}); | ||
}); | ||
it('returns stale object then invalidate cache on error when dropOnError is not set', function (done) { | ||
var rule = { | ||
expiresIn: 100, | ||
staleIn: 20, | ||
staleTimeout: 5, | ||
generateFunc: function (id, next) { | ||
++gen; | ||
setTimeout(function () { | ||
if (gen === 1) { | ||
return next(null, { gen: gen }); | ||
} | ||
return next(new Error()); | ||
}, 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) { | ||
// Generates a new one in background which will produce Error and clear the cache | ||
expect(value2.gen).to.equal(1); // Stale | ||
setTimeout(function () { | ||
policy.get('test', function (err, value3, cached, report) { | ||
expect(err).to.be.instanceof(Error); // Stale | ||
done(); | ||
}); | ||
}, 3); | ||
}); | ||
}, 21); | ||
}); | ||
}); | ||
}); | ||
it('returns stale object then does not invalidate cache on error if dropOnError is false', function (done) { | ||
var rule = { | ||
expiresIn: 100, | ||
staleIn: 20, | ||
staleTimeout: 5, | ||
dropOnError: false, | ||
generateFunc: function (id, next) { | ||
++gen; | ||
setTimeout(function () { | ||
if (gen === 1) { | ||
return next(null, { gen: gen }); | ||
} | ||
return next(new Error()); | ||
}, 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) { | ||
// Generates a new one in background which will produce Error, but not clear the cache | ||
expect(value2.gen).to.equal(1); // Stale | ||
setTimeout(function () { | ||
policy.get('test', function (err, value3, cached, report) { | ||
expect(value3.gen).to.equal(1); // Stale | ||
done(); | ||
}); | ||
}, 3); | ||
}); | ||
}, 21); | ||
}); | ||
}); | ||
}); | ||
it('returns fresh objects', function (done) { | ||
@@ -559,3 +717,3 @@ | ||
policy.get('test', function (err, value1, cached) { | ||
policy.get('test', function (err, value1, cached, report) { | ||
@@ -565,3 +723,3 @@ expect(value1.gen).to.equal(1); // Fresh | ||
policy.get('test', function (err, value2, cached) { | ||
policy.get('test', function (err, value2, cached, report) { | ||
@@ -572,3 +730,3 @@ expect(value2.gen).to.equal(2); // Fresh | ||
policy.get('test', function (err, value3, cached) { | ||
policy.get('test', function (err, value3, cached, report) { | ||
@@ -609,3 +767,3 @@ expect(value3.gen).to.equal(2); // Fresh | ||
policy.get('test', function (err, value1, cached) { | ||
policy.get('test', function (err, value1, cached, report) { | ||
@@ -615,3 +773,3 @@ expect(value1.gen).to.equal(1); // Fresh | ||
policy.get('test', function (err, value2, cached) { | ||
policy.get('test', function (err, value2, cached, report) { | ||
@@ -647,3 +805,3 @@ // Generates a new one which will produce Error | ||
policy.get('test', function (err, value1, cached) { | ||
policy.get('test', function (err, value1, cached, report) { | ||
@@ -653,3 +811,3 @@ expect(value1.gen).to.equal(1); // Fresh | ||
policy.get('test', function (err, value2, cached) { | ||
policy.get('test', function (err, value2, cached, report) { | ||
@@ -659,3 +817,3 @@ expect(value2.gen).to.equal(1); // Fresh | ||
policy.get('test', function (err, value3, cached) { | ||
policy.get('test', function (err, value3, cached, report) { | ||
@@ -697,3 +855,3 @@ expect(value3.gen).to.equal(2); // Fresh | ||
policy.get('test', function (err, value1, cached) { | ||
policy.get('test', function (err, value1, cached, report) { | ||
@@ -703,3 +861,3 @@ expect(value1.gen).to.equal(1); // Fresh | ||
policy.get('test', function (err, value2, cached) { | ||
policy.get('test', function (err, value2, cached, report) { | ||
@@ -740,3 +898,3 @@ expect(err).to.exist; | ||
policy.get('test', function (err, value1, cached) { | ||
policy.get('test', function (err, value1, cached, report) { | ||
@@ -746,3 +904,3 @@ expect(err.output.statusCode).to.equal(503); | ||
policy.get('test', function (err, value2, cached) { | ||
policy.get('test', function (err, value2, cached, report) { | ||
@@ -752,3 +910,3 @@ expect(value2.gen).to.equal(1); | ||
policy.get('test', function (err, value3, cached) { | ||
policy.get('test', function (err, value3, cached, report) { | ||
@@ -782,3 +940,3 @@ expect(err.output.statusCode).to.equal(503); | ||
var result = null; | ||
var compare = function (err, value, cached) { | ||
var compare = function (err, value, cached, report) { | ||
@@ -816,3 +974,3 @@ if (!result) { | ||
var result = null; | ||
var compare = function (err, value, cached) { | ||
var compare = function (err, value, cached, report) { | ||
@@ -866,13 +1024,13 @@ if (!result) { | ||
policy.get('test', function (err, value1, cached) { // Cache lookup takes 10 + generate 5 | ||
policy.get('test', function (err, value1, cached, report) { // Cache lookup takes 10 + generate 5 | ||
expect(value1.gen).to.equal(1); // Fresh | ||
setTimeout(function () { // Wait for stale | ||
expect(value1.gen).to.equal(1); // Fresh | ||
setTimeout(function () { // Wait for stale | ||
policy.get('test', function (err, value2, cached) { // Cache lookup takes 10, generate comes back after 5 | ||
policy.get('test', function (err, value2, cached, report) { // Cache lookup takes 10, generate comes back after 5 | ||
expect(value2.gen).to.equal(2); // Fresh | ||
policy.get('test', function (err, value3, cached) { // Cache lookup takes 10 | ||
expect(value2.gen).to.equal(2); // Fresh | ||
policy.get('test', function (err, value3, cached, report) { // Cache lookup takes 10 | ||
expect(value3.gen).to.equal(2); // Cached (10 left to stale) | ||
expect(value3.gen).to.equal(2); // Cached (10 left to stale) | ||
@@ -1045,5 +1203,3 @@ client.connection.get = orig; | ||
var rule = Catbox.policy.compile(null); | ||
expect(rule).to.deep.equal({}); | ||
done(); | ||
@@ -1057,6 +1213,5 @@ }); | ||
}; | ||
var rule = Catbox.policy.compile(config, false); | ||
expect(rule.expiresIn).to.equal(config.expiresIn); | ||
done(); | ||
@@ -1070,2 +1225,3 @@ }); | ||
}; | ||
var fn = function () { | ||
@@ -1077,4 +1233,3 @@ | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('Invalid segment name: undefined (Empty string)'); | ||
done(); | ||
@@ -1088,6 +1243,5 @@ }); | ||
}; | ||
var rule = Catbox.policy.compile(config, false); | ||
expect(rule.expiresIn).to.equal(config.expiresIn); | ||
done(); | ||
@@ -1102,2 +1256,3 @@ }); | ||
}; | ||
var fn = function () { | ||
@@ -1109,3 +1264,2 @@ | ||
expect(fn).to.throw('Rule cannot include both expiresIn and expiresAt {"expiresAt":"02:00","expiresIn":50}'); | ||
done(); | ||
@@ -1119,2 +1273,3 @@ }); | ||
}; | ||
var fn = function () { | ||
@@ -1126,3 +1281,2 @@ | ||
expect(fn).to.throw('expiresAt must be a string {"expiresAt":0}'); | ||
done(); | ||
@@ -1142,3 +1296,2 @@ }); | ||
expect(fn).to.throw('Invalid time string for expiresAt: 4'); | ||
done(); | ||
@@ -1158,3 +1311,2 @@ }); | ||
expect(fn).to.throw('expiresIn must be an integer {"expiresIn":"50"}'); | ||
done(); | ||
@@ -1175,3 +1327,2 @@ }); | ||
expect(fn).to.throw('Rule cannot include both expiresIn and expiresAt {"expiresAt":"02:00","expiresIn":0}'); | ||
done(); | ||
@@ -1188,3 +1339,2 @@ }); | ||
expect(fn).to.not.throw(); | ||
done(); | ||
@@ -1201,3 +1351,2 @@ }); | ||
expect(fn).to.not.throw(); | ||
done(); | ||
@@ -1210,4 +1359,6 @@ }); | ||
expiresAt: '03:00', | ||
staleIn: 1000000 | ||
staleIn: 1000000, | ||
generateFunc: function () { } | ||
}; | ||
var fn = function () { | ||
@@ -1218,4 +1369,3 @@ | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('Rule must include both of staleIn and staleTimeout or none'); | ||
done(); | ||
@@ -1228,4 +1378,6 @@ }); | ||
expiresAt: '03:00', | ||
staleTimeout: 100 | ||
staleTimeout: 100, | ||
generateFunc: function () { } | ||
}; | ||
var fn = function () { | ||
@@ -1236,4 +1388,3 @@ | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('Rule must include both of staleIn and staleTimeout or none'); | ||
done(); | ||
@@ -1247,4 +1398,6 @@ }); | ||
staleIn: 100000000, | ||
staleTimeout: 500 | ||
staleTimeout: 500, | ||
generateFunc: function () { } | ||
}; | ||
var fn = function () { | ||
@@ -1255,4 +1408,3 @@ | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('staleIn must be less than 86400000 milliseconds (one day) when using expiresAt'); | ||
done(); | ||
@@ -1266,4 +1418,6 @@ }); | ||
staleIn: 1000000, | ||
staleTimeout: 500 | ||
staleTimeout: 500, | ||
generateFunc: function () { } | ||
}; | ||
var fn = function () { | ||
@@ -1274,4 +1428,3 @@ | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('staleIn must be less than expiresIn'); | ||
done(); | ||
@@ -1285,4 +1438,6 @@ }); | ||
staleIn: 100000, | ||
staleTimeout: 500000 | ||
staleTimeout: 500000, | ||
generateFunc: function () { } | ||
}; | ||
var fn = function () { | ||
@@ -1293,4 +1448,3 @@ | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('staleTimeout must be less than expiresIn'); | ||
done(); | ||
@@ -1304,4 +1458,6 @@ }); | ||
staleIn: 20000, | ||
staleTimeout: 10000 | ||
staleTimeout: 10000, | ||
generateFunc: function () { } | ||
}; | ||
var fn = function () { | ||
@@ -1312,4 +1468,3 @@ | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('staleTimeout must be less than the delta between expiresIn and staleIn'); | ||
done(); | ||
@@ -1323,4 +1478,6 @@ }); | ||
staleIn: 500000, | ||
staleTimeout: 500 | ||
staleTimeout: 500, | ||
generateFunc: function () { } | ||
}; | ||
var fn = function () { | ||
@@ -1331,4 +1488,3 @@ | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('Cannot use stale options without server-side caching'); | ||
done(); | ||
@@ -1342,9 +1498,9 @@ }); | ||
staleIn: 500000, | ||
staleTimeout: 500 | ||
staleTimeout: 500, | ||
generateFunc: function () { } | ||
}; | ||
var rule = Catbox.policy.compile(config, true); | ||
expect(rule.staleIn).to.equal(500 * 1000); | ||
expect(rule.expiresIn).to.equal(1000 * 1000); | ||
done(); | ||
@@ -1358,27 +1514,11 @@ }); | ||
staleIn: 5000000, | ||
staleTimeout: 500 | ||
staleTimeout: 500, | ||
generateFunc: function () { } | ||
}; | ||
var rule = Catbox.policy.compile(config, true); | ||
expect(rule.staleIn).to.equal(5000 * 1000); | ||
done(); | ||
}); | ||
it('throws an error if has only staleTimeout or staleIn', function (done) { | ||
var config = { | ||
staleIn: 30000, | ||
expiresIn: 60000 | ||
}; | ||
var fn = function () { | ||
Catbox.policy.compile(config, true); | ||
}; | ||
expect(fn).to.throw(Error); | ||
done(); | ||
}); | ||
it('does not throw an error if has both staleTimeout and staleIn', function (done) { | ||
@@ -1389,3 +1529,4 @@ | ||
staleTimeout: 300, | ||
expiresIn: 60000 | ||
expiresIn: 60000, | ||
generateFunc: function () { } | ||
}; | ||
@@ -1397,3 +1538,3 @@ | ||
}; | ||
expect(fn).to.not.throw(Error); | ||
expect(fn).to.not.throw(); | ||
done(); | ||
@@ -1415,3 +1556,3 @@ }); | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('Cannot use stale options without server-side caching'); | ||
done(); | ||
@@ -1425,3 +1566,4 @@ }); | ||
expiresIn: 60000, | ||
staleTimeout: 300 | ||
staleTimeout: 300, | ||
generateFunc: function () { } | ||
}; | ||
@@ -1439,4 +1581,5 @@ | ||
staleIn: 2000, | ||
expiresIn: 1000, | ||
staleTimeout: 3000 | ||
expiresIn: 10000, | ||
staleTimeout: 30000, | ||
generateFunc: function () { } | ||
}; | ||
@@ -1446,6 +1589,6 @@ | ||
Catbox.policy.compile(config, false); | ||
Catbox.policy.compile(config, true); | ||
}; | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('staleTimeout must be less than expiresIn'); | ||
done(); | ||
@@ -1467,3 +1610,3 @@ }); | ||
expect(fn).to.throw(Error); | ||
expect(fn).to.throw('staleIn must be less than expiresIn'); | ||
done(); | ||
@@ -1628,459 +1771,4 @@ }); | ||
}); | ||
describe('getOrGenerate()', function () { | ||
it('bypasses cache when not configured', function (done) { | ||
var cache = new Catbox.Policy({ expiresIn: 1 }); | ||
var generateFunc = function (callback) { | ||
callback(null, 'new result'); | ||
}; | ||
cache.getOrGenerate('test', generateFunc, function (err, value, cached) { | ||
expect(err).to.not.exist; | ||
expect(value).to.equal('new result'); | ||
expect(cached).to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it('returns the processed cached item', function (done) { | ||
var rule = { | ||
expiresIn: 100, | ||
staleIn: 20, | ||
staleTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
return callback(null, { gen: ++gen }); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value, cached) { | ||
expect(value.gen).to.equal(1); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('handles concurrent requests for different keys', function (done) { | ||
var rule = { | ||
expiresIn: 100, | ||
staleIn: 20, | ||
staleTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var generate = function (gen) { | ||
return function (callback) { | ||
return callback(null, { gen: gen }); | ||
}; | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('1', generate(1), function (err, value, cached) { | ||
expect(value.gen).to.equal(1); | ||
}); | ||
policy.getOrGenerate('2', generate(2), function (err, value, cached) { | ||
expect(value.gen).to.equal(2); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('returns the processed cached item after cache error', function (done) { | ||
var rule = { | ||
expiresIn: 100, | ||
staleIn: 20, | ||
staleTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
client.get = function (key, callback) { callback(new Error('bad client')); }; | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
return callback(null, { gen: ++gen }); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value, cached) { | ||
expect(value.gen).to.equal(1); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('returns the processed cached item using manual ttl', function (done) { | ||
var rule = { | ||
expiresIn: 26, | ||
staleIn: 20, | ||
staleTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
setTimeout(function () { | ||
return callback(null, { gen: ++gen }, 100); | ||
}, 6); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value1, cached) { | ||
expect(value1.gen).to.equal(1); // Fresh | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value2, cached) { | ||
expect(value2.gen).to.equal(1); // Stale | ||
done(); | ||
}); | ||
}, 27); | ||
}); | ||
}); | ||
}); | ||
it('returns stale object then fresh object based on timing', function (done) { | ||
var rule = { | ||
expiresIn: 100, | ||
staleIn: 20, | ||
staleTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
setTimeout(function () { | ||
return callback(null, { gen: ++gen }, 100); | ||
}, 6); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value1, cached) { | ||
expect(value1.gen).to.equal(1); // Fresh | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value2, cached) { | ||
expect(value2.gen).to.equal(1); // Stale | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value3, cached) { | ||
expect(value3.gen).to.equal(2); // Fresh | ||
done(); | ||
}); | ||
}, 3); | ||
}); | ||
}, 21); | ||
}); | ||
}); | ||
}); | ||
it('returns stale object then invalidate cache on error', function (done) { | ||
var rule = { | ||
expiresIn: 100, | ||
staleIn: 20, | ||
staleTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
++gen; | ||
setTimeout(function () { | ||
if (gen !== 2) { | ||
return callback(null, { gen: gen }); | ||
} | ||
return callback(new Error()); | ||
}, 6); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value1, cached) { | ||
expect(value1.gen).to.equal(1); // Fresh | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value2, cached) { | ||
// Generates a new one in background which will produce Error and clear the cache | ||
expect(value2.gen).to.equal(1); // Stale | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value3, cached) { | ||
expect(value3.gen).to.equal(3); // Fresh | ||
done(); | ||
}); | ||
}, 3); | ||
}); | ||
}, 21); | ||
}); | ||
}); | ||
}); | ||
it('returns fresh objects', function (done) { | ||
var rule = { | ||
expiresIn: 100, | ||
staleIn: 20, | ||
staleTimeout: 10 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
return callback(null, { gen: ++gen }); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value1, cached) { | ||
expect(value1.gen).to.equal(1); // Fresh | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value2, cached) { | ||
expect(value2.gen).to.equal(2); // Fresh | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value3, cached) { | ||
expect(value3.gen).to.equal(2); // Fresh | ||
done(); | ||
}); | ||
}, 1); | ||
}); | ||
}, 21); | ||
}); | ||
}); | ||
}); | ||
it('returns error when generated within stale timeout', function (done) { | ||
var rule = { | ||
expiresIn: 30, | ||
staleIn: 20, | ||
staleTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
++gen; | ||
if (gen !== 2) { | ||
return callback(null, { gen: gen }); | ||
} | ||
return callback(new Error()); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value1, cached) { | ||
expect(value1.gen).to.equal(1); // Fresh | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value2, cached) { | ||
// Generates a new one which will produce Error | ||
expect(err).to.be.instanceof(Error); // Stale | ||
done(); | ||
}); | ||
}, 21); | ||
}); | ||
}); | ||
}); | ||
it('returns new object when stale has less than staleTimeout time left', function (done) { | ||
var rule = { | ||
expiresIn: 25, | ||
staleIn: 15, | ||
staleTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
return callback(null, { gen: ++gen }); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value1, cached) { | ||
expect(value1.gen).to.equal(1); // Fresh | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value2, cached) { | ||
expect(value2.gen).to.equal(1); // Fresh | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value3, cached) { | ||
expect(value3.gen).to.equal(2); // Fresh | ||
done(); | ||
}); | ||
}, 11); | ||
}); | ||
}, 10); | ||
}); | ||
}); | ||
}); | ||
it('invalidates cache on error without stale', function (done) { | ||
var rule = { | ||
expiresIn: 20, | ||
staleIn: 5, | ||
staleTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
++gen; | ||
if (gen === 2) { | ||
return callback(new Error()); | ||
} | ||
return callback(null, { gen: gen }); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value1, cached) { | ||
expect(value1.gen).to.equal(1); // Fresh | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value2, cached) { | ||
expect(err).to.exist; | ||
policy.get('test', function (err, value3) { | ||
expect(value3).to.equal(null); | ||
done(); | ||
}); | ||
}); | ||
}, 8); | ||
}); | ||
}); | ||
}); | ||
it('returns timeout error when generate takes too long', function (done) { | ||
var rule = { | ||
expiresIn: 10, | ||
generateTimeout: 5 | ||
}; | ||
var client = new Catbox.Client(Import, { partition: 'test-partition' }); | ||
var policy = new Catbox.Policy(rule, client, 'test-segment'); | ||
var gen = 0; | ||
var generateFunc = function (callback) { | ||
setTimeout(function () { | ||
return callback(null, { gen: ++gen }); | ||
}, 6); | ||
}; | ||
client.start(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value1, cached) { | ||
expect(err.output.statusCode).to.equal(503); | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value2, cached) { | ||
expect(value2.gen).to.equal(1); | ||
setTimeout(function () { | ||
policy.getOrGenerate('test', generateFunc, function (err, value3, cached) { | ||
expect(err.output.statusCode).to.equal(503); | ||
done(); | ||
}); | ||
}, 10); | ||
}); | ||
}, 2); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
147389
2125
130