Mutex #

Available since 2.9.0
Deprecated since 2.57.0

Mutex is deprecated since 2.57.0: use withLock instead. Both take the same Redis key, so a Mutex and a withLock call on the same resource exclude each other, and call sites can be migrated one at a time.

Instantiates a new mutex, allowing to lock a resource.
Works both with single-node and cluster environments.

The constructor reads the node identifier: instantiate a Mutex once Kuzzle is running, from a plugin's init method onward, or in an application once app.start() has resolved. Before that, it throws a TypeError.


Constructor #

new Mutex(resource: string, options?: MutexOptions);

ArgumentsTypeDescription
resource
string
Resource identifier to be locked (must be identical across all nodes for the same resource)
options
MutexOptions
(optional) Mutex options (see below)

options #

The options object is used to configure the mutex behavior. Its type, MutexOptions, can be imported from kuzzle. The following properties can be provided:

OptionTypeDescription
attemptDelay
number
(default: 200) Delay in milliseconds between lock attempts
timeout
number
(default: -1) Mutex lock acquisition timeout, in milliseconds. If set to 0, locking will return immediately, whether it can or cannot lock within the 1st attempt. If set to -1, the mutex will try to acquire the resource indefinitely
ttl
number
(default: 5000) Lock time to live, in milliseconds. Locks are freed when unlock is invoked, or after that delay has expired.

Usage #

Waiting indefinitely until a resource is available:

import { Mutex } from 'kuzzle';

async function ConcurrentAccessMethod () {
  const mutex = new Mutex('concurrentAccessMethodResource');

  await mutex.lock();

  try {
    // safe zone: will only be executed once the resource is locked
  }
  finally {
    await mutex.unlock();
  }
}

Making sure that at most 1 task is executed at any given time in a cluster environment:

import { Mutex, TooManyRequestsError } from 'kuzzle';

async function DoNotRepeatProcess () {
  const mutex = new Mutex('uniqueTask', { timeout: 0 });

  const locked = await mutex.lock();

  if (!locked) {
    throw new TooManyRequestsError('That task is already ongoing');
  }

  try {
    // ... do things
  }
  finally {
    await mutex.unlock();
  }
}

Migrating to withLock #

withLock runs a callback under the lock and releases it when the callback settles: there is no lock / unlock pair to keep balanced.

MutexwithLock
timeout: -1 (default): wait indefinitelyretryAttempts: Infinity (the default is 10 retries, retryDelay 200 ms apart)
timeout: 0: lock() resolves to falseretryAttempts: 0: rejects with MutexAcquireError
timeout: n (milliseconds)retryAttempts set to about n / retryDelay
attemptDelayretryDelay
ttl (default 5000), never extendedttl (default 30000), extended while the callback runs

The first example above becomes:

import { withLock } from 'kuzzle';

async function ConcurrentAccessMethod () {
  await withLock('concurrentAccessMethodResource', async () => {
    // safe zone: will only be executed once the resource is locked
  }, { retryAttempts: Infinity });
}