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

@kokosro/ioredis-lock

v0.0.1

Published

A lightweight, distributed lock implementation for Node.js using Redis and ioredis. This package provides a simple way to implement distributed locks across multiple processes or servers.

Downloads

44

Readme

@kokosro/ioredis-lock

A lightweight, distributed lock implementation for Node.js using Redis and ioredis. This package provides a simple way to implement distributed locks across multiple processes or servers.

Features

  • 🔒 Distributed locking with Redis
  • 🔄 Automatic retry with configurable frequency
  • ⏱️ Configurable lock TTL and max wait time
  • 🎯 Type-safe with TypeScript
  • 🔧 Functional programming approach
  • 🪵 Optional verbose logging
  • 🎨 Customizable key namespacing

Installation

npm install @kokosro/ioredis-lock ioredis

Quick Start

import { createLockService } from "@kokosro/ioredis-lock";
import Redis from "ioredis";

// Create a lock service
const lockService = await createLockService({
  connection: new Redis(), // optional, creates new connection if not provided
  ttlSeconds: 10, // default: 10
  maxWaitMilliseconds: 10000, // default: 10000
  frequencyMilliseconds: 1000, // default: 1000
});

// Acquire and release a lock manually
const release = await lockService.acquireLock("my-resource");
try {
  // Do some work with exclusive access
  console.log("Lock acquired!");
} finally {
  await release();
}

API

createLockService(options)

Creates a new lock service instance.

Options

| Option | Type | Default | Description | | ----------------------- | ----------------------- | ------------- | ------------------------------------------------------------------ | | connection | Redis \| RedisOptions | new Redis() | ioredis connection instance or connection config object | | frequencyMilliseconds | number | 1000 | How often to retry acquiring the lock (in milliseconds) | | ttlSeconds | number | 10 | Time-to-live for the lock (in seconds) | | maxWaitMilliseconds | number | 10000 | Maximum time to wait for lock acquisition before throwing an error | | verbose | boolean | false | Enable verbose logging | | namespacePrefix | string | 'lock' | Prefix for all lock keys in Redis | | keySeparator | string | ':' | Separator between namespace and key |

RedisOptions (Connection Config)

When passing a connection config object, you can use any option from the ioredis RedisOptions. Here are the most commonly used options:

| Option | Type | Description | | ---------------------- | ------------------------------------------- | ------------------------------------------- | | host | string | Redis server host | | port | number | Redis server port | | username | string | Redis username (Redis 6+) | | password | string | Redis password | | db | number | Redis database number | | tls | boolean \| { key, cert, ca } | Enable TLS/SSL connection | | connectTimeout | number | Connection timeout in milliseconds | | keepAlive | number | TCP keep-alive interval in milliseconds | | family | 4 \| 6 | IP version (IPv4 or IPv6) | | retryStrategy | (times: number) => number \| void \| null | Custom retry strategy function | | maxRetriesPerRequest | number \| null | Max retries per request, null for unlimited | | enableOfflineQueue | boolean | Queue commands when connection is down | | lazyConnect | boolean | Don't auto-connect on instantiation | | connectionName | string | Connection name for debugging | | readOnly | boolean | Enable read-only mode |

Example with connection config:

const lockService = await createLockService({
  connection: {
    host: "redis.example.com",
    port: 6380,
    password: "your-password",
    tls: true,
    connectTimeout: 10000,
    retryStrategy: (times) => Math.min(times * 50, 2000),
  },
  ttlSeconds: 30,
});

Returns

A LockService object with the following methods:

lockService.acquireLock(key: string)

Acquires a lock for the given key. Waits until the lock is available or times out.

Parameters:

  • key (string): The key to lock

Returns: Promise<() => Promise<number>> - A release function that returns 1 if successfully released, 0 otherwise

Example:

const release = await lockService.acquireLock("user:123");
try {
  // Critical section
} finally {
  await release();
}

lockService.exclusiveSet<T>(key: string, fn: (value: T) => Promise<any>)

Acquires a lock, gets the current value from Redis, runs a function with that value, and sets the result back to Redis.

Parameters:

  • key (string): The Redis key to read and write
  • fn (function): A function that receives the current value and returns the new value

Returns: Promise<any> - The new value that was set

Example:

// Increment a counter atomically
await lockService.exclusiveSet("counter", async (currentValue) => {
  const current = parseInt(currentValue || "0");
  return (current + 1).toString();
});

lockService.exclusiveRun<T>(key: string, fn: () => Promise<T>)

Acquires a lock and runs a function with exclusive access. Useful when you want to run arbitrary code while holding a lock.

Parameters:

  • key (string): The lock key
  • fn (function): The function to run while holding the lock

Returns: Promise<T | undefined> - The result of the function

Example:

const result = await lockService.exclusiveRun("process-orders", async () => {
  // Process orders with exclusive access
  const orders = await getOrders();
  await processOrders(orders);
  return orders.length;
});

Use Cases

Preventing Concurrent Operations

// Ensure only one instance processes a user's data at a time
await lockService.exclusiveRun(`process-user:${userId}`, async () => {
  const userData = await fetchUserData(userId);
  await processUserData(userData);
  await saveUserData(userId, userData);
});

Atomic Counters

// Implement a distributed counter
const newCount = await lockService.exclusiveSet("page-views", async (value) => {
  return (parseInt(value || "0") + 1).toString();
});

Resource Pool Management

// Ensure only one process modifies a resource pool
const resource = await lockService.exclusiveRun("resource-pool", async () => {
  const pool = await getAvailableResources();
  const resource = pool.pop();
  await updateResourcePool(pool);
  return resource;
});

How It Works

The package uses Lua scripts to ensure atomic operations in Redis:

  1. Acquire Lock: Uses SET key value EX ttl NX pattern via Lua script to atomically set a lock with a TTL if it doesn't exist
  2. Release Lock: Verifies the lock value matches before deleting to ensure only the lock holder can release it
  3. Retry Logic: Automatically retries lock acquisition at the configured frequency until successful or timeout

Each lock is identified by a unique UUID, ensuring that only the process that acquired the lock can release it.

Error Handling

The lock service throws an error if:

  • Unable to acquire lock within maxWaitMilliseconds
  • User-provided function throws an error (in exclusiveSet and exclusiveRun)
try {
  await lockService.acquireLock("my-key");
} catch (error) {
  console.error("Failed to acquire lock:", error.message);
  // Handle timeout or other errors
}

Best Practices

  1. Always release locks: Use try-finally blocks to ensure locks are released
  2. Set appropriate TTL: Choose a TTL longer than your critical section execution time
  3. Handle timeouts: Be prepared for lock acquisition timeouts in high-contention scenarios
  4. Use descriptive keys: Name your locks clearly to avoid conflicts
  5. Monitor lock duration: Enable verbose mode during development to monitor lock timing

License

AGPL-3.0

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Repository

https://github.com/kokosro/ioredis-lock