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

stores

v1.0.0

Published

Fast, reliable and smart caching system heavily based on streams.

Downloads

586

Readme

stores

Build Status

stores is a fast, reliable, smart caching system heavily based on streams.

stores deals transparently with cache stampede or cache miss storm for you. Basically, it ensures that only one request fetches the resource to be cached. Other ones are put in a queue and served ASAP, when the resource has been fetched or cached.

stores aims to be a solid caching facility that allows you to easily implement your custom backing store. That said, it comes with a balanced filesystem store and an express middleware.

Installation

npm install stores --save --production

Usage

You're in hurry, I can understand that. Here is an example on how you can cache processed images easily:

app.get(/\.(?:jpg|png|gif)$/, stores('file', function(req, slot, next) {
	processImage(req).stream().pipe(slot);
});

This code ensures that you only process a given image once, and serve the cached version to all others. It implicitly use the FileStore to cache the image to the filesystem. slot represents a stream pointing to a cache bucket that the FileStore has automatically created.

stores(store, [options], [fetch])

This provided middleware allows you to configure a store globally.

store

This mandatory argument can be:

  • a string that defines which type of store you want to use (for now only file).
  • an existing Store instance.
  • a constructor that inherits from Store.
stores('file');
stores(new FileStore());
stores(FileStore);

Note that you can register custom stores and then use the string flavor:

stores.MyPreciousStore = MyPreciousStore;
stores('myPrecious');

options

Optional, options will configure the store. All stores accepts those values:

  • maxPending: (default: 100), defines how many requests can be enqueued while a resource is being cached (see [hit hot]).
  • writesRetries: (default: 3), defines how many times the store will try to write to a cache bucket that fails (see [sealed buckets].
stores('file', { maxPending: Infinity, writesRetries: 0 });

Note that each implementation can add additional values for their specific needs (i.e. [file store options]).

fetch(req, slot, next)

Optional, this callback allows the store to fetch missing data and store it. It is called on a cache miss, with the following arguments:

  • req: the network request.
  • slot: a Writable stream that will persist data to cache. It is allocated by the store. You must pipe to it.
  • next: next middleware in the stack. It's useful if something went wrong and you want to abort the request asap.
stores('file', function(req, slot, next) {
	var stream = createSomeStream(req);
	stream.pipe(slot);
});

Note that if you don't specify a fetch callback here, then you must specify it via Store#fetch or Store#get.

Store object

As seen previously, you can instanciate a store by yourself for some additional flexibility. As a user, you only are interested in the public api.

Store([options])

Creates a store with the given options.

var store = new FileStore({ writesRetries: 0 });

Store#fetch(fetch)

This tells the store how to fetch a missing resource from the cache. If you haven't specified it via the stores middleware, you still can do it here globally.

var store = new FileStore();
store.fetch(function(req, slot, next) {
	var stream = createSomeStream(req);
	stream.pipe(slot);
});

The main advantage of specifying it this way, is that you can change the fetch method whenever you want during the lifecycle of your application.

Store#get(req, res, next, [fetch])

This is the most important method. It will try to fetch a resource from the cache or call fetch callback. It can be useful if for some reason, you prefer using stores in one of your existing route of middleware, or when you want to add additional logic before using it.

var store = new FileStore();

app.get(/\.(?:jpg|png|gif)$/, function(req, res, next) {
	// don't process/cache tuhmbnails
	if (~req.url.indexOf('thumb')) {
		fs.createReadStream(path.join(root, req.url)).pipe(res);
		return;
	}

	// process others
	store.get(req, res, next, function(req, slot, next) {
		processImage(req).stream().pipe(slot);
	});
});

Note that if you already have specified a fetch method globally, you can then omit the fetch argument:

app.get(/\.(?:jpg|png|gif)$/, function(req, res, next) {
	store.get(req, res, next);
});

Available stores

FileStore

As you may already guess, it uses the filesystem as storage medium. It creates a balanced directory structure which ensures performances are always the best, even if your cache is growing fast.

Basically each cache entry is associated with a strong hash (sha256). This hash is used to create the path to the cache file. This path is composed of multiple subdirectories in order to ensure there is not more that 256 entries in each directory.

FileStore([options])

Creates a filesystem store with the given options. Available options are common ones plus the following ones:

  • root: (default: current directory), root directory of the cache structure.
  • depth: (default: 4), number of subdirectories less 1.
var store = new FileStore({ root: '/var/cache/www', depth: 2});

Custom stores

To implement a custom store, you have to inherit from the Store object.

This object provides two methods, _get and _lock that are respectively needed to fetch the original resource and to lock a new cache bucket. A cache bucket can be seen as the physical location where your cached resource will be stored. It can be a file, a memory chunk, a REDIS key, a S3 bucket, or whatever you want.

Store object

Graceful filesystem streams

You also need be aware that stores uses [graceful-fs-stream] (gfs) as dependency. gfs slightly changes the behavior of fs.createReadStream and fs.createWriteStream by opening / creating the underlying file on first read or write. The main advantage is that instead of throwing an error, those function will emit an error event instead.

If you do still want to use standard versions in your project, use fs._createReadStream or fs._createWriteStream.

Author

| twitter/ngryman | |---| | Nicolas Gryman |