### Start Development Environment with Docker Compose Source: https://github.com/mike-marcacci/node-redlock/blob/main/CONTRIBUTING.md Starts the Docker Compose environment, which includes setting up dependencies, building the project, and initializing Redis instances for testing. This command ensures a consistent environment for development. ```bash docker compose up ``` -------------------------------- ### Install Redlock with npm Source: https://github.com/mike-marcacci/node-redlock/blob/main/README.md Installs the redlock package as a dependency for your Node.js project using npm. ```bash npm install --save redlock ``` -------------------------------- ### CommonJS Usage with Redlock and ioredis Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Demonstrates how to import and use Redlock in a CommonJS environment with ioredis for Redis client management. This example shows the basic acquisition and release of a distributed lock using async/await syntax. ```javascript const { default: Redlock } = require("redlock"); const Redis = require("ioredis"); const redis = new Redis({ host: "localhost", port: 6379 }); const redlock = new Redlock([redis], { retryCount: 10, retryDelay: 200 }); // Use async/await or promises (async () => { const lock = await redlock.acquire(["my-resource"], 5000); try { // Critical section console.log("Lock acquired"); } finally { await lock.release(); } })(); ``` -------------------------------- ### Redlock Custom Lua Scripts for Locking Behavior Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Explains how to customize the default Redis Lua scripts used by Redlock for acquiring, extending, and releasing locks. This allows for specialized locking behavior tailored to specific application needs. The example shows how to provide custom scripts for `acquireScript` and `extendScript`, while reusing the default `releaseScript`. ```typescript const redlock = new Redlock( [redisA, redisB, redisC], { driftFactor: 0.01, retryCount: 10 }, { // Provide custom acquire script acquireScript: (defaultScript) => { // Modify or replace default script return defaultScript + `\n-- Custom logic here`; }, // Provide custom extend script extendScript: ` -- Custom extend implementation for i, key in ipairs(KEYS) do if redis.call("get", key) ~= ARGV[1] then return 0 end end for i, key in ipairs(KEYS) do redis.call("set", key, ARGV[1], "PX", ARGV[2]) end return #KEYS `, // Provide custom release script function releaseScript: (defaultScript) => { return defaultScript; } } ); // Scripts are hashed and cached by Redis const lock = await redlock.acquire(["custom:resource"], 5000); await lock.release(); ``` -------------------------------- ### Redlock Error Handling and Monitoring Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Shows how to implement error handling and monitoring for Redlock operations using event listeners. It covers ignoring specific errors like `ResourceLockedError` and logging other critical errors such as node failures or network issues. The example also demonstrates inspecting detailed statistics for failed lock acquisition attempts. ```typescript import Redlock, { ExecutionError, ResourceLockedError } from "redlock"; const redlock = new Redlock([redisA, redisB, redisC]); // Monitor errors from individual Redis nodes redlock.on("error", (error) => { // Ignore explicit resource locked errors if (error instanceof ResourceLockedError) { return; } // Log other errors (node failures, network issues, etc.) console.error("Redlock error:", error); }); try { await redlock.acquire(["resource:x"], 5000); } catch (error) { if (error instanceof ExecutionError) { // Failed to achieve quorum during retry window console.error("Lock acquisition failed:", error.message); // Inspect per-attempt statistics const stats = await Promise.all(error.attempts); stats.forEach((attempt, index) => { console.log(`Attempt ${index}:`, { membershipSize: attempt.membershipSize, quorumSize: attempt.quorumSize, votesFor: attempt.votesFor.size, votesAgainst: attempt.votesAgainst.size }); }); } else if (error instanceof ResourceLockedError) { // Resource already locked by another process console.log("Resource is currently locked"); } } ``` -------------------------------- ### Configure Redlock with ioredis clients Source: https://github.com/mike-marcacci/node-redlock/blob/main/README.md Sets up a Redlock instance by initializing multiple ioredis clients and configuring Redlock options such as drift factor, retry counts, and delays. It requires at least one Redis client and supports customization of retry behavior and automatic lock extension. ```typescript import Client from "ioredis"; import Redlock from "redlock"; const redisA = new Client({ host: "a.redis.example.com" }); const redisB = new Client({ host: "b.redis.example.com" }); const redisC = new Client({ host: "c.redis.example.com" }); const redlock = new Redlock( // You should have one client for each independent redis node // or cluster. [redisA, redisB, redisC], { // The expected clock drift; for more details see: // http://redis.io/topics/distlock driftFactor: 0.01, // multiplied by lock ttl to determine drift time // The max number of times Redlock will attempt to lock a resource // before erroring. retryCount: 10, // the time in ms between attempts retryDelay: 200, // time in ms // the max time in ms randomly added to retries // to improve performance under high contention // see https://www.awsarchitectureblog.com/2015/03/backoff.html retryJitter: 200, // time in ms // The minimum remaining time on a lock before an extension is automatically // attempted with the `using` API. automaticExtensionThreshold: 500, // time in ms } ); ``` -------------------------------- ### Acquire, extend, and release lock directly Source: https://github.com/mike-marcacci/node-redlock/blob/main/README.md Demonstrates manual lock management using `redlock.acquire`, `lock.extend`, and `lock.release`. This approach allows for explicit control over lock acquisition, extension with a new duration, and release, typically within a `try...finally` block to ensure the lock is always released. ```typescript // Acquire a lock. let lock = await redlock.acquire(["a"], 5000); try { // Do something... await something(); // Extend the lock. Note that this returns a new `Lock` instance. lock = await lock.extend(5000); // Do something else... await somethingElse(); } finally { // Release the lock. await lock.release(); } ``` -------------------------------- ### Initialize Redlock with Multiple Redis Instances (TypeScript) Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Initializes a Redlock instance with multiple ioredis clients for fault tolerance. Configuration options include clock drift compensation, retry counts, and retry delays. ```typescript import Client from "ioredis"; import Redlock from "redlock"; // Create independent Redis clients const redisA = new Client({ host: "a.redis.example.com" }); const redisB = new Client({ host: "b.redis.example.com" }); const redisC = new Client({ host: "c.redis.example.com" }); // Initialize Redlock with multiple Redis nodes for fault tolerance const redlock = new Redlock( [redisA, redisB, redisC], { driftFactor: 0.01, // Clock drift compensation (multiplied by TTL) retryCount: 10, // Max lock acquisition attempts retryDelay: 200, // Base delay between retries (ms) retryJitter: 200, // Random jitter added to retry delay (ms) automaticExtensionThreshold: 500 // Auto-extend when <500ms remaining } ); ``` -------------------------------- ### Run a One-Off Command with Docker Compose Source: https://github.com/mike-marcacci/node-redlock/blob/main/CONTRIBUTING.md Executes a specific command within the Docker Compose environment, such as running tests. The `--rm` flag ensures the container is removed after the command completes. This is useful for executing single tasks without keeping the environment running. ```bash docker compose run --rm runner yarn test ``` -------------------------------- ### Graceful Shutdown with Redlock Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Illustrates how to implement a graceful shutdown process for applications using Redlock. It ensures that all Redis client connections are properly closed and Redlock resources are cleaned up when the application receives a termination signal. ```typescript const redlock = new Redlock([redisA, redisB, redisC]); // Application shutdown handler process.on("SIGTERM", async () => { console.log("Shutting down gracefully..."); try { // Quit all Redis client connections await redlock.quit(); console.log("All Redis connections closed"); } catch (error) { console.error("Error during shutdown:", error); } process.exit(0); }); // Manual cleanup async function cleanup() { await redlock.quit(); } ``` -------------------------------- ### Import Redlock in CommonJS projects Source: https://github.com/mike-marcacci/node-redlock/blob/main/README.md Shows how to import the Redlock class in a CommonJS environment using `require`. This is necessary for older Node.js applications that do not use ES Modules. Ensure only one version (ESM or CJS) is used to avoid conflicts. ```javascript const { default: Redlock } = require("redlock"); ``` -------------------------------- ### Redis Cluster Multi-Resource Locking with Hash Tags Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Demonstrates how to lock multiple resources atomically in a Redis cluster by using hash tags to ensure all related keys are placed on the same cluster node. This approach guarantees that the lock operation is performed on a single node, preventing race conditions. The library requires a Redis cluster connection and the Redlock instance. ```typescript import Client, { Cluster } from "ioredis"; import Redlock from "redlock"; // Initialize Redis cluster const cluster = new Cluster([ { host: "cluster1.redis.example.com", port: 7000 }, { host: "cluster2.redis.example.com", port: 7001 }, { host: "cluster3.redis.example.com", port: 7002 } ]); const redlock = new Redlock([cluster]); // Use hash tags to ensure all keys resolve to same cluster node // Format: {tag}key - only the {tag} portion determines the node await redlock.using( [ "{tenant:org123}user:456", "{tenant:org123}account:789", "{tenant:org123}transaction:101" ], 5000, async (signal) => { // All resources locked atomically on same cluster node await performAtomicOperation(); } ); // Generic prefix for all locks (simplest approach) await redlock.using( ["{redlock}resource:a", "{redlock}resource:b"], 3000, async () => { /* ... */ } ); // Single resource in cluster (no hash tag needed) await redlock.using(["single:resource"], 5000, async () => { /* ... */ }); ``` -------------------------------- ### Manual Lock Acquisition and Release (TypeScript) Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Explicitly acquires, extends, and releases locks for fine-grained control over distributed resources. Includes error handling and checking lock expiration. Custom retry settings can be provided during acquisition. ```typescript // Acquire a lock on resources for 5000ms let lock = await redlock.acquire(["resource:1", "resource:2"], 5000); try { // Perform first operation await processData("resource:1"); // Extend the lock by another 5000ms (returns new Lock instance) lock = await lock.extend(5000); // Perform second operation await processData("resource:2"); // Check lock expiration const timeRemaining = lock.expiration - Date.now(); console.log(`Lock expires in ${timeRemaining}ms`); } catch (error) { console.error("Operation failed:", error); throw error; } finally { // Always release the lock await lock.release(); } // Acquire with custom retry settings const customLock = await redlock.acquire( ["limited:resource"], 3000, { retryCount: 3, retryDelay: 150 } ); ``` -------------------------------- ### Automatic Lock Management with `using()` (TypeScript) Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Executes code within an auto-extending lock context, supporting abort signals for graceful handling of lock extension failures. The lock is automatically released upon completion or error. Custom retry settings can also be applied. ```typescript await redlock.using(["user:123", "account:456"], 5000, async (signal) => { // Acquire locks on multiple resources for 5 seconds const userBalance = await getUserBalance("user:123"); const accountBalance = await getAccountBalance("account:456"); // Check if lock extension failed if (signal.aborted) { // Lock could not be extended - exclusivity no longer guaranteed throw signal.error; } // Perform critical operations while holding the lock if (userBalance >= 100) { await updateBalance("user:123", userBalance - 100); await updateBalance("account:456", accountBalance + 100); } // Lock automatically released on completion or error }); // Using with custom settings await redlock.using( ["resource:xyz"], 3000, { retryCount: 5, retryDelay: 100 }, async (signal) => { await performCriticalOperation(); } ); ``` -------------------------------- ### Handle Redlock Errors Source: https://github.com/mike-marcacci/node-redlock/blob/main/README.md This snippet demonstrates how to listen for and handle errors emitted by the redlock library. It specifically ignores `ResourceLockedError` exceptions, which indicate that a resource is already locked, and logs all other errors to the console. This is useful for monitoring and debugging distributed locking operations. ```typescript redlock.on("error", (error) => { // Ignore cases where a resource is explicitly marked as locked on a client. if (error instanceof ResourceLockedError) { return; } // Log all other errors. console.error(error); }); ``` -------------------------------- ### Execute routine with auto-extending lock Source: https://github.com/mike-marcacci/node-redlock/blob/main/README.md Uses the `redlock.using` method to acquire a lock for specified resources, execute an asynchronous routine, and automatically extend the lock as needed. It returns the routine's result or throws an error if the lock cannot be maintained. The routine receives an AbortSignal to monitor lock status. ```typescript await redlock.using(["foo", "bar"], 5000, async (signal) => { // Do something... await something("foo"); // Make sure any attempted lock extension has not failed. if (signal.aborted) { // Note: The error thrown out of `redlock.using` will not be `signal.error` but rather "The operation was unable to achieve a quorum during its retry window." // and that's after retrying to release the lock (after 2-3 seconds if you use the default settings). throw signal.error; } // Do something else... await somethingElse("bar"); }); ``` -------------------------------- ### Lock Status Checking (TypeScript) Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Checks resource availability by attempting lock acquisition with zero retries. Handles `ResourceLockedError` specifically to determine if a resource is locked or unavailable. Supports persistent locking with infinite retries. ```typescript try { // Attempt to acquire lock with no retries const lock = await redlock.acquire( ["resource:check"], 1000, { retryCount: 0 } ); // Resource is available console.log("Resource is unlocked and available"); await lock.release(); } catch (error) { if (error instanceof ResourceLockedError) { // Resource is locked or unavailable console.log("Resource is currently locked"); } else { // Other error occurred console.error("Lock check failed:", error); } } // Unlimited retries until lock acquired const persistentLock = await redlock.acquire( ["resource:wait"], 5000, { retryCount: -1 } // -1 means infinite retries ); ``` -------------------------------- ### Redlock Lock Extension with Expiration Validation Source: https://context7.com/mike-marcacci/node-redlock/llms.txt Illustrates how to extend the duration of an acquired lock while performing validation to ensure it has not expired. This prevents extending a lock that has already been lost. It includes robust error handling within a `finally` block to ensure the lock is always released, even if errors occur during operations or extension. ```typescript let lock = await redlock.acquire(["resource:extend"], 5000); try { await performQuickOperation(); // Check if lock has expired before extending if (lock.expiration < Date.now()) { throw new Error("Lock already expired, cannot extend"); } // Extend by 3000ms - invalidates old lock and returns new one lock = await lock.extend(3000); // Continue with more operations await performAdditionalWork(); // Extend again if needed const timeRemaining = lock.expiration - Date.now(); if (timeRemaining < 1000) { lock = await lock.extend(5000); } } catch (error) { if (error instanceof ExecutionError) { console.error("Extension failed - lock may be lost"); } throw error; } finally { // Release will be attempted even if expired await lock.release(); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.