From 5f94b5fd3f2348984328d8d0992c71949b9ca878 Mon Sep 17 00:00:00 2001 From: Jan Krems Date: Tue, 26 Jan 2016 13:48:20 -0800 Subject: [PATCH] feat: True timeouts for cache calls --- lib/cache.js | 12 +++++-- test/timeout.test.js | 80 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 test/timeout.test.js diff --git a/lib/cache.js b/lib/cache.js index be3c8e9..eaec17f 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -87,7 +87,7 @@ Cache.prototype._set = function _set(key, val, options) { }, options); } - return util.toPromise(val).then(writeToBackend); + return this._applyTimeout(util.toPromise(val).then(writeToBackend)); }; Cache.prototype.set = function set(rawKey, val, _opts, _cb) { @@ -98,8 +98,16 @@ Cache.prototype.set = function set(rawKey, val, _opts, _cb) { return this._set(key, val, optsWithDefaults).nodeify(args.cb); }; +Cache.prototype._applyTimeout = function _applyTimeout(value) { + var timeoutMs = this.defaults.timeout; + if (timeoutMs > 0) { + return value.timeout(timeoutMs); + } + return value; +}; + Cache.prototype._getWrapped = function _getWrapped(key) { - return this.backend.get(key); + return this._applyTimeout(this.backend.get(key)); }; // For backwards compatibility, eventually we should deprecate this. // It *should* be a private API. diff --git a/test/timeout.test.js b/test/timeout.test.js new file mode 100644 index 0000000..b8d9a91 --- /dev/null +++ b/test/timeout.test.js @@ -0,0 +1,80 @@ +import assert from 'assertive'; +import Bluebird from 'bluebird'; +import { identity } from 'lodash'; + +import Cache from '../lib/cache'; + +describe('Cache timeouts', () => { + const cache = new Cache({ + backend: { + get() { + return Bluebird.resolve({ d: 'get result' }).delay(150); + }, + set() { + return Bluebird.resolve('set result').delay(150); + }, + }, + name: 'awesome-name', + debug: true, + }); + + describe('with a timeout <150ms', () => { + before(() => cache.defaults.timeout = 50); + + it('get fails fast', async () => { + const err = await Bluebird.race([ + cache.get('my-key').then(null, identity), + Bluebird.delay(100, 'too slow'), // this should not be used + ]); + assert.expect(err instanceof Error); + assert.equal('TimeoutError', err.name); + }); + + it('set fails fast', async () => { + const err = await Bluebird.race([ + cache.set('my-key', 'my-value').then(null, identity), + Bluebird.delay(100, 'too slow'), // this should not be used + ]); + assert.expect(err instanceof Error); + assert.equal('TimeoutError', err.name); + }); + + it('getOrElse fails fast', async () => { + const value = await Bluebird.race([ + cache.getOrElse('my-key', 'my-value').then(null, identity), + // We need to add a bit of time here because we'll run into the + // timeout twice - once when trying to read and once while writing. + Bluebird.delay(150, 'too slow'), // this should not be used + ]); + assert.equal('my-value', value); + }); + }); + + describe('with a timeout >150ms', () => { + before(() => cache.defaults.timeout = 250); + + it('receives the value', async () => { + const value = await Bluebird.race([ + cache.get('my-key').then(null, identity), + Bluebird.delay(200, 'too slow'), // this should not be used + ]); + assert.equal('get result', value); + }); + + it('sets the value', async () => { + const value = await Bluebird.race([ + cache.set('my-key', 'my-value').then(null, identity), + Bluebird.delay(200, 'too slow'), // this should not be used + ]); + assert.equal('set result', value); + }); + + it('getOrElse can retrieve a value', async () => { + const value = await Bluebird.race([ + cache.getOrElse('my-key', 'my-value').then(null, identity), + Bluebird.delay(200, 'too slow'), // this should not be used + ]); + assert.equal('get result', value); + }); + }); +});