Have ideas to improve npm?Join in the discussion! »

    redis-semaphore
    TypeScript icon, indicating that this package has built-in type declarations

    4.1.0 • Public • Published

    redis-semaphore

    NPM version Build status Dependency Status FOSSA Status Coverage Status Maintainability Known Vulnerabilities FOSSA Status

    Mutex and Semaphore implementations based on Redis ready for distributed systems

    Features

    • Fail-safe (all actions performed by LUA scripts (atomic))

    Usage

    Installation

    npm install --save redis-semaphore ioredis
    # or
    yarn add redis-semaphore ioredis

    Mutex

    See RedisLabs: Locks with timeouts

    new Mutex(redisClient, key [, { lockTimeout = 10000, acquireTimeout = 10000, retryInterval = 10, refreshInterval = lockTimeout * 0.8 }])
    • redisClient - required, configured redis client
    • key - required, key for locking resource (final key in redis: mutex:<key>)
    • options - optional
      • lockTimeout - optional ms, time after mutex will be auto released (expired)
      • acquireTimeout - optional ms, max timeout for .acquire() call
      • retryInterval - optional ms, time between acquire attempts if resource locked
      • refreshInterval - optional ms, auto-refresh interval; to disable auto-refresh behaviour set 0
      • onLockLost - optional function, called when lock loss is detected due refresh cycle; default onLockLost throws unhandled LostLockError

    Example

    const Mutex = require('redis-semaphore').Mutex
    const Redis = require('ioredis')
    
    // TypeScript
    // import { Mutex } from 'redis-semaphore'
    // import Redis from 'ioredis'
    
    const redisClient = new Redis()
    
    async function doSomething() {
      const mutex = new Mutex(redisClient, 'lockingResource')
      await mutex.acquire()
      try {
        // critical code
      } finally {
        await mutex.release()
      }
    }

    Example with lost lock handling

    async function doSomething() {
      const mutex = new Mutex(redisClient, 'lockingResource', {
        // By default onLockLost throws unhandled LostLockError
        onLockLost(err) {
          console.error(err)
        }
      })
      await mutex.acquire()
      try {
        while (mutex.isAcquired) {
          // critical cycle iteration
        }
      } finally {
        // It's safe to always call release, because if lock is no longer belongs to this mutex, .release() will have no effect
        await mutex.release()
      }
    }

    Semaphore

    See RedisLabs: Basic counting sempahore

    This implementation is slightly different from the algorithm described in the book, but the main idea has not changed.

    zrank check replaced with zcard, so now it is fair as RedisLabs: Fair semaphore (see tests).

    In edge cases (node time difference is greater than lockTimeout) both algorithms are not fair due cleanup stage (removing expired members from sorted set), so FairSemaphore API has been removed (it's safe to replace it with Semaphore).

    Most reliable way to use: lockTimeout is greater than possible node clock differences, refreshInterval is not 0 and is less enough than lockTimeout (by default is lockTimeout * 0.8)

    new Semaphore(redisClient, key, maxCount [, { lockTimeout = 10000, acquireTimeout = 10000, retryInterval = 10, refreshInterval = lockTimeout * 0.8 }])
    • redisClient - required, configured redis client
    • key - required, key for locking resource (final key in redis: semaphore:<key>)
    • maxCount - required, maximum simultaneously resource usage count
    • options optional See Mutex options

    Example

    const Semaphore = require('redis-semaphore').Semaphore
    const Redis = require('ioredis')
    
    // TypeScript
    // import { Semaphore } from 'redis-semaphore'
    // import Redis from 'ioredis'
    
    const redisClient = new Redis()
    
    async function doSomething() {
      const semaphore = new Semaphore(redisClient, 'lockingResource', 5)
      await semaphore.acquire()
      try {
        // maximum 5 simultaneously executions
      } finally {
        await semaphore.release()
      }
    }

    MultiSemaphore

    Same as Semaphore with one difference - MultiSemaphore will try to acquire multiple permits instead of one.

    MultiSemaphore and Semaphore shares same key namespace and can be used together (see test/src/RedisMultiSemaphore.test.ts).

    new MultiSemaphore(redisClient, key, maxCount, permits [, { lockTimeout = 10000, acquireTimeout = 10000, retryInterval = 10, refreshInterval = lockTimeout * 0.8 }])
    • redisClient - required, configured redis client
    • key - required, key for locking resource (final key in redis: semaphore:<key>)
    • maxCount - required, maximum simultaneously resource usage count
    • permits - required, number of acquiring permits
    • options optional See Mutex options

    Example

    const MultiSemaphore = require('redis-semaphore').MultiSemaphore
    const Redis = require('ioredis')
    
    // TypeScript
    // import { MultiSemaphore } from 'redis-semaphore'
    // import Redis from 'ioredis'
    
    const redisClient = new Redis()
    
    async function doSomething() {
      const semaphore = new MultiSemaphore(redisClient, 'lockingResource', 5, 2)
    
      await semaphore.acquire()
      try {
        // make 2 parallel calls to remote service which allow only 5 simultaneously calls
      } finally {
        await semaphore.release()
      }
    }

    RedlockMutex

    Distributed Mutex version

    See The Redlock algorithm

    new RedlockMutex(redisClients, key [, { lockTimeout = 10000, acquireTimeout = 10000, retryInterval = 10, refreshInterval = lockTimeout * 0.8 }])
    • redisClients - required, array of configured redis client connected to independent nodes
    • key - required, key for locking resource (final key in redis: mutex:<key>)
    • options optional See Mutex options

    Example

    const RedlockMutex = require('redis-semaphore').RedlockMutex
    const Redis = require('ioredis')
    
    // TypeScript
    // import { RedlockMutex } from 'redis-semaphore'
    // import Redis from 'ioredis'
    
    const redisClients = [
      new Redis('127.0.0.1:6377'),
      new Redis('127.0.0.1:6378'),
      new Redis('127.0.0.1:6379')
    ] // or cluster.nodes('master')
    
    async function doSomething() {
      const mutex = new RedlockMutex(redisClients, 'lockingResource')
      await mutex.acquire()
      try {
        // critical code
      } finally {
        await mutex.release()
      }
    }

    RedlockSemaphore

    Distributed Semaphore version

    See The Redlock algorithm

    new RedlockSemaphore(redisClients, key, maxCount [, { lockTimeout = 10000, acquireTimeout = 10000, retryInterval = 10, refreshInterval = lockTimeout * 0.8 }])
    • redisClients - required, array of configured redis client connected to independent nodes
    • key - required, key for locking resource (final key in redis: semaphore:<key>)
    • maxCount - required, maximum simultaneously resource usage count
    • options optional See Mutex options

    Example

    const RedlockSemaphore = require('redis-semaphore').RedlockSemaphore
    const Redis = require('ioredis')
    
    // TypeScript
    // import { RedlockSemaphore } from 'redis-semaphore'
    // import Redis from 'ioredis'
    
    const redisClients = [
      new Redis('127.0.0.1:6377'),
      new Redis('127.0.0.1:6378'),
      new Redis('127.0.0.1:6379')
    ] // or cluster.nodes('master')
    
    async function doSomething() {
      const semaphore = new Semaphore(redisClients, 'lockingResource', 5)
      await semaphore.acquire()
      try {
        // maximum 5 simultaneously executions
      } finally {
        await semaphore.release()
      }
    }

    RedlockMultiSemaphore

    Distributed MultiSemaphore version

    See The Redlock algorithm

    new RedlockMultiSemaphore(redisClients, key, maxCount, permits [, { lockTimeout = 10000, acquireTimeout = 10000, retryInterval = 10, refreshInterval = lockTimeout * 0.8 }])
    • redisClients - required, array of configured redis client connected to independent nodes
    • key - required, key for locking resource (final key in redis: semaphore:<key>)
    • maxCount - required, maximum simultaneously resource usage count
    • permits - required, number of acquiring permits
    • options optional See Mutex options

    Example

    const RedlockMultiSemaphore = require('redis-semaphore').RedlockMultiSemaphore
    const Redis = require('ioredis')
    
    // TypeScript
    // import { RedlockMultiSemaphore } from 'redis-semaphore'
    // import Redis from 'ioredis'
    
    const redisClients = [
      new Redis('127.0.0.1:6377'),
      new Redis('127.0.0.1:6378'),
      new Redis('127.0.0.1:6379')
    ] // or cluster.nodes('master')
    
    async function doSomething() {
      const semaphore = new RedlockMultiSemaphore(
        redisClients,
        'lockingResource',
        5,
        2
      )
    
      await semaphore.acquire()
      try {
        // make 2 parallel calls to remote service which allow only 5 simultaneously calls
      } finally {
        await semaphore.release()
      }
    }

    License

    MIT

    FOSSA Status

    Install

    npm i redis-semaphore

    DownloadsWeekly Downloads

    4,855

    Version

    4.1.0

    License

    MIT

    Unpacked Size

    127 kB

    Total Files

    157

    Last publish

    Collaborators

    • avatar