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

race-lock-js

v0.0.4

Published

In-memory lock utility to prevent race conditions in JavaScript apps.

Downloads

6

Readme

🔐 RaceLock JS

racelock

🚀 A Powerful In-Memory Lock Utility for Node.js

RaceLock JS is a robust, in-memory locking mechanism designed to safeguard your Node.js applications from common concurrency issues. It provides a simple yet powerful API to prevent race conditions, ensure task uniqueness, and safely coordinate asynchronous operations within a single process.


✨ Features

  • Simple API: Intuitive start() and end() methods for straightforward lock management.

  • Auto-Release: Optional lock timeouts for automatic release, preventing deadlocks.

  • Ownership Checks: Enhance security by ensuring only the lock owner can release it.

  • Exponential Backoff Retry: Built-in retryStart() for robust lock acquisition in contention scenarios.

  • Wait until Released: waitForUnlock() allows operations to pause until a desired lock becomes available.

  • Metadata Support: Attach custom data to locks for better context and debugging.

  • Introspection Utilities: Easily monitor and inspect active locks with getLockCount(), getAllLockedKeys(), and getLockInfo().

  • Forcible Release: forceUnlock() and clearAllLocks() for emergency lock management (use with caution).


📦 Installation

To install RaceLock JS in your Node.js project, use npm:

Bash

npm install racelock

🚀 Usage

Basic Locking

Acquire and release locks to protect critical sections of your code.

JavaScript

const RaceLock = require('racelock'); // Installed via npm

async function performCriticalTask() {
  const lockKey = 'job-123';
  if (RaceLock.start(lockKey, 5000)) { // Acquire lock with a 5-second timeout
    try {
      console.log('Lock acquired! Doing important work...');
      // Simulate asynchronous work
      await new Promise(resolve => setTimeout(resolve, 2000));
      console.log('Important work finished.');
    } finally {
      // Always ensure the lock is released, even if errors occur
      RaceLock.end(lockKey);
      console.log('Lock released.');
    }
  } else {
    console.log(`Failed to acquire lock for ${lockKey}. It's currently in use.`);
  }
}

performCriticalTask();

Example: Async Task Protection

Use retryStart() for resilient lock acquisition in concurrent environments.

JavaScript

const RaceLock = require('racelock'); // Installed via npm

async function processInvoice(orderId) {
  const lockKey = `invoice:${orderId}`;
  const ownerId = `worker-${Math.floor(Math.random() * 100)}`; // Example worker ID

  console.log(`${ownerId} attempting to process invoice ${orderId}...`);

  // Try to acquire the lock up to 3 times with exponential backoff (200ms base delay)
  // Lock will expire after 3 seconds if acquired
  const lockAcquired = await RaceLock.retryStart(
    lockKey,
    3, // attempts
    200, // base delay in ms
    3000, // lock timeout in ms
    { type: 'pdf_generation', order: orderId }, // metadata
    ownerId // owner ID
  );

  if (lockAcquired) {
    try {
      console.log(`${ownerId} acquired lock for ${lockKey}. Generating invoice...`);
      await new Promise(r => setTimeout(r, 2000)); // Simulate invoice generation
      console.log(`${ownerId} finished generating invoice for ${orderId}.`);
    } finally {
      // Release the lock, ensuring the ownerId matches
      RaceLock.end(lockKey, ownerId);
      console.log(`${ownerId} released lock for ${lockKey}.`);
    }
  } else {
    console.log(`${ownerId} could not acquire lock for ${lockKey} after multiple attempts. Skipping.`);
  }
}

// Simulate concurrent calls
processInvoice('ORD123');
processInvoice('ORD123'); // This call will likely contend for the same lock
processInvoice('ORD456');

📘 API Reference

start(key, timeout = 0, meta = {}, ownerId = null)

Attempts to acquire a lock for a given key.

  • key (string, required): A unique identifier for the lock.

  • timeout (number, optional, default: 0): Time in milliseconds after which the lock will automatically expire. A value of 0 means no auto-expiration.

  • meta (object, optional, default: {}): An object to store arbitrary metadata associated with the lock (e.g., moduleName, jobType).

  • ownerId (string, optional, default: null): An identifier for the entity holding the lock. Used for ownership checks when releasing.

Returns: true if the lock was successfully acquired, false if it was already locked.

end(key, ownerId = null)

Releases an active lock.

  • key (string, required): The unique ID of the lock to release.

  • ownerId (string, optional, default: null): If provided, the lock will only be released if this ownerId matches the one specified during start().

isLocked(key)

Checks the current status of a lock.

  • key (string, required): The unique ID of the lock to check.

Returns: true if the lock is currently active, false otherwise.

getLockInfo(key)

Retrieves detailed information about an active lock.

  • key (string, required): The unique ID of the lock.

Returns: An object containing:

JavaScript

{
  timestamp: 1650000000000, // Timestamp when the lock was acquired (Unix epoch in ms)
  meta: { job: 'invoice' },  // The metadata object provided during lock acquisition
  ownerId: 'worker-1'        // The owner ID provided during lock acquisition
}

Returns undefined if the lock does not exist.

waitForUnlock(key, checkInterval = 100)

Returns a Promise that resolves once the specified lock is released. This is useful for scenarios where you need to wait for another process to finish before proceeding.

  • key (string, required): The unique ID of the lock to wait for.

  • checkInterval (number, optional, default: 100): The interval in milliseconds at which to check for the lock's release.

Returns: Promise<void>

JavaScript

await RaceLock.waitForUnlock('task-key');
console.log('Task-key lock has been released! Proceeding...');

retryStart(key, attempts, delay, timeout, meta, ownerId)

Attempts to acquire a lock multiple times using an exponential backoff strategy.

  • key (string, required): Unique lock ID.

  • attempts (number, required): The maximum number of times to try acquiring the lock.

  • delay (number, required): The base delay in milliseconds between retries. The actual delay will increase exponentially (e.g., delay, delay * 2, delay * 4, etc.).

  • timeout (number, optional): Lock expiration time in milliseconds if acquired.

  • meta (object, optional): Optional metadata for the lock.

  • ownerId (string, optional): Optional ID of the lock holder.

Returns: Promise<boolean> - true if the lock was acquired within the specified attempts, false otherwise.

forceUnlock(key)

Forcibly releases a lock without checking for ownership. Use with extreme caution.

  • key (string, required): The unique ID of the lock to force release.

getLockCount()

Returns: number - The total number of active locks currently managed by RaceLock JS.

getAllLockedKeys()

Returns: string[] - An array of all currently active lock keys.

clearAllLocks()

⚠️ USE WITH EXTREME CAUTION IN PRODUCTION ENVIRONMENTS ⚠️ Clears all active locks and cancels any associated auto-release timeouts. This can lead to race conditions if not used judiciously. Primarily intended for testing or emergency recovery.


🛡 Best Practices

  • Always Use finally: Ensure locks are always released by placing RaceLock.end() calls within a finally block when working with try...catch. This guarantees release even if errors occur.

  • Utilize ownerId: If multiple systems, workers, or instances could potentially try to release the same lock, use ownerId during start() and end() to ensure only the rightful owner can release it.

  • Employ retryStart(): For job queues, concurrent workers, or any scenario where contention is expected, retryStart() is your go-to method for robust lock acquisition.

  • Careful with clearAllLocks(): This method is powerful and can disrupt ongoing operations. Reserve it for development, testing, or critical recovery scenarios.


📄 License

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

© 2025 Nishikanta Ray


🙌 Contributing

We welcome contributions of all kinds! If you have ideas for improvements, new features, or find a bug, please feel free to:

  • Fork the repository.

  • Star it to show your support!

  • Submit a Pull Request with your enhancements.

  • Open an Issue to report bugs or suggest features.