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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@justin-kucerak/redlock

v1.0.0

Published

A TypeScript implementation of the Redlock algorithm for distributed locking with Redis.

Downloads

7

Readme

Redlock - Distributed Locking with Redis in TypeScript

A TypeScript implementation of the Redlock algorithm for distributed locking using Redis. This package provides a flexible, client-agnostic locking mechanism suitable for synchronization across multiple processes or services.

Table of Contents

Features

  • Distributed Locking: Implements the Redlock algorithm for safe distributed locking across multiple Redis instances.
  • Client-Agnostic: Works with any Redis client library that implements the provided RedisClient interface.
  • Event-Driven: Emits events for lock acquisition, release, extension, and errors, allowing for custom handling and logging.
  • TypeScript Support: Fully typed for enhanced development experience and code safety.

Installation

Install the package via npm:

npm install @justin-kucerak/redlock

You'll also need to install a Redis client library. The package supports ioredis and node-redis via provided adapters.

For ioredis:

npm install ioredis

For node-redis:

npm install redis

Note: The Redis client libraries are listed as peer dependencies. You must install one of them separately.

Usage

Quick Start

Here's a simple example of how to use the Redlock class with ioredis:

import { Redlock, IORedisAdapter } from '@justin-kucerak/redlock';
import Redis from 'ioredis';

// Create Redis clients
const redisClients = [
  new Redis({ host: 'localhost', port: 6379 }),
  new Redis({ host: 'localhost', port: 6380 }),
  new Redis({ host: 'localhost', port: 6381 }),
];

// Wrap clients with adapters
const clients = redisClients.map((client) => new IORedisAdapter(client));

// Create Redlock instance
const redlock = new Redlock(clients);

async function doWork() {
  const resource = 'my-resource';
  const ttl = 10000; // 10 seconds

  let lockId: string | null = null;

  try {
    // Acquire the lock
    lockId = await redlock.acquire(resource, ttl);
    console.log('Lock acquired:', lockId);

    // Perform your critical section code here

    // Optionally extend the lock if needed
    await redlock.extend(resource, lockId, ttl);
    console.log('Lock extended');

    // Release the lock
    await redlock.release(resource, lockId);
    console.log('Lock released');
  } catch (error) {
    console.error('Error:', error);
  } finally {
    // Ensure the lock is released if it was acquired
    if (lockId) {
      try {
        await redlock.release(resource, lockId);
      } catch (releaseError) {
        console.error('Error releasing lock in finally block:', releaseError);
      }
    }

    // Close Redis clients
    redisClients.forEach((client) => client.disconnect());
  }
}

doWork();

Event Handling

The Redlock class extends EventEmitter and emits events during its operation. You can attach listeners to these events for logging or custom handling.

redlock.on('error', (error) => {
  console.error('Redlock error:', error);
});

redlock.on('lockAcquired', ({ resource, lockId }) => {
  console.log(`Lock acquired on resource ${resource} with ID ${lockId}`);
});

redlock.on('lockReleased', ({ resource, lockId }) => {
  console.log(`Lock released on resource ${resource} with ID ${lockId}`);
});

redlock.on('lockExtended', ({ resource, lockId, ttl }) => {
  console.log(`Lock on resource ${resource} extended with ID ${lockId} for ${ttl}ms`);
});

API Documentation

Class: Redlock

The Redlock class provides methods to acquire, release, and extend locks on resources using the Redlock algorithm.

Constructor

constructor(clients: RedisClient[], options?: RedlockOptions)
  • Parameters:
    • clients (RedisClient[]): An array of Redis clients implementing the RedisClient interface.
    • options (RedlockOptions, optional):
      • retryCount (number, optional): Number of times to retry acquiring the lock (default: 3).
      • retryDelay (number, optional): Delay between retries in milliseconds (default: 200).

Methods

acquire(resource, ttl)

Acquires a distributed lock on the specified resource.

async acquire(resource: string, ttl: number): Promise<string>
  • Parameters:
    • resource (string): The resource key to lock.
    • ttl (number): The time-to-live of the lock in milliseconds.
  • Returns:
    • Promise<string>: The unique lock ID if the lock is acquired.
  • Throws:
    • Error if the lock cannot be acquired after the maximum retries.

release(resource, lockId)

Releases the distributed lock on the specified resource.

async release(resource: string, lockId: string): Promise<void>
  • Parameters:
    • resource (string): The resource key to unlock.
    • lockId (string): The unique lock ID returned by acquire.
  • Throws:
    • Error if the lock cannot be released.

extend(resource, lockId, ttl)

Extends the duration of an existing lock.

async extend(resource: string, lockId: string, ttl: number): Promise<void>
  • Parameters:
    • resource (string): The resource key whose lock duration is to be extended.
    • lockId (string): The unique lock ID returned by acquire.
    • ttl (number): The new time-to-live for the lock in milliseconds.
  • Throws:
    • Error if the lock cannot be extended.

Events

The Redlock class emits the following events:

  • error: Emitted when an error occurs.

    • Listener Parameters: (error: Error)
  • lockAcquired: Emitted when a lock is successfully acquired.

    • Listener Parameters: ({ resource: string, lockId: string, validityTime: number })
  • lockReleased: Emitted when a lock is successfully released.

    • Listener Parameters: ({ resource: string, lockId: string })
  • lockExtended: Emitted when a lock is successfully extended.

    • Listener Parameters: ({ resource: string, lockId: string, ttl: number })
  • lockError: Emitted when an error occurs during lock acquisition on a client.

    • Listener Parameters: ({ client: RedisClient, error: Error })
  • unlockError: Emitted when an error occurs during lock release on a client.

    • Listener Parameters: ({ client: RedisClient, error: Error })
  • extendError: Emitted when an error occurs during lock extension on a client.

    • Listener Parameters: ({ client: RedisClient, error: Error })
  • attemptFailed: Emitted when a lock acquisition attempt fails but retries are remaining.

    • Listener Parameters: ({ resource: string, lockId: string, attempts: number })

Implementing Redis Clients

To use the Redlock class, you need to provide Redis clients that implement the RedisClient interface.

export interface RedisClient {
  set(
    key: string,
    value: string,
    options?: {
      nx?: boolean;
      px?: number; // Expiration in milliseconds
    }
  ): Promise<'OK' | null>;

  eval(
    script: string,
    keys: string[],
    args: (string | number)[]
  ): Promise<number>;

  disconnect(): void;
}

Using ioredis

First, install ioredis as a dependency:

npm install ioredis

Then, use the IORedisAdapter provided by the package:

import { Redlock, IORedisAdapter } from '@justin-kucerak/redlock';
import Redis from 'ioredis';

// Create Redis clients
const redisClients = [
  new Redis({ host: 'localhost', port: 6379 }),
  // ... other clients
];

// Wrap Redis clients with IORedisAdapter
const clients = redisClients.map((client) => new IORedisAdapter(client));

// Create Redlock instance
const redlock = new Redlock(clients);

Using node-redis

First, install redis as a dependency:

npm install redis

Then, use the NodeRedisAdapter provided by the package:

import { Redlock, NodeRedisAdapter } from '@justin-kucerak/redlock';
import { createClient } from 'redis';

// Create Redis clients
const redisClients = [
  createClient({ url: 'redis://localhost:6379' }),
  // ... other clients
];

// Ensure clients are connected
await Promise.all(redisClients.map((client) => client.connect()));

// Wrap Redis clients with NodeRedisAdapter
const clients = redisClients.map((client) => new NodeRedisAdapter(client));

// Create Redlock instance
const redlock = new Redlock(clients);

Testing

The package includes unit tests to ensure correct functionality.

Unit Tests

Unit tests are written using Jest and can be run with:

npm run test

These tests use mock implementations of the RedisClient interface to simulate Redis behavior.

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository and create a new branch for your feature or bug fix.
  2. Write tests to cover your changes.
  3. Submit a pull request with a detailed description of your changes.

For major changes, please open an issue first to discuss what you would like to change.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by the Redlock algorithm as described by Redis.
  • Thanks to the contributors of ioredis and node-redis for their excellent Redis client libraries.
  • Hat tip to all developers who have contributed to similar distributed locking mechanisms.