Simple process locking using Redis.
This module lets you spin up two networked processes and ensure only one of them runs at a time. Lock state is synchronized between processes using Redis.
npm install process-lock-node
Ensure that the REDIS_URL
environment variable is set for each process using this library:
REDIS_URL=redis://your-redis-host:6379
In your process, intialize a lock by giving it a name and timeout:
const ProcessLock = require('process-lock-node');
const processLock = new ProcessLock('lock_name', 60);
When the process starts up, attempt to acquire the lock:
function waitForLock() {
processLock.lock((err, owner) => {
if (owner) {
startProcess();
} else {
setTimeout(waitForLock, 60000);
}
});
}
As long as the process is running, ensure that you refresh the lock. If you do not refresh the lock,
it will expire automatically in timeout
seconds, where timeout
is the value you passed when initializing
the lock. This is to prevent a process that unexpectly dies from holding the lock indefinitely.
function startProcess() {
setInterval(() => {
processLock.update((err, renewed) => {
if (err || !renewed) {
// Handle the error
}
});
}, 30000);
}
The refresh interval must be smaller than the timeout
to ensure that we refresh before the lock expires. Setting the refresh interval to half of the timeout is recommended.
When the process exits, clean up the lock so that it can be acquired by another process:
onProcessExit(() => {
processLock.clear((err, cleared) => {
if (err || !cleared) {
// Log the error
}
});
});
Construct a process lock.
lockName: string
A name for the lock. Processes that require synchronization should initialize a lock with the samelockName
.timeout: number
Seconds from now when an acquired lock will expire and be automatically released.
Attempt to acquire a lock.
callback: (err, value: boolean) -> ()
A node-style callback that receives the result of attempting to acquire the lock.value
is true if the lock was obtained.
Attempt to renew a lock.
callback: (err, value: boolean) -> ()
A callback that receives the result of attempting to renew the lock.value
is:false
if the lock could not be renewedtrue
if it was explicitly renewed.
Attempt to release a lock.
force: boolean
If true, force clear the lock even if the current process is not the owner.callback: (err, value: boolean) -> ()
A callback that receives the result of attempting to clear the lock.value
is:false
if the lock was not cleared because the current process was not the ownertrue
if the lock was clearednull
if the lock did not exist at all.
Check the state of a lock.
callback: (err, value: string) -> ()
A callback that receives the result of checking a lock's state.value
is"open"
or"locked"
.