Fork me on GitHub

Magazine

Documentation redux.

Try not to be too ugly.

Notes:

These notes should be expanded upon and then linked to from the API documentation so I don't have to expound on wider concepts in the API documentaion.

new Cache

Create a new empty cache. There are no public constructor parameters.

cache.count

The count of entries in the cache.

cache.heft

The sum of the heft of each entry in the cache. The heft is the relative size of the entry value in relation to other entry values in the cache. It is an optional property and only meaningful if you've set the heft property of individual entries.

entry = cache.hold(key)

Obtain a reference to an entry in the cache. The method will return an Entry if one exists for the given key or null if no entry exists.

The key can be any JSON serializable object. Keys are compared using their serialized values and not by object identity. You can use any JSON serializable JavaScript type. Any objects in the key the same set of key/value pairs are equivalent regardless of insertion order. This insertion order independent comparison is obtained by serializing the key using Keyify.

If an entry is returned it must be released using release() or removed using remove(). Failure to do so will result in memory leaks when the cache is unable to evict the entry because it is permenantly referenced.

subCache = cache.cache()

Create a sub-cache that shares the memory pool of this cache but has a different set of keys and an independent least-recently used list.

This allows you share a pool of memory accross sub-systems in your program, but have different eviction strategies for different sub-sytems.

It also allows you to audit reference counting if a sub-system shuts down as part of normal operation before program shutdown — you can assert that the cache for that sub-system is has no referenced entries by evicting down to zero using shrink().

The parent cache and sub-cache have separate key spaces. Entries added to the sub-cache are inaccessible to through parent cache. If you add a key to the sub-cache for which there is an entry in the parent cache you will create a new separate entry in the sub-cache that is only accessible in the sub-cache.

And vice-versa. If you add a key to the parent cache for which there is an entry in the sub-cache you will create new separate entry in the parent cache that is only accessible the parent cache.

The parent cache eviction methods will iterate over both the parent and child cache entries. The child cache eviction methods will iterate only over the subset of entries that where added through the child's hold() method.

entry = cache.hold(key, initialValue)

Obtain a reference to an existing entry in the cache for the given key or create a new entry for the key using the given initialValue if an entry does not already exist. This get or set behavior is how we resolve race conditions when populating the cache.

entry = cache.hold(key, initializer, heft)

Obtain a reference to an existing entry in the cache for the given key or create a new entry for the key using the given initialValue and heft if an entry does not already exist. This get or set behavior is how we resolve race conditions when populating the cache.

The heft is the relative size of the entry value in relation to other entry values in the cache.

cache.shrink(count)

Make a best effort attempt to shrink the cache.count down to the given count.

The shrink() method will iterate through the least-recently used cache entries from least-recently used to most-recently used removing entries until the desired count is obtained or the first referenced entry is encountered.

Because we stop when we encounter the first referenced entry, it is important to release entries as soon as you're done with them. If your program holds onto an entry indefinately and it does not make any other calls to hold the reference it will eventually become both the least-recently used entry and referenced. TODO Expound on this and move it up above the API documentation.

cache.purge(heft)

Make a best effort attempt to reduce the cache.heft down to the given heft.

The shrink() method will iterate through the least-recently used cache entries from least-recently used to most-recently used removing entries until the desired heft is obtained or the first referenced entry is encountered.

The sum of the heft of each entry in the cache. The heft is the relative size of the entry value in relation to other entry values in the cache. It is an optional property and only meaningful if you've set the heft property of individual entries.

cache.every(({ value, heft }) => {})

While there is no way to iterate over keys or entries, nor should there be, we do provide this dirty little secret for the purposes of asserting the validity of your cache.

every accepts a function that receives and object with a value and heft parameter for every entry entry in the cache. This is not the entry itself. Calling release() or remove() is not necessary and indeed, not possible.

You should not use this function to gather up values and you absolutely must not call cache.hold() from within the callback function. Calling cache.hold() will change the least-recently used ordering and likely result in an endless loop.

const iterator = cache.evictable()
for (const entry of cache.evictable()) {
    if (<done condition>) {
        entry.release()
        break
    }
    entry.remove()
}

Returns a custom eviction strategy iterator. The iterator will iterate through the cache entries from the least-recently used entry to the most-recently used entry stopping ...

Probably better to just have least(). That's all we're doing here and it is more explicit.

cache.reduce((accumulator, { value, heft }) => {}, initialValue)

While there is no way to iterate over keys or entries, nor should there be, we do provide this dirty little secret for the purposes of asserting the validity of your cache.

reduce accepts a function that receives an accumulator and an object with a value and heft parameter for every entry entry in the cache. This is not the entry itself. Calling release() or remove() is not necessary and indeed, not possible. The return value of the callback function is given to the next invocation of the callback function. The initial value of the accumulator is specified with the initialValue argument. Unlink JavaScript's Array.reduce(), The initialValue argument is required.

You should not use this function to gather up values and you absolutely must not call cache.hold() from within the callback function. Calling cache.hold() will change the least-recently used ordering and likely result in an endless loop.