diff --git a/package.json b/package.json index 82d9784..a377bc5 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,12 @@ "type": "git", "url": "git+https://github.com/blockai/cloud-cache.git" }, - "keywords": ["cache", "caching", "s3", "abstract-blob-store"], + "keywords": [ + "cache", + "caching", + "s3", + "abstract-blob-store" + ], "author": "Oli Lalonde (https://syskall.com)", "license": "MIT", "bugs": { @@ -40,6 +45,7 @@ "cz-conventional-changelog": "^1.2.0", "eslint-config-blockai": "^1.0.1", "fs-blob-store": "^5.2.1", + "mem": "^0.1.1", "nodemon": "^1.10.2", "randomstring": "^1.1.5", "request": "^2.74.0", @@ -62,4 +68,4 @@ "debug": "^2.2.0", "duplexify": "^3.4.5" } -} \ No newline at end of file +} diff --git a/src/index.js b/src/index.js index eefe48e..5e94335 100644 --- a/src/index.js +++ b/src/index.js @@ -95,13 +95,20 @@ export default (store, { .catch(reject) }) - const getOrSet = (key, fn, opts) => get(key) - .catch((err) => { - if (!(err instanceof KeyNotExistsError)) throw err - // evaluate function as promise - return Promise.resolve().then(() => fn()) - .then((value) => set(key, value, opts).then(() => value)) - }) + const getOrSet = (key, fn, opts = {}) => { + const doCacheMiss = () => Promise.resolve() + .then(() => fn()) + .then((value) => set(key, value, opts).then(() => value)) + + if (opts.refresh) return doCacheMiss() + + return get(key) + .catch((err) => { + if (!(err instanceof KeyNotExistsError)) throw err + // evaluate function as promise + return doCacheMiss() + }) + } // Streaming API const getStream = (key) => { @@ -133,7 +140,12 @@ export default (store, { const onError = (err) => { proxy.destroy(err) } - get(key, { stream: true }) + + const getKey = opts.refresh + ? () => Promise.reject(new KeyNotExistsError(key)) + : () => get(key, { stream: true }) + + getKey() .then((rs) => { proxy.setReadable(rs) }) diff --git a/test/promise.test.js b/test/promise.test.js index ad0189e..9cb6dd6 100644 --- a/test/promise.test.js +++ b/test/promise.test.js @@ -86,3 +86,19 @@ test('ttl works', (t) => { }) }, 300) }) + +test('cache.getOrSet (refresh works)', (t) => { + const getLeet = () => new Promise((resolve) => { + setTimeout(() => resolve(1337), 100) + }) + const getEleet = () => new Promise((resolve) => { + setTimeout(() => resolve(31337), 100) + }) + return cache.getOrSet('refresh', getLeet).then((val) => { + t.equal(val, 1337) + return cache.getOrSet('refresh', getEleet, { refresh: true }).then((val2) => { + t.equal(val2, 31337) + }) + }) +}) + diff --git a/test/stream.test.js b/test/stream.test.js index 72b7474..a797663 100644 --- a/test/stream.test.js +++ b/test/stream.test.js @@ -1,18 +1,26 @@ import test from 'blue-tape' import fs from 'fs' import concat from 'concat-stream' +import mem from 'mem' import getCloudCache from './utils' const cache = getCloudCache('stream') -const poemPath = `${__dirname}/files/poem.txt` -const poem = () => fs.createReadStream(poemPath) -const poemStr = fs.readFileSync(poemPath) +const file = mem((name) => { + const filepath = `${__dirname}/files/${name}` + const rs = () => fs.createReadStream(filepath) + const buf = fs.readFileSync(filepath) + return { path: filepath, rs, buf } +}) + +const poem = file('poem.txt') +const image = file('large.jpg') + // const devnull = () => fs.createWriteStream('/dev/null') test('cache.sets', (t) => { - poem().pipe(cache.sets('poem')).on('finish', () => { + poem.rs().pipe(cache.sets('poem')).on('finish', () => { t.end() }) }) @@ -21,8 +29,8 @@ test('cache.gets', (t) => { cache .gets('poem') .on('error', t.fail) - .pipe(concat((str) => { - t.equal(str.toString(), poemStr.toString()) + .pipe(concat((buf) => { + t.equal(buf.toString(), poem.buf.toString()) t.end() })) }) @@ -31,11 +39,11 @@ test('cache.getOrSets', (t) => { let callCount = 0 const getPoemStream = () => { callCount++ - return poem() + return poem.rs() } - const check = (str) => { - t.equal(str.toString(), poemStr.toString()) + const check = (buf) => { + t.equal(buf.toString(), poem.buf.toString()) t.equal(callCount, 1) } @@ -56,11 +64,11 @@ test('cache.getOrSets, pipe later', (t) => { let callCount = 0 const getPoemStream = () => { callCount++ - return poem() + return poem.rs() } const check = (str) => { - t.equal(str.toString(), poemStr.toString()) + t.equal(str.toString(), poem.buf.toString()) t.equal(callCount, 1) } @@ -79,3 +87,33 @@ test('cache.getOrSets, pipe later', (t) => { })) }, 300) }) + +test('cache.getOrSets, refresh=true', (t) => { + const check = ({ buf }, buf2) => { + t.ok(Buffer.compare(buf, buf2) === 0) + } + + const refresh = () => { + cache.getOrSets('refresh', image.rs, { refresh: true }) + .on('error', t.fail) + .pipe(concat((buf) => { + check(image, buf) + })) + .on('finish', () => { + cache.gets('refresh').pipe(concat((buf2) => { + check(image, buf2) + t.end() + })) + }) + } + + cache.getOrSets('refresh', poem.rs) + .on('error', t.fail) + .on('data', () => {}) // make sure poem.rs is consumed + .on('end', () => { + cache.gets('refresh').pipe(concat((buf) => { + check(poem, buf) + refresh() + })) + }) +})