npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

node-stash

v0.4.0

Published

Distributed cache using Redis and in-memory LRU

Downloads

504

Readme

stash

Travis Dependencies Join the chat at https://gitter.im/gosquared/stash

NPM

Distributed cache using Redis as cache store and memory lru for fast access to loaded data.

Requires Redis >= v2.6.12 and node-redis client ^0.8

Install

npm install node-stash

Usage


var Stash = require('node-stash');
var Redis = require('redis');
var stash = Stash.createStash(redis.createClient, {
  lru: {
    max: 100000,          // max number of cached results
    maxAge: 600000,       // max age of cached results
    errTTL: 5000,         // max age of cached error results
    timeout: 5000         // min time before callback queue reset
  }
});

var fetchRow = function(id, cb) {
  var fetch = function(done) {
    /**
     * This is our function to fetch data from a resource.
     * The results of this function are cached by stash.
     *
     * It is wise to set a timeout on your fetch, so that
     * it calls `done` with an error if it takes too long
     * to complete. This is useful in case your external
     * resource is overloaded and being slow. If configured,
     * stash will cache this error and prevent overloading the
     * resource with more fetches.
     */
    // Example: querying a mysql db
    mysql.query({
      sql: 'SELECT thing FROM things WHERE id = ? LIMIT 1',
      timeout: 5000
    }, [id], done);
  };

  var key = 'appName:component:'+id;
  stash.get(key, fetch, cb);
};

// get a row
fetchRow(1, function(err, row) {
  // called async with cached or
  // freshly fetched data
});

API

Create an instance of stash:

var Stash = require('node-stash');
var stash = Stash.createStash(createRedisClient, { /* options... optional :) */ });

createRedisClient must be a function that creates and returns an instance of node-redis compatible with v0.8. This way your app can still have control over instantiating and handling errors on the redis client.

Do not simply return an existing redis client, as this will be placed into pub-sub mode. Stash needs to be able to create distinct redis clients for its pub-sub and non-pub sub Redis functionality.

stash.get(key, fetchFn, cb)

Retrieve a key. If the key has been previously retrieved, and not older than the LRU maxAge, it will be loaded from memory, resulting in extremely fast access. If not, it will attempt to load the key from Redis. If the key is not in Redis, it will invoke fetchFn, which is a function you define to retrieve the value. Stash caches the result of fetchFn in Redis.

Stash is designed to run in a distributed architecture. As such, you might have multiple instances of an app running stash, with many processes attempting to retrieve values from the database and cache. Stash has in-built concurrency control around your fetchFn, which ensures that only one instance of your app will be able to execute fetchFn at a time. This helps prevent stampeding your data source when keys expire from the cache.

If the fetchFn invokes its callback with an error, you can configure a different ttl for cached error results. See the lru.errTTL option.

fetchFn signature: function (cb) {}

Example fetchFn:

var fetch = function(cb) {
  mysql.query('SELECT something FROM somewhere', cb);
};

When multiple calls to stash.get are made before a key is retrieved and cached, stash will transparently queue callbacks until the first single fetch & cache operation is completed, at which point the callbacks in the queue are invoked with the result. In this way, Stash abstracts concurrency complexity, so you only need to worry about a single function call to retrieve a value for your distributed app.

stash.del(key, [cb])

Remove a value from the memory and Redis cache. Once this is complete, the value will need to be fetched.

Note that the key is only deleted from the memory cache of the current process, so if you're running many distributed instances of stash, their memory caches might still contain the key. Use stash.invalidate to perform cluster-wide purges.

stash.clear()

Empty the memory cache for all keys. Does not touch Redis cache.

stash.invalidate(key, [cb])

Invalidate a key from the cache. This is similar to stash.del, but goes one step further and ensures that all other instances of stash in other processes also delete the key from their memory caches. An invalidation is a cluster-wide purge.

Options


// example handlers
var debug = console.log;
var metrics = {
  increment: function(name, inc) {
    // name is the counter name, inc is the increment number
  }
};

var opts = {
  redis: {
    wait: true,           // when false, errors if redis connection is down, otherwise queues commands
    ttl: {
      cache: 600000,      // redis cache key ttl
      lock: 10000         // in-case unlock does not work, ttl of redis-based fetchFn lock
    },
    clients: {
      // Clients passed here will be used instead of createRedisClient()
      // This is useful if you want to share redis clients across
      // many instances of stash. It is recommended to create clients
      // solely for use with stash instances.
      cache: null,
      broadcast: null // This client is placed in pub/sub mode.
      // It is recommended to call client.setMaxListeners(0) on this client if
      // you intend to share it across more than 10 instances of stash.
    }
  },
  timeout: {
    retry: 1000           // optimistic lock retry delay
  },
  lru: {
    max: 100000,          // max number of cached results
    maxAge: 600000,       // max age of cached results
    errTTL: 5000,         // max age of cached error results
    timeout: 5000         // min time before callback queue reset
  },
  retryLimit: 5,          // max retry times for optimistic locking
  log: debug,             // set to your own logging handler
  metrics: metrics        // set to your own metrics handler
};