API
Below is the API documentation for Verrou.
Lock API
acquire
Acquire the lock. If the lock is already acquired, it will wait until it is released or the timeout is reached.
const lock = verrou.createLock('key', '10s')
const acquired = await lock.acquire()
const acquired = await lock.acquire({ retry: { timeout: 1000 } })
const acquired = await lock.acquire({ retry: { timeout: '1s' } })
if (acquired) {
await criticalSection()
}
Accept an optional object with the following properties:
retry: An object with the following properties:timeout: The maximum time to wait for the lock to be acquired. Defaults toInfinity.delay: The delay in milliseconds between each retry. Defaults to250attempts: The maximum number of attempts to acquire the lock.
acquire will return a boolean indicating if the lock was acquired or not
acquireImmediately
Try to acquire the lock immediately ( without retrying ). If the lock is already acquired, it will return false immediately.
import { errors } from '@verrou/core'
const lock = verrou.createLock('key')
const acquired = await lock.acquireImmediately()
if (acquired) {
await criticalSection()
}
release
Release the lock. Note that only the lock owner can release the lock.
const lock = verrou.createLock('key')
await lock.acquire()
await lock.release()
[Symbol.asyncDispose]
Release the lock if it is owned by this instance. Called automatically when the lock is declared with await using. Unlike release, it does not throw E_LOCK_NOT_OWNED if the lock was never acquired, already released, or is now owned by someone else.
{
await using lock = verrou.createLock('key')
if (await lock.acquire()) {
// do something
}
} // lock is released here
run
Acquire the lock, run the callback, and release the lock. The method will return a tuple with the first value being a boolean indicating if the lock was acquired or not, and the second value being the result of the callback.
const lock = verrou.createLock('key')
const [executed, result] = await lock.run(() => {
// do something
return 'result'
})
runImmediately
Same as run, but try to acquire the lock immediately ( without retrying ).
const lock = verrou.createLock('key')
const [executed, result] = await lock.runImmediately(async () => {
// do something
return 'result'
})
Accept an optional object with the same properties as acquire.
isLocked
Check if the lock is acquired.
const lock = verrou.createLock('key')
const isLocked = await lock.isLocked()
getOwner
Get the owner name of the lock. By default, the owner name is a randomly generated string.
Getting the owner is useful for sharing the lock between multiple processes
const lock = verrou.createLock('key')
const owner = await lock.getOwner()
isExpired
Check if the lock is expired.
const lock = verrou.createLock('key', '10s')
const isExpired = lock.isExpired()
extend
Extend the lock expiration time.
const lock = verrou.createLock('key', '10s')
await lock.acquire()
// extend the lock expiration time by 10 seconds
// since this is the default expiration time of the lock
await lock.extend()
// extend the lock expiration time by 20 seconds
await lock.extend('20s')
Argument is optional and defaults to the lock expiration time.
getRemainingTime
Get the remaining time in milliseconds before the lock expires.
const lock = verrou.createLock('key', '10s')
await lock.acquire()
// returns remaining time for the lock in milliseconds
const remainingTime = lock.getRemainingTime()
forceRelease
Force the lock to be released, no matter the owner of the lock.
const lock = verrou.createLock('key')
await lock.forceRelease()
LockFactory API
createLock
Create a lock.
import { RedisStore } from '@verrou/core/drivers/redis'
const lockFactory = new LockFactory(new RedisStore())
const lock1 = lockFactory.createLock('key')
const lock2 = lockFactory.createLock('key', '5m')
const lock3 = lockFactory.createLock('key', 30_000)
First argument is the lock key. Second argument is optional and is the lock expiration time. By default, the lock expiration time is 30s.
restoreLock
Restore a lock. Useful when sharing a lock between multiple processes. See Sharing a lock between multiple processes for more details.
import { RedisStore } from '@verrou/core/drivers/redis'
const lockFactory = new LockFactory(new RedisStore())
const lock1 = lockFactory.createLock('key', 'owner')
const lock2 = lockFactory.restoreLock(lock1.serialize())
shutdown
Stops acquisitions, waits for active run and runImmediately callbacks, then releases this factory's remaining locks. Returns Promise<void>.
await lockFactory.shutdown({ timeout: '10s' })
The optional ShutdownOptions argument is exported from @verrou/core/types. Its timeout property defaults to 30_000:
timeout | Callback wait |
|---|---|
| Number or duration string | At most that duration |
0 | No wait |
null | Until all callbacks finish |
The timeout does not cancel callbacks or limit store requests. Connections remain open.
- Acquisitions reject with
E_SHUTDOWNas soon as shutdown starts, including retries and calls through existing handles. - Acquisitions already sent to the store settle before cleanup. A late success is released without starting its callback.
- Repeated shutdown calls return the same promise. Only the first call's options apply.
Cleanup uses owner tokens and attempts every release. Ownership-loss errors are ignored. Other release failures reject shutdown with an AggregateError.
If a release fails, the lease remains tracked until release or expiration. You can retry lock.release() after the store recovers.
Within a factory, store mutations for the same key run in order. Different keys and stores remain independent.
Only leases acquired or extended through this factory are tracked. Released and expired leases are removed. Directly constructed Lock instances are not managed.
restoreLock() alone does not register a lease. Extending a restored or expired lease registers it, and releasing a restored handle removes the matching lease. serialize() does not transfer tracking to another process.
After shutdown starts, release() does nothing for an untracked lease. This lets callbacks finish after shutdown has released their locks.
Verrou API
Verrou API is a wrapper around the LockFactory API.
const verrou = new Verrou({
default: 'myRedisStore',
stores: {
myMemoryStore: { driver: memoryStore() },
myRedisStore: { driver: redisStore() },
mySecondMemoryStore: { driver: memoryStore() },
myPostgresStore: { driver: postgresStore() },
},
})
createLock and restoreLock use the default store. shutdown shuts down all initialized stores, not just the default store.
If you need to use a different store, you can use the use method.
// use the myRedisStore store, since it's the defined default store
verrou.createLock('key')
// use a store that is not the default store
verrou.use('myMemoryStore').createLock('key')
verrou.use('mySecondMemoryStore').createLock('key')
use
As explained above, the use method allows you to use a different store than the default store.
verrou.use('myMemoryStore').createLock('key')
shutdown
Shuts down all initialized stores concurrently. Accepts the same options as LockFactory.shutdown.
await verrou.shutdown({ timeout: '10s' })
Unused stores remain uninitialized. Accessing one after shutdown throws E_SHUTDOWN.
A store failure does not stop the other stores' cleanup. The returned promise rejects with an AggregateError containing the failing factories' errors.
See Graceful shutdown for a SIGTERM handler and connection cleanup.