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

redis-locking-promise-memoizer

v0.3.1

Published

A promise based asynchronous function memoizer for node.js, using redis as the memo store.

Downloads

251

Readme

redis-locking-promise-memoize Build Status

===

Heavily inspired by redis-memoizer, but with some key differences.

  • Promise based. Expects the function to return a promise, and the memoized function returns a promise. Uses Q internally.
  • Rather than trying to generate redis keys based on the input function (which can collide for identical functions in different contexts), the caller must explicitly provide a base value for keys to be generated based on.
  • Locking. This library doesn't try to mitigate cache stampedes (which can be addressed by combining this library with an in-memory memoizer), instead focusing on reducing the calls to the original function across all process instances by introducing locking at the redis level.

A promise based asynchronous function memoizer for node.js, using redis as the memo store. Memos expire after a specified timeout. Great as a drop-in performance optimization / caching layer for heavy asynchronous functions.

Wikipedia explains it best:

...memoization is an optimization technique used primarily to speed up computer programs by having function calls avoid repeating the calculation of results for previously processed inputs.

var memoize = require("redis-locking-promise-memoizer")();

function someExpensiveOperation(arg1, arg2) {
	// later...
	return Q.promise();
}

// only do that expensive thing once per minute
var memoized = memoize(someExpensiveOperation, 'some key', 60 * 1000);

Now, calls to memoized will have the same effect as calling someExpensiveOperation, except it will be much faster. The results of the first call are stored in redis and then looked up for subsequent calls.

Redis effectively serves as a shared network-available cache for function calls. Thus, the memoization cache is available across processes, so that if the same function call is made from different processes they will reuse the cache.

Uses

Lets say you are making a DB call that's rather expensive. Let's say you've wrapped the call into a getUserProfile function that looks as follows:

function getUserProfile(userId) {
	// Go over to the DB, perform expensive call, get user's profile
	return Q.resolve(userProfile);
}

Let's say this call takes 500ms, which is unacceptably high, and you want to make it faster, and don't care about the fact that the value of userProfile might be slightly outdated (until the cache timeout is hit in redis). You could simply do the following:

// only check for user changes once per hour
var getMemoizedUserProfile = memoize(getUserProfile, 'user profile', 60 * 60 * 1000);

getMemoizedUserProfile("user1").then(function(userProfile) {
	// First call. This will take some time.

	getMemoizedUserProfile("user1").then(function(userProfile) {
		// Second call. This will be blazingly fast.
	});
});

This can similarly be used for any network or disk bound async calls where you are tolerant of slightly outdated values.

Usage

Initialization

var memoize = require("redis-locking-promise-memoizer")(redisPort, redisHost, redisOptions);

Initializes the module with redis' connection parameters. The params are passed along as-is to the node-redis module for connecting to redis.

memoize(fn, key, timeout)

Memoizes a promise returning function and returns it.

  • fn must be a function that returns a promise (if it returns a value synchronously, the memoized version will return a promise that resolves to that value).

  • key is the unique id for this memoized function. You can memoize the same function into two memoized functions by changing this key, or make two difference functions share a cache by setting this to the same value.

  • timeout is the amount of time in milliseconds for which the result of the function call should be cached in redis. Once the timeout is hit, the value is deleted from redis automatically. This is done using the redis psetex command. The timeout is only set the first time, so the value expires after the timeout time has expired since the first call. The timeout is not reset with every call to the memoized function. Once the value has expired in redis, this module will treat the function call as though it's called the first time again. timeout can alternatively be a function, if you want to dynamically determine the cache time based on the data returned. The returned data will be passed into the timeout function.

    var httpCallMemoized = memoize(makeHttpCall, function(res) {
    	// return a number based on say response's expires header
    });
    
    httpCallMemoized(function(res) { ... });

Cache Stampedes

Rather than protect against redis cache stampedes, as redis-memoizer does, this module uses locking to ensure that only one instance of the memoized function is called across all instances of your program. An in-memory memoizer is recommended to reduce the load on redis.

In-Memory/Redis memoizing combo example:

var localMemoize = require('memoizee');
var redisMemoize = require('redis-locking-promise-memoizer');

var memoize = function (fn, key, ttl) {
    return localMemoize(redisMemoize(fn, key, ttl), { maxAge: ttl });
};

Installation

Use npm to install redis-locking-promise=memoizer:

npm install redis-locking-promise-memoizer

To run the tests, install the dev-dependencies by cd'ing into node_modules/redis-locking-promise-memoizer and running npm install once, and then grunt test.

License

(The MIT License)

Copyright (c) 2014 Patrick Williams [email protected]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.