autocache

Caching system that automatically populates itself

Stats

stars 🌟issues ⚠️updated 🛠created 🐣size 🏋️‍♀️
151Sep 10, 2015Feb 17, 2015Minified + gzip package size for autocache in KB

Readme

Autocache

Travis Status

TL;DR memorisation back by a persistent store.

Instead of caching single keys and values, autocache allows you to define a setter and when a request for that key is placed, it will run the setter live, caching the result and returning it.

Importantly, the autocache can, and should be used with a persistent store so long as the adapter implements the storage api.

Note that by default, the cache is stored in memory (which kinda isn't the point), so when you restart, the cache will be lost.

Usage

Autocache can be used either on the server or on the client (again, recommended with a persistent adapter).

General usage:

  • Define a storage proceedure against a key
  • Get the key value
  • Clear/invalidate values

Note that autocache is a singleton, so we only need to set the store once.

var redisAC = require('autocache-redis');
var cache = require('autocache')({ store: redisAC });

cache.define('testStatus', function (done) {
  // call an imaginary test status API
  http.request('/test-status').then(function (result) {
    done(null, result.status);
  }).catch(function (error) {
    done(error);
  });
});

...now in another script elsewhere:

var cache = require('autocache');

app.get('/status', function (req, res) {
  cache.get('testStatus', function (error, status) {
    if (error) {
      return res.send(error);
    }

    res.render(status === 'pass' ? 'test-passing' : 'test-fail');
  });
});

// every 10 minutes, clear the cache
// note: we could also do this using the object notation and the TTL property
setInterval(function () {
  cache.clear('testStatus');
}, 10 * 60 * 1000);

Important notes

  • If an update is taking a long time, this does not block .get requests. The gets will be served with the old value until the update has completed and commited a new value.
  • Calls to .clear will queue (and thus block) gets until the clear is complete, after which the .get requests are automatically flushed.

Adapters

Current adapters:

Please do contribute your own adapters - missing: mongodb, memcache, sqlite..?

Methods

cache.define(string, function)

For a particular string key, set a function that will return a cached value.

Note that the function can be synchronous or asynchronous. If your code accepts a done function, you can pass the value you wish to cache to the done function argument (as seen in the usage example above).

cache.define(options)

As above, but with extended options:

{
  name: "string",
  update: function () {},
  ttl: 1000, // time to live (ms)
  ttr: 1000, // time to refresh (ms)
}

TTL will auto expire (and clear) the entry based on the ttl milliseconds since the last time it was accessed.

Note that if ttr is present, ttl will be ignored.

cache.get(string, function)

If a cached value is available for string it will call your function with an error first, then the result.

If there is no cached value, autocache will run the definition, cache the value and then call your function.

If multiple calls are made to get under the same string value, and the value hasn't been cached yet, the calls will queue up until a cached value has been returned, after which all the queued functions will be called.

cache.get(string, [fn args], function)

Cache getting also supports arguments to your definition function. This is only supported on async definitions.

For example:

cache.define('location', function (name, done) {
  xhr.get('/location-lookup/' + name).then(function (result) {
    done(null, result);
  }).catch(function (error) {
    done(error);
  });
});

// this will miss the cache, and run the definition
cache.get('location', 'brighton', function (error, data) {
  // does something with data
});

// this will ALSO miss the cache
cache.get('location', 'berlin', function (error, data) {
  // does something with data
});

// this will hit the cache
cache.get('location', 'berlin', function (error, data) {
  // does something with data
});

In the above example, once the cache is called with the argument brighton, the name and argument are now the unique key to the cache.

cache.update(string, function)

Calls the definition for the string, caches it internally, and calls your function with and error and the result.

cache.clear([string])

Clear all (with no arguments) or a single cached entry.

cache.destroy([string])

Destroy the all definitions (with no arguments) or a single definition entry.

cache.configure({ store: adapter })

Set and store the storage adapter for persistent storage. See notes on adapters.

cache.reset()

Clear all of the internal state of the cache, except for the storage adapter.

Storage API

If you want to write your own adapter for persistent storage you must implement the following functions:

get(key<string>, callback<function>) // get single
set(key<string>, value<string>, callback<function>) // set single
destroy([key<string>], callback<function>) // delete single
clear(callback<function>) // delete all
dock(autocache<Autocache>) // passes copy of active cache
toString() // returns a string representation of your store (for debugging)

See the adapters for examples of code.

Notes:

  1. Callbacks must pass an error first object, then the value. The value should be undefined if not found.
  2. Callbacks are expected to be asynchronous (but are acceptable as synchronous).
  3. clear should only clear objects created by the cache (which can be identified by a prefix).
  4. Calling the adapter function should accept the autocache as an argument, example below.
  5. Autocache will handle converting user objects to and from JSON, so the adapter will always be storing a string.
  6. dock is called with the autocache instance passed in, if the store is already connected, you should call autocache.emit('connect') immediately.

Important once your adapter has been attached, it should emit a connect event:

// this tells autocache that we're reading to start caching
autocache.emit('connect');

Automatically setting the autocache store

When the adapter is required, the user must be able to pass the autocache object into your adapter. This call will set the autocache's store to your adapter.

Below is the code from the localStorage adapter. It returns the store if called, but also checks if the autocache was passed in, and if it was, calls the configure function to assign the store as itself:

function LocalStore(autocache) {
  if (autocache) {
    autocache.configure({ store: new LocalStore() });
    return LocalStore;
  }
}

TODO

  • Test prefix support

License

MIT

If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.