node-redlock
Version:
A distributed locking algorithm used to manage distributed resources in a system.
129 lines (102 loc) • 5.96 kB
Markdown
Software Design Document (SDD) for Redlock
==============================================
Introduction and Overview
-------------------------------
Redlock is a distributed locking library for Node.js that uses the Redis database to manage locks. It allows multiple client processes to acquire and release locks on shared resources, ensuring that only one process can hold the lock at any given time. This is useful in scenarios where multiple processes need to access a shared resource concurrently and ensure that updates are performed in an orderly fashion.
System Architecture
------------------------------
The Redlock library is built on top of the Redis database, which provides the necessary functionality for locking. The Redlock library itself acts as an abstraction layer between the client processes and the Redis database, providing a simple API for acquiring and releasing locks.
Data Design
-------------------
The Redlock library uses a combination of Redis keys and values to manage locks. When a client process attempts to acquire a lock on a given resource, it sets a key in Redis with a value that includes the resource name and a unique identifier for the client process. The lock is released by deleting the corresponding Redis key.
Interface Design
-----------------------------
The Redlock library provides a simple API for acquiring and releasing locks. The `acquireLock` method takes two arguments: the name of the resource to be locked, and a time-to-live (TTL) value that specifies how long the lock should be held. The method returns a lock object that includes the name of the resource and a unique identifier for the client process.
The `releaseLock` method takes two arguments: the name of the resource to be unlocked, and the identifier of the lock to be released. This method releases the lock by deleting the corresponding Redis key.
Component Design
-----------------------------
The Redlock library is composed of several components, including a client factory, a lock manager, and a retry strategy. The client factory is responsible for creating client instances for the Redis database. The lock manager is responsible for managing locks on the shared resources. The retry strategy is used to handle cases where multiple client processes attempt to acquire a lock at the same time.
User Interface Design
-----------------------------------
There is no user interface associated with the Redlock library, as it is a library that is meant to be used by other software components rather than by users directly.
Assumptions and Dependencies
---------------------------------
The Redlock library assumes that the Redis database is available and functioning correctly. It also assumes that all client processes are using the same configuration for connecting to the Redis database. The library has a dependency on the `ioredis` library, which provides an interface for interacting with Redis databases.
Glossary of Terms
-----------------------------
* Redlock: A distributed locking library for Node.js that uses the Redis database to manage locks.
* Redis: A popular in-memory data structure store, used as a database in this context.
* Client process: A software component that needs to access a shared resource concurrently and ensure that updates are performed in an orderly fashion.
* Lock: A mechanism used by the Redlock library to ensure that only one client process can hold a given resource at any given time.
* Resource: A shared resource that multiple client processes need to access concurrently.
* TTL (Time-to-Live): The amount of time that a lock should be held by a client process before being released.
Class Diagram (in Mermaid syntax)
-------------------------------------------
```lua
class Redlock {
constructor(clients) {
this.clients = clients;
}
async acquireLock(resource, ttl) {
// Set a key in Redis with the resource name and client identifier
await this.setLockKey(resource, ttl);
// Return the lock object
return { resource, id: uuid() };
}
async releaseLock(resource, lockId) {
// Delete the key in Redis corresponding to the lock
await this.deleteLockKey(resource, lockId);
}
async renewLock(resource, lockId, ttl) {
// Increment the TTL of the lock by calling redis.call("pexpire", KEYS[1], ARGV[2])
await this.renewTTL(resource, lockId);
}
}
class ClientFactory {
createClient() {
// Create a new client instance for the Redis database
return ioredis;
}
}
class LockManager {
async acquireLock(resource, ttl) {
const clients = await this.getClients();
for (const client of clients) {
const lock = await client.acquireLock(resource, ttl);
if (lock) {
return lock;
}
}
throw new Error("Could not acquire lock");
}
async releaseLock(resource, lockId) {
const clients = await this.getClients();
for (const client of clients) {
await client.releaseLock(resource, lockId);
}
}
async renewLock(resource, lockId, ttl) {
const clients = await this.getClients();
for (const client of clients) {
await client.renewLock(resource, lockId, ttl);
}
}
}
class RetryStrategy {
async retry(fn, maxAttempts) {
let attempt = 0;
while (attempt < maxAttempts) {
try {
await fn();
return true;
} catch (err) {
// Retry with a higher delay on the next attempt
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt)));
attempt++;
}
}
throw new Error("Maximum number of retries exceeded");
}
}
```
The above class diagram shows the structure of the Redlock library, with the `Redlock` class as the main entry point, and the `ClientFactory`, `LockManager`, and `RetryStrategy` classes as its internal components. The `Redlock` class is responsible for acquiring and releasing locks, while the other two classes provide additional functionality.