Giter Club home page Giter Club logo

simple-redis-mutex's Introduction

Simple Redis Mutex

version Release Status Test Dev Status weekly downloads license

Implements mutex lock using redis as described in redis docs. The term simple is opposed to the more complex Redlock, that was also proposed by Redis in their docs for use in case of distributed redis instances.

This implementation of redis lock introduces some fine tuning features to the lock such as lock expire time, and acquire retry time, and acquire timeout (all described below).

Also acquiring the lock in a FIFO manner is supported by just providing a boolean option fifo: true, this will make the lock behave like a mutex queue and allow only one instance to acquire the lock at a time, and once the lock is released the first one to wait for it is the first one to acquire it and so on.

Install

Install the package using npm.

npm i simple-redis-mutex

Examples

const { lock } = require('simple-redis-mutex');
const Redis = require('ioredis');

// Connect to redis using ioredis
redis = new Redis(process.env.REDIS_URI);

async function someFunction() {
  // Acquire the lock, by passing redis client and the resource name (all settings are optional)
  const release = await lock(redis, 'resource-name');

  // Do some operations that require mutex lock
  await doSomeCriticalOperations();

  // Release the lock
  await release();
}

Usage

To acquire the lock you just call the lock function exported from the package, and pass to it ioredis client, and the resource name for the lock. You can also pass any optional options to fine-tune the lock as needed (see API below).

API

The package exports one named function lock, that acquires the lock and returns another function that releases the lock. The API for the lock function is as follows ...

lock(
  client,
  lockName,
  { retryTimeMillis = 100, timeoutMillis, failAfterMillis, fifo }
): Promise<Function>
  • client <ioredis client>: ioredis client.
  • lockName <String>: This is the name of the lock, and this is what distinguishes one lock from another, so that the part that needs mutual exclusion would always require a lock with the same name to be acquired by any process that attempts to enter that part. The key in redis database will be derived from this name.
  • retryTimeMillis <Number>: (default 100) This defines how much should a process wait before trying to acquire the same lock again, provided time is milliseconds, this time cannot be null.
  • timeoutMillis <Number>: (default null) This defines the expiry time of the lock after it's acquired, so after that expiry time another process can acquire the lock even if the current holder did not release it, time provided is in milliseconds, null timeout value means that the lock will never expire.
  • failAfterMillis <Number>: (default null) This defines the maximum time a process should wait for the lock until it can acquire it, when this time has passes and the process has not acquired the lock yet, the function will throw an Error saying that the lock could not be acquired in the given time, the provided time is in milliseconds, null value means that the function will not fail until it has acquired the lock.
  • fifo <Boolean>: (default false) If this is set the waiting instances will be acquire the lock in a FIFO manner, i.e. the first one to wait will be the first one to acquire the lock once it's released and so on.
  • Return type <Promise<Function>>: The unlock function, that is an async function, and should be called to release the lock.

Notes

  • This package has Peer Dependency on ioredis.
  • It's taken into account the case that process A acquires the lock, then it expires, then process B acquires the lock. When process A try to release the lock, it will not be released, as it's now acquired by B.
  • The same lock can be acquired with different options each time, so one time it can have an expiry time, and the next acquire it can lock indefinitely, the same with all the other options, although this behavior is not encouraged as it can be hard to debug.

simple-redis-mutex's People

Contributors

airs0urce avatar amrsaber avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

simple-redis-mutex's Issues

Could you add a cluster please?

Hi! ๐Ÿ‘‹

Firstly, thanks for your work on this project! ๐Ÿ™‚

Today I used patch-package to patch [email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/simple-redis-mutex/index.d.ts b/node_modules/simple-redis-mutex/index.d.ts
index ab22b7c..3c7e918 100644
--- a/node_modules/simple-redis-mutex/index.d.ts
+++ b/node_modules/simple-redis-mutex/index.d.ts
@@ -1,4 +1,4 @@
-import { Redis } from 'ioredis';
+import { Redis, Cluster } from 'ioredis';
 
 /**
  * Release the lock only if it has the same lockValue as acquireLock sets it.
@@ -36,7 +36,7 @@ interface acquireOptions {
  * @returns a promise that resolves with release function.
  */
 export function lock(
-  client: Redis,
+  client: Redis | Cluster,
   lockName: string,
   options: acquireOptions
 ): Promise<ReleaseFunction>;

This issue body was partially generated by patch-package.

lock continues to attempt even after fail timeout

Issue is when the failAfterMillis option is used to timeout a lock attempt, the module properly rejects the promise with the time out error. However the retry attempt timeout continues to fire so the library keeps trying to set the lock in redis, even though there is no client waiting on it anymore. This problem ended up putting orphan locks in redis.

Hacked around the problem with a manual fork by saving an attemptTimeoutId and clearing the timeout before the reject.

    // Set time out to fail acquiring the lock if it's sent
    if (failAfterMillis != null) {
      failTimeoutId = setTimeout(
        () => { 
          if (attemptTimeoutId != null) { clearTimeout(attemptTimeoutId); }   // ADDED
          reject(new Error(`Lock could not be acquire for ${failAfterMillis} millis`)); 
        }, failAfterMillis);
    }

Thanks
Chris

QUESTION: Acquiring multiple locks

Hi,
I'm not clever enough to figure this out, so I just wanted to check.

I am wanting to acquire a lock while updating multiple resources, so ideally I would want to get a lock for each resource. I am assuming that there will be 1 call to redis for each lock, so if I wanted to lock 500 resources, then that would be 500 calls to redis which would probably cause some problem. I guess there is no way to group calls to get several locks at once?

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.