Read-through, fail-through, Redis-backed caching
- Node.js 4.x (LTS) or newer
- Redis 2.8 or newer
For local development, consider Docker for managing your Redis installation. First install the image:
docker run -d --name redis -p 6379:6379 redis:2.8
Then, run the image:
docker start redis
Install with npm:
npm install --save orchard
const Orchard = require('orchard');
const orchard = new Orchard();
orchard(key, () => value).then(console.log);
This module exports a constructor. Instantiate an Orchard cache with desired options
:
const Orchard = require('orchard');
const options = {...};
const orchard = new Orchard(options);
Option | Type | Description | Default |
---|---|---|---|
herdCacheParams |
Object |
Parameters passed to the internal, in-memory LRU cache | { max: 1024, maxAge: 60000 } |
keyHash |
Function |
Hashing function applied to resolved keys (prior to prefixing) | An identity function |
prefix |
String |
Prepended to resolved cache keys; use if a single database supports multiple caching services | None |
scanCount |
Number |
Hints Redis' SCAN command during keyspace enumeration (e.g. with delete) |
10 |
ttl |
String or Number |
Sets expiration for a cached value, a Number is treated as milliseconds and a String is treated as an ms duration |
None |
url |
String |
Redis URI with required authentication | redis://localhost:6379 |
Notes:
herdCacheParams
: To mitigate thundering herd risks, Orchard holds akey
in memory while initially resolving the priming value; subsequent calls using the samekey
(within the same process) receive the samePromise
until the value is resolved. This temporary cache is pruned to avoid unnecessary memory consumption.keyHash
: This function is invoked with the resolved key and should resolve to (or return) a deterministically hashed digest of the input. For example:function keyHash(key) { return makeDigest(key); }
. By providing a hashing function at instantiation, applications can avoid hashing sensitive keys at every Orchard call-site.ttl
: Redis uses seconds for expiration timers; Orchard convertsttl
from milliseconds to seconds, rounding down.
Retrieve data from the Orchard cache, invoking the priming function to prime the cache.
const Orchard = requirefastr'orchard');
const orchard = new Orchard(options);
orchard(key, () => Promise.resolve(value)).then(console.log);
Returns a Promise
that resolves to the final value of the primingFunction
. Internally, Orchard attempts to get the current value of key
from the database. If the key does not exist or the database is not available, the primingFunction
is invoked with the resolved key
(in "raw" form, without the prefix
and without the keyHash
function applied) and the final value is set in the database for future use.
key
can be any one of:
- a
String
- a
Promise
that resolves to aString
- a
Function
that returns aString
- a
Function
that returns aPromise
that resolves to aString
primingFunction
can be any one of:
- a
Function
that returns a JSON-stringify-ableObject
- a
Function
that returns aPromise
that resolves to a JSON-stringify-ableObject
The Promise
is rejected if there is an error resolving the primingFunction
. Rejected cache calls are not persisted.
Warning: The key
resolution process does not have the same stampede protections as the priming function. Instead, if key
is a Function
, it is invoked on every cache call. If you plan to remotely resolve the key you may want to consider caching the key
function as well.
Option | Type | Description | Default |
---|---|---|---|
force |
Boolean |
If true , ignore current cached value and overwrite with new value |
false |
Immediately delete one or more keys and associated data from the Orchard cache.
const Orchard = require('orchard');
const orchard = new Orchard(options);
orchard.del(key).then(console.log);
Returns a Promise
that resolves to the number of removed keys. If the last character of the resolved key
is an asterisk (*
– e.g. items:*
), the key is treated as a pattern to use with Redis' SCAN
and MATCH
commands.
Listen to events emitted by Orchard.
cache.on('cache:hit', console.log);
The cache instance is also an event emitter. eventName
is a String
corresponding to a supported event and eventListener
is a callback function invoked each time the event is triggered.
{
requestId: <String:UUID>,
key: <String>,
ms: <Number:Integer:Milliseconds>
}
ms
is milliseconds elapsed between invocation and resolution of the cached value.
{
requestId: <String:UUID>,
key: <String>,
ms: <Number:Integer:Milliseconds>
}
ms
is milliseconds elapsed between invocation and resolution of the priming value before writing to the database.
<Error>{
code: <String>,
errno: <String>,
syscall: <String>
}
No event data
To run the full test suite, including unit tests and linting:
npm test
To run the only the unit test suite:
npm run test-unit
This project maintains 100% coverage of statements, branches, and functions. To determine unit test coverage:
npm run coverage
To run the integrated test suit against a live Redis database (redis://localhost:6379
):
npm run test-integrated
Warning: The integrated test suite deletes all keys prefixed with __ORCHARD_INTEGRATED_TEST
.
This module is instrumented with debug to provide information about behavior during development. All debug invocations use the orchard
prefix for targeted debug output:
DEBUG=orchard* npm test
PRs are welcome! PRs must pass unit tests and linting prior to merge. For bugs, please include a failing test which passes when your PR is applied. To enable a git hook that runs npm test
prior to pushing, cd
into your repo and run:
touch .git/hooks/pre-push
chmod +x .git/hooks/pre-push
echo "npm test" > .git/hooks/pre-push
This project follows semantic versioning. See the change log for release information.
- This module is licensed under the MIT License
- Redis is licensed under the "Three Clause" BSD License