redis-cache-decorator
!NPM versionnpm-imagenpm-url
!Build statustravis-imagetravis-url
!Test coveragecodecov-imagecodecov-url
!Dependency Statusdavid-imagedavid-url
!Licenselicense-imagelicense-url
!Downloadsdownloads-imagedownloads-urlA decorator to cache your functions. Features:
- Uses Redis caching, expiration, and pub/sub.
- Concurrency locking - if the function is being run elsewhere with the same arguments, it will wait for the result of that function instead of executing again.
- Caching - caches results for as long as you want. If you set
ttl=0
, then you're just this library for concurrency locking, which is completely fine. - Timeouts - throws when executing or waiting for a function call takes too long
- Only tested with ioredis
Use Cases:
- Race conditions
- API calls with rate limits
- Expensive database calls
- Expensive function calls
Example
Here's a function that caches all your queries.const assert = require('assert')
const Redis = require('ioredis')
const pg = require('pg-then')
const pool = pg.Pool(process.env.POSTGRES_URI)
const CreateCacheDecorator = require('redis-cache-decorator')({
client: Redis.createClient(),
subscriber: Redis.createClient()
})
const fn = CreateCacheDecorator({
namespace: 'crazy-database-call',
})((query, values) => {
return db.query(query).then(result => result.rows)
})
fn(`
SELECT *
FROM users
WHERE id = $1
`, [
1
]).then(users => {
assert(Array.isArray(users))
})
API
const CreateCacheDecorator = require('redis-cache-decorator')(options)
Creates a constructor with the following options:client <required>
- a redis client for GET/SET/PUBLISH, etc.subscriber <required>
- a redis client forPSUBSCIRBE
namespace = ''
- a prefix for all the eventsencoding = 'json'
- how data is encoded between redis and node.js.
json
- the default
- string
- buffer
ttl = '30s'
- the TTL expiration in seconds.timeout = '30s'
- how long to wait for the function to execute.pollFactor = 1 / 10
- the fraction of the timeout to poll.
30s
timeout with a 1 / 10
factor means that redis is polled for new changes every 3 seconds.minimumPollInterval = '100ms'
- the minimum frequency of polling so you don't end up spamming rediscreateTimeoutError = () => <Error>{ message: 'Timed out!', code: 'RCDTIMEDOUT' }
- the function called to create a timeout error.
if (err.code === 'RCDTIMEDOUT')
.onError = err => console.error(err.stack)
- an error handler for redis network errors.disabled = false
- disable this decorator, specifically useful for testing.
const decorate = CreateCacheDecorator(options)
Create a decorator with a set of options.namespace <required>
- a namespace for this decoratorpollInterval
- by default, calculated fromtimeout
,pollFactor
, andminimumPollInterval
, but you can set this yourself.ttl
timeout
pollFactor
minimumPollInterval
createTimeoutError
onError
const decoratedFunction = decorate(fn)
Decorates the function. The decorated function will have the same API as the original function.- The function should return a value or a
Promise
that can beJSON.stringify()
d. - The function can be synchronous or asynchronous.
this
is not supported.
this
within the function.
The primary reason is that it's difficult to decide how to cache.const job = decoratedFunction(...args)
Execute the decorated function. Will always return aPromise
that resolves to the value.
In addition, the promise will have the following properties:.hash
- hash of the arguments for this job.
job.then(value => {}, err => {})
Resolve the promise to retrieve the results of the job.const hash = decoratedFunction.createHash(...args)
Create the hash string for the specified arguments.decoratedFunction.set(hash, value).then(value => {})
Manually set the value of a hash.Streams
If a stream is returned, the values will automatically be buffered.encoding='json'
- the stream will be concatenated as an object stream -> array.
encoding='string'
- the stream will be buffered into a string.encoding='buffer'
- the stream will be buffered into aBuffer()
instance.