eager-cache
v2.0.0
Published
Cache that 'eagerly' fetches everything it intends to hold in a single request.
Downloads
16
Maintainers
Readme
eager-cache
eager-cache is a module that implements the lifecycle of a single object cache. It exposes an abstract class that can load and hold a single object or array (or any single value) from any source with support for invalidation of the cached value.
A common use for something like this is to fetch a table of configuration values from a database, and keep it in memory for quick reuse. If a change occurs in the configuration table, the cache can be invalidated, forcing the application to re-fetch the cached object.
Notes about this implementation:
- You must extend this class with a new class that implements the
async _load()
function. - Optionally, you can override the
async _invalidate()
function to perform any cleanup on the existing cache during invalidation. - Invalidation is not automatic - it must be called explicitly.
- If
async _load()
fails, pending promises will reject, but future calls toget()
will attempt to reload the cache. - Multiple attempts to get the contents of the cache while it's invalid, or loading, will not cause multiple load attempts. The calls will wait until the pending load is complete.
- eager-cache uses debug. All eager-cache instances will have a logger named
eager-cache:moniker
, where moniker is defined in the eager-cache constructor. eager-cache logging isn't intended to be very verbose, but it will log most state changes, for example, when invalidated.
Installation
npm install eager-cache
Usage
Here's an example of how I like to implement an eager-cache.
// configuration-cache.js
const { EagerCache } = require('eager-cache')
class ConfigurationCache extends EagerCache {
constructor() {
super({ moniker: 'ConfigurationCache' })
}
async _load() {
// Let's say this returns an object, but it can be whatever.
return await fetchConfigurations()
}
}
const cache = new ConfigurationCache()
// You could just export the cache here, but I prefer this method
// because destructuring class functions is a bad idea.
module.exports = {
getConfiguration: () => cache.get(),
invalidate: () => cache.invalidate()
}
And here's how I'd use it.
const { getConfiguration } = require('configuration-cache')
async function requestHandler(ctx) {
const configuration = await getConfiguration()
ctx.body = {
serviceName: configuration.serviceName
}
}
Other considerations
- In your
async _load()
implementation, consider manipulating the results of your asynchronous call into a data structure that's more convenient. For example, turn an array into an object, if you'll always be looking things up by a single key. - Overwrite
async _invalidate()
if you need to clean things up. For example, I have an implementation of this where I fetch several database connection configurations from a single table and instantiate them. If I invalidate the cache, and create all-new connections, I need to destroy the old connections.