Skip to content
This repository has been archived by the owner on Jul 3, 2019. It is now read-only.

Commit

Permalink
feat(memoization): memoizers can be injected through opts.memoize (#90)
Browse files Browse the repository at this point in the history
Fixes: #76

BREAKING CHANGE: If you were passing an object to opts.memoize, it will now be used as an injected memoization object. If you were only passing booleans and other non-objects through that option, no changes are needed.
  • Loading branch information
zkat authored Apr 28, 2017
1 parent 0e55dc9 commit e5614c7
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 23 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,9 @@ If provided, cacache will memoize the given cache insertion in memory, bypassing
any filesystem checks for that key or digest in future cache fetches. Nothing
will be written to the in-memory cache unless this option is explicitly truthy.

There is no facility for limiting memory usage short of
[`cacache.clearMemoized()`](#clear-memoized), so be mindful of the sort of data
you ask to get memoized!
If `opts.memoize` is an object or a `Map`-like (that is, an object with `get`
and `set` methods), it will be written to instead of the global memoization
cache.

Reading from existing memoized data can be forced by explicitly passing
`memoize: false` to the reader functions, but their default will be to read from
Expand Down
19 changes: 10 additions & 9 deletions get.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ function getData (byDigest, cache, key, opts) {
opts = opts || {}
const memoized = (
byDigest
? memo.get.byDigest(cache, key)
: memo.get(cache, key)
? memo.get.byDigest(cache, key, opts)
: memo.get(cache, key, opts)
)
if (memoized && opts.memoize !== false) {
return BB.resolve(byDigest ? memoized : {
Expand All @@ -46,9 +46,9 @@ function getData (byDigest, cache, key, opts) {
integrity: entry.integrity
}).then(res => {
if (opts.memoize && byDigest) {
memo.put.byDigest(cache, key, res)
memo.put.byDigest(cache, key, res, opts)
} else if (opts.memoize) {
memo.put(cache, entry, res.data)
memo.put(cache, entry, res.data, opts)
}
return res
})
Expand All @@ -59,7 +59,7 @@ module.exports.stream = getStream
function getStream (cache, key, opts) {
opts = opts || {}
let stream = through()
const memoized = memo.get(cache, key)
const memoized = memo.get(cache, key, opts)
if (memoized && opts.memoize !== false) {
stream.on('newListener', function (ev, cb) {
ev === 'metadata' && cb(memoized.entry.metadata)
Expand All @@ -84,7 +84,7 @@ function getStream (cache, key, opts) {
memoLength += c.length
cb(null, c, en)
}, cb => {
memoData && memo.put(cache, entry, Buffer.concat(memoData, memoLength))
memoData && memo.put(cache, entry, Buffer.concat(memoData, memoLength), opts)
cb()
})
} else {
Expand All @@ -111,7 +111,7 @@ function getStream (cache, key, opts) {
module.exports.stream.byDigest = getStreamDigest
function getStreamDigest (cache, integrity, opts) {
opts = opts || {}
const memoized = memo.get.byDigest(cache, integrity)
const memoized = memo.get.byDigest(cache, integrity, opts)
if (memoized && opts.memoize !== false) {
const stream = through()
stream.write(memoized, () => stream.end())
Expand All @@ -129,7 +129,8 @@ function getStreamDigest (cache, integrity, opts) {
memoData && memo.put.byDigest(
cache,
integrity,
Buffer.concat(memoData, memoLength)
Buffer.concat(memoData, memoLength),
opts
)
cb()
})
Expand All @@ -142,7 +143,7 @@ function getStreamDigest (cache, integrity, opts) {
module.exports.info = info
function info (cache, key, opts) {
opts = opts || {}
const memoized = memo.get(cache, key)
const memoized = memo.get(cache, key, opts)
if (memoized && opts.memoize !== false) {
return BB.resolve(memoized.entry)
} else {
Expand Down
38 changes: 29 additions & 9 deletions lib/memoization.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,42 @@ function clearMemoized () {
}

module.exports.put = put
function put (cache, entry, data) {
MEMOIZED.set(`key:${cache}:${entry.key}`, { entry, data })
putDigest(cache, entry.integrity, data)
function put (cache, entry, data, opts) {
pickMem(opts).set(`key:${cache}:${entry.key}`, { entry, data })
putDigest(cache, entry.integrity, data, opts)
}

module.exports.put.byDigest = putDigest
function putDigest (cache, integrity, data) {
MEMOIZED.set(`digest:${cache}:${integrity}`, data)
function putDigest (cache, integrity, data, opts) {
pickMem(opts).set(`digest:${cache}:${integrity}`, data)
}

module.exports.get = get
function get (cache, key) {
return MEMOIZED.get(`key:${cache}:${key}`)
function get (cache, key, opts) {
return pickMem(opts).get(`key:${cache}:${key}`)
}

module.exports.get.byDigest = getDigest
function getDigest (cache, integrity) {
return MEMOIZED.get(`digest:${cache}:${integrity}`)
function getDigest (cache, integrity, opts) {
return pickMem(opts).get(`digest:${cache}:${integrity}`)
}

class ObjProxy {
constructor (obj) {
this.obj = obj
}
get (key) { return this.obj[key] }
set (key, val) { this.obj[key] = val }
}

function pickMem (opts) {
if (!opts || !opts.memoize) {
return MEMOIZED
} else if (opts.memoize.get && opts.memoize.set) {
return opts.memoize
} else if (typeof opts.memoize === 'object') {
return new ObjProxy(opts.memoize)
} else {
return MEMOIZED
}
}
4 changes: 2 additions & 2 deletions put.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function putData (cache, key, data, opts) {
opts.size = res.size
return index.insert(cache, key, res.integrity, opts).then(entry => {
if (opts.memoize) {
memo.put(cache, entry, data)
memo.put(cache, entry, data, opts)
}
return res.integrity
})
Expand Down Expand Up @@ -49,7 +49,7 @@ function putStream (cache, key, opts) {
opts.size = size
index.insert(cache, key, integrity, opts).then(entry => {
if (opts.memoize) {
memo.put(cache, entry, Buffer.concat(memoData, memoTotal))
memo.put(cache, entry, Buffer.concat(memoData, memoTotal), opts)
}
stream.emit('integrity', integrity)
cb()
Expand Down
93 changes: 93 additions & 0 deletions test/memoization.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,96 @@ test('can clear out the memoization cache', t => {
)
t.done()
})

test('accepts optional injected cache', t => {
memo.clearMemoized()
const MEMO = new Map()
memo.put(CACHE, ENTRY, DATA, {memoize: MEMO})
t.deepEqual(
memo.get(CACHE, ENTRY.key),
null,
'entry not in global memo cache'
)
t.deepEqual(
memo.get(CACHE, ENTRY.key, {memoize: MEMO}),
{entry: ENTRY, data: DATA},
'entry fetched from injected memoizer'
)
t.deepEqual(
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: MEMO}),
DATA,
'content entry fetched from injected memoizer'
)
t.deepEqual(
MEMO.get(`key:${CACHE}:${ENTRY.key}`),
{entry: ENTRY, data: DATA},
'entry is in the injected memoizer'
)
t.deepEqual(
MEMO.get(`digest:${CACHE}:${ENTRY.integrity}`),
DATA,
'content entry is in the injected memoizer'
)
MEMO.clear()
t.deepEqual(
memo.get(CACHE, ENTRY.key, {memoize: MEMO}),
null,
'tried to read from cleared memoizer'
)
t.deepEqual(
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: MEMO}),
null,
'tried to read by digest from cleared memoizer'
)
memo.put.byDigest(CACHE, ENTRY.integrity, DATA, {memoize: MEMO})
t.deepEqual(
MEMO.get(`digest:${CACHE}:${ENTRY.integrity}`),
DATA,
'content entry is in the injected memoizer'
)
const obj = {}
memo.put(CACHE, ENTRY, DATA, {memoize: obj})
t.deepEqual(
memo.get(CACHE, ENTRY.key, {memoize: obj}),
{entry: ENTRY, data: DATA},
'entry fetched from injected object memoizer'
)
t.deepEqual(
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: MEMO}),
DATA,
'content entry fetched from injected object memoizer'
)
memo.clearMemoized()
memo.put(CACHE, ENTRY, DATA, {memoize: 'foo'})
t.deepEqual(
memo.get(CACHE, ENTRY.key, {memoize: 'foo'}),
{entry: ENTRY, data: DATA},
'entry fetched from global memoization obj on non-obj option'
)
t.deepEqual(
memo.get(CACHE, ENTRY.key, {memoize: 'foo'}),
{entry: ENTRY, data: DATA},
'entry fetched from global memoization obj on non-obj option'
)
t.deepEqual(
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: 'foo'}),
DATA,
'content entry fetched global memoizer obj on non-obj option'
)
t.deepEqual(
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: 'foo'}),
DATA,
'content entry fetched global memoizer obj on non-obj option'
)
t.deepEqual(
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: false}),
DATA,
'content entry fetched global memoizer obj on non-obj option'
)
t.deepEqual(
memo.get.byDigest(CACHE, ENTRY.integrity, {memoize: false}),
DATA,
'content entry fetched global memoizer obj on non-obj option'
)
t.done()
})

0 comments on commit e5614c7

Please sign in to comment.