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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@redis-x/locker

v0.3.1

Published

Distributed locking library for Redis.

Readme

@redis-x/locker

npm version Open on npmx.dev license

A powerful and flexible distributed locking library for Redis with TypeScript support.

Features

  • 🔒 Robust Locking: Reliable distributed locking mechanism built on Redis
  • ✨ Flexible: Support for both single-resource and multi-resource locks
  • ⏱️ Configurable TTL: Set custom timeouts for your locks
  • 🔄 Lock Extension: Extend lock duration without releasing
  • 🔁 Retry Logic: Configurable retry mechanism for lock acquisition
  • 🔍 Type Safety: Written in TypeScript with full type definitions

Installation

bun i @redis-x/locker redis
# or with pnpm
pnpm add @redis-x/locker redis
# or with npm
npm install @redis-x/locker redis

Requirements:

  • Node.js 16 or newer;
  • redis@^4.6;
  • (optional) @redis-x/client;
  • Redis server 7.4 or newer.

Basic Usage

First, set up a Redis client:

import { createClient } from 'redis';

const redisClient = createClient();
await redisClient.connect();

Lockers accept either a plain redis client or a RedisXClient from @redis-x/client package. If you already use RedisXClient in your project, pass it to the constructor to avoid duplicating RedisXClient instances.

Single Resource Locking

When you need to lock a single resource (like a user account during update).

import { RedisXLocker } from '@redis-x/locker';

// Create a locker for a specific resource
const locker = new RedisXLocker(redisClient, 'user:123', {
  ttl: 5000,            // Lock expires after 5 seconds
  retry_interval: 100,  // Wait 100ms between retry attempts
  retry_count: 5        // Try 5 times before giving up
});

let lock;
try {
  lock = await locker.lock();

  // Critical section - update user data
  await updateUserData(123);
} catch (error) {
  console.error('Operation failed:', error);
} finally {
  // Clean up the lock regardless of success or failure
  lock?.release().catch(console.error);
}

Options

Both RedisXLocker and RedisXMultiLocker accept the same options:

| Option | Default | Description | | ---------------- | ------: | --------------------------------------------------------------- | | ttl | 5000 | Lock lifetime in milliseconds. Can be overridden in lock(). | | retry_interval | 100 | Milliseconds to wait between attempts. | | retry_count | 10 | Maximum number of attempts, including the first one. |

Invalid values throw a RangeError.

Multi-Resource Locking

For operations that require multiple resources to be locked simultaneously (like transferring money between accounts). Either all IDs are locked or none: if any of them is already locked, the attempt fails and is retried.

lock(ids) accepts an array or Set with at least one unique ID. There is no fixed upper limit; the practical one depends on your runtime. Numbers are converted to strings, so 1 and '1' are the same ID.

import { RedisXMultiLocker } from '@redis-x/locker';

// Create a multi-locker with a namespace
const multiLocker = new RedisXMultiLocker(redisClient, 'banking');

let lock;
try {
  // Lock multiple accounts for a money transfer
  lock = await multiLocker.lock(['account:456', 'account:789']);

  // Critical section - both accounts are locked
  await transferMoney(456, 789, 100);
} catch (error) {
  console.error('Transfer failed:', error);
} finally {
  // Release all locks when done, even if an error occurred
  lock?.release().catch(console.error);
}

Single-lock keys and multi-lock namespaces share the same Redis keys, so don't use one name for both.

Custom TTL for Specific Operations

You can override the default TTL for each lock operation:

// Quick operation with short lock
const quickLock = await locker.lock(1000); // 1 second TTL

// Long operation with extended lock
const longLock = await multiLocker.lock(['account:456'], 30000); // 30 second TTL

Extending a Lock

Sometimes your operation takes longer than expected. Instead of releasing and re-acquiring the lock, extend it. extend() adds time to what the lock has left:

let lock;
try {
  lock = await locker.lock();

  // Start long-running task
  const isCompleted = await startLongTask();

  // If task isn't done yet, extend the lock
  if (!isCompleted) {
    await lock.extend(5000); // Add 5 more seconds
    await finishLongTask();
  }
} catch (error) {
  console.error('Operation failed:', error);
} finally {
  // Always clean up
  lock?.release().catch(console.error);
}

Handling Lock Errors

  • RedisXLockerAcquireError is thrown by lock() when every attempt failed because the resource is locked, and by extend() when the lock has expired or belongs to someone else.
  • RedisXLockerReleaseError is thrown by release() when the lock has expired or belongs to someone else. Call release(true) to skip it: a lock that belongs to someone else stays untouched either way.
  • RangeError is thrown for an invalid TTL, retry option or number of IDs, before anything is sent to Redis.
import { RedisXLockerAcquireError } from '@redis-x/locker';

try {
  const lock = await locker.lock();
  // ...
} catch (error) {
  if (error instanceof RedisXLockerAcquireError) {
    console.log('Resource is currently locked by another process');
  }
}

You can ignore RedisXLockerReleaseError by calling lock?.release(true). However, if you encounter RedisXLockerReleaseError, it means the lock has expired before task completion. You should consider setting lock lifetime to a longer duration to avoid issues.