{"id":46157,"library":"redis-semaphore","title":"redis-semaphore","description":"Redis-based distributed mutex (lock) and semaphore implementations for Node.js applications. Current stable version 5.7.0. Actively maintained with regular releases. Key differentiators: fully atomic operations using LUA scripts (fail-safe), built-in lock auto-refresh, optional lock-loss detection, and a simple API. Compared to alternatives like 'redlock', redis-semaphore offers more fine-grained control over lock timeout, acquire attempts, and refresh intervals, plus explicit lost-lock handling. Works exclusively with ioredis client. Ships TypeScript types.","status":"active","version":"5.7.0","language":"javascript","source_language":"en","source_url":"ssh://git@github.com/swarthy/redis-semaphore","tags":["javascript","redis","redlock","mutex","semaphore","typescript"],"install":[{"cmd":"npm install redis-semaphore","lang":"bash","label":"npm"},{"cmd":"yarn add redis-semaphore","lang":"bash","label":"yarn"},{"cmd":"pnpm add redis-semaphore","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Required peer dependency – redis client used to interact with Redis server","package":"ioredis","optional":false}],"imports":[{"note":"ESM vs CJS: the package supports both, but ESM is recommended. For CJS, use `const { Mutex } = require('redis-semaphore')`","wrong":"const Mutex = require('redis-semaphore').Mutex","symbol":"Mutex","correct":"import { Mutex } from 'redis-semaphore'"},{"note":"Named export, not a default export. Use destructured import.","wrong":"import * as redisSemaphore from 'redis-semaphore'; const Semaphore = redisSemaphore.Semaphore","symbol":"Semaphore","correct":"import { Semaphore } from 'redis-semaphore'"},{"note":"TypeScript type for options passed to Mutex constructor. Only relevant with TypeScript.","wrong":"","symbol":"LockOptions","correct":"import { LockOptions } from 'redis-semaphore'"},{"note":"Correct destructuring avoids odd capitalization.","wrong":"const redis = require('redis-semaphore'); new redis.Mutex(...)","symbol":"Mutex (CommonJS)","correct":"const { Mutex } = require('redis-semaphore')"},{"note":"Constructor expects an ioredis-compatible client. Specify the correct type if using custom subclass.","wrong":"","symbol":"Redis.Type (for custom client)","correct":"import { Redis as IORedis } from 'ioredis'; client: IORedis"}],"quickstart":{"code":"import Redis from 'ioredis';\nimport { Mutex } from 'redis-semaphore';\n\nasync function main() {\n  const redisClient = new Redis();\n  const mutex = new Mutex(redisClient, 'resource-key', {\n    lockTimeout: 5000,\n    acquireTimeout: 10000,\n    acquireAttemptsLimit: 50,\n    retryInterval: 100,\n    refreshInterval: 4000,\n  });\n\n  await mutex.acquire();\n  try {\n    console.log('Lock acquired, doing critical work...');\n    // Simulate work\n    await new Promise(resolve => setTimeout(resolve, 2000));\n  } finally {\n    await mutex.release();\n    console.log('Released lock.');\n  }\n  redisClient.quit();\n}\n\nmain().catch(console.error);","lang":"typescript","description":"Demonstrates creating a Mutex instance, acquiring the lock, executing critical code, and releasing the lock with error handling."},"warnings":[{"fix":"Pass explicit lockTimeout (e.g., 0 for no expiry, though not recommended) to preserve old behavior.","message":"Version 5.0.0 changed the default lockTimeout from Infinity to 10000 ms. If your code relied on infinite timeout, lock will expire after 10 seconds.","severity":"breaking","affected_versions":">=5.0.0"},{"fix":"Upgrade Node.js to v14.17.0 or later.","message":"Version 4.0.0 dropped Node.js 10 support. Minimum Node version is now 14.17.0.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"Use `acquireAttemptsLimit: Infinity` or `Number.POSITIVE_INFINITY` as needed.","message":"The `acquireAttemptsLimit` option default changed from Infinity to Number.POSITIVE_INFINITY. No functional change, but the option may be renamed in a future major version.","severity":"deprecated","affected_versions":">=5.0.0"},{"fix":"Understand that release is idempotent and safe to call unconditionally.","message":"The `mutex.release()` call, even if lock no longer belongs to the current mutex, has no effect. It is safe to always call release.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Use unique identifiers (e.g., from crypto.randomUUID()) unless you explicitly need to share the lock.","message":"When using `acquiredExternally` option with a custom `identifier`, ensure the identifier is unique across parallel executors. Sharing the same identifier may treat multiple locks as the same holder.","severity":"gotcha","affected_versions":">=5.0.0"},{"fix":"Provide an onLockLost callback to avoid unhandled errors, or catch LostLockError globally.","message":"The `onLockLost` callback defaults to throwing an unhandled LostLockError. If not handled, this can crash the process.","severity":"gotcha","affected_versions":">=5.0.0"}],"env_vars":null,"search_vec":"'5.7.0':19 'acquir':69 'activ':20 'altern':54 'api':51 'applic':15 'atom':28 'attempt':70 'auto':41 'auto-refresh':40 'base':6 'built':37 'built-in':36 'client':84 'compar':52 'control':65 'current':16 'detect':47 'differenti':26 'distribut':7 'exclus':81 'explicit':75 'fail':34 'fail-saf':33 'fine':63 'fine-grain':62 'fulli':27 'grain':64 'handl':79 'implement':12 'interv':73 'ioredi':83 'javascript':88 'key':25 'like':55 'lock':9,39,45,67,78 'lock-loss':44 'loss':46 'lost':77 'lost-lock':76 'lua':31 'maintain':21 'mutex':8,91 'node.js':14 'offer':60 'oper':29 'option':43 'plus':74 'redi':2,5,58,89 'redis-bas':4 'redis-semaphor':1,57 'redlock':56,90 'refresh':42,72 'regular':23 'releas':24 'safe':35 'script':32 'semaphor':3,11,59,92 'ship':85 'simpl':50 'stabl':17 'timeout':68 'type':87 'typescript':86,93 'use':30 'version':18 'work':80","created_at":"2026-06-07T12:58:34.517772+00:00","updated_at":"2026-06-07T12:58:34.517772+00:00","problems":[{"fix":"Ensure lockTimeout is a number, e.g., `{ lockTimeout: 10000 }`.","cause":"Passing a non-number value (e.g., string) to lockTimeout option.","error":"Error: The 'lockTimeout' option must be a number at new Mutex"},{"fix":"Install and use `ioredis` as the client: `npm install ioredis` and pass an instance.","cause":"Using a Redis client that is not ioredis-compatible (e.g., redis package v4+).","error":"TypeError: redisClient.subscribe is not a function"},{"fix":"Either handle the error via onLockLost callback, or avoid long-running critical sections that exceed lockTimeout.","cause":"The lock was expired or removed externally (e.g., by another process or due to network issues) and the refresh cycle detected it.","error":"LostLockError: Mutex lock lost: 'resource-key'"}],"ecosystem":"npm","meta_description":null,"install_score":null,"quickstart_score":null,"quickstart_tag":null,"pypi_latest":null,"cli_name":null,"cli_version":null,"type":"library","homepage":"https://github.com/swarthy/redis-semaphore#readme","github":"ssh://git@github.com/swarthy/redis-semaphore","docs":null,"changelog":null,"pypi":null,"npm":"redis-semaphore","openapi_spec":null,"status_page":null,"smithery":null,"categories":["database"],"base_url":null,"auth_type":null,"provenance":{"verified_status":null,"verified_at":null,"last_verified":"2026-06-07","next_check":"2026-09-05","install_tag":null}}