Chapter 9. Clustered Locks
A clustered lock is a lock which is distributed and shared among all nodes in the Data Grid cluster and currently provides a way to execute code that will be synchronized between the nodes in a given cluster.
9.1. Installation
In order to start using the clustered locks, you needs to add the dependency in your Maven pom.xml
file:
pom.xml
<dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-clustered-lock</artifactId> </dependency>
9.2. ClusteredLock Configuration
Currently there is a single type of ClusteredLock
supported : non reentrant, NODE ownership lock.
9.2.1. Ownership
-
NODE
When aClusteredLock
is defined, this lock can be used from all the nodes in the Data Grid cluster. When the ownership is NODE type, this means that the owner of the lock is the Data Grid node that acquired the lock at a given time. This means that each time we get aClusteredLock
instance with theClusteredCacheManager
, this instance will be the same instance for each Data Grid node. This lock can be used to synchronize code between Data Grid nodes. The advantage of this lock is that any thread in the node can release the lock at a given time. -
INSTANCE
- not yet supported
When a ClusteredLock
is defined, this lock can be used from all the nodes in the Data Grid cluster. When the ownership is INSTANCE type, this means that the owner of the lock is the actual instance we acquired when ClusteredLockManager.get("lockName")
is called.
This means that each time we get a ClusteredLock
instance with the ClusteredCacheManager
, this instance will be a new instance. This lock can be used to synchronize code between Data Grid nodes and inside each Data Grid node. The advantage of this lock is that only the instance that called 'lock' can release the lock.
9.2.2. Reentrancy
When a ClusteredLock
is configured reentrant, the owner of the lock can reacquire the lock as many consecutive times as it wants while holding the lock.
Currently, only non reentrant locks are supported. This means that when two consecutive lock
calls are sent for the same owner, the first call will acquire the lock if it’s available, and the second call will block.
9.3. ClusteredLockManager Interface
The ClusteredLockManager
interface, marked as experimental, is the entry point to define, retrieve and remove a lock. It automatically listen to the creation of EmbeddedCacheManager
and proceeds with the registration of an instance of it per EmbeddedCacheManager
. It starts the internal caches needed to store the lock state.
Retrieving the ClusteredLockManager
is as simple as invoking the EmbeddedClusteredLockManagerFactory.from(EmbeddedCacheManager)
as shown in the example below:
// create or obtain your EmbeddedCacheManager EmbeddedCacheManager manager = ...; // retrieve the ClusteredLockManager ClusteredLockManager clusteredLockManager = EmbeddedClusteredLockManagerFactory.from(manager);
@Experimental public interface ClusteredLockManager { boolean defineLock(String name); boolean defineLock(String name, ClusteredLockConfiguration configuration); ClusteredLock get(String name); ClusteredLockConfiguration getConfiguration(String name); boolean isDefined(String name); CompletableFuture<Boolean> remove(String name); CompletableFuture<Boolean> forceRelease(String name); }
-
defineLock
: Defines a lock with the specified name and the defaultClusteredLockConfiguration
. It does not overwrite existing configurations. -
defineLock(String name, ClusteredLockConfiguration configuration)
: Defines a lock with the specified name andClusteredLockConfiguration
. It does not overwrite existing configurations. -
ClusteredLock get(String name)
: Get’s aClusteredLock
by it’s name. A call ofdefineLock
must be done at least once in the cluster. See ownership level section to understand the implications ofget
method call.
Currently, the only ownership level supported is NODE.
-
ClusteredLockConfiguration getConfiguration(String name)
:
Returns the configuration of a ClusteredLock
, if such exists.
-
boolean isDefined(String name)
: Checks if a lock is already defined. -
CompletableFuture<Boolean> remove(String name)
: Removes aClusteredLock
if such exists. -
CompletableFuture<Boolean> forceRelease(String name)
: Releases - or unlocks - aClusteredLock
, if such exists, no matter who is holding it at a given time. Calling this method may cause concurrency issues and has to be used in exceptional situations.
9.4. ClusteredLock Interface
ClusteredLock
interface, marked as experimental, is the interface that implements the clustered locks.
@Experimental public interface ClusteredLock { CompletableFuture<Void> lock(); CompletableFuture<Boolean> tryLock(); CompletableFuture<Boolean> tryLock(long time, TimeUnit unit); CompletableFuture<Void> unlock(); CompletableFuture<Boolean> isLocked(); CompletableFuture<Boolean> isLockedByMe(); }
-
lock
: Acquires the lock. If the lock is not available then call blocks until the lock is acquired. Currently, there is no maximum time specified for a lock request to fail, so this could cause thread starvation. -
tryLock
Acquires the lock only if it is free at the time of invocation, and returnstrue
in that case. This method does not block (or wait) for any lock acquisition. tryLock(long time, TimeUnit unit)
If the lock is available this method returns immediately withtrue
. If the lock is not available then the call waits until :- The lock is acquired
- The specified waiting time elapses
If the time is less than or equal to zero, the method will not wait at all.
-
unlock
Releases the lock. Only the holder of the lock may release the lock.
-
isLocked
Returnstrue
when the lock is locked andfalse
when the lock is released. -
isLockedByMe
Returnstrue
when the lock is owned by the caller andfalse
when the lock is owned by someone else or it’s released.
9.4.1. Usage Examples
EmbeddedCache cm = ...; ClusteredLockManager cclm = EmbeddedClusteredLockManagerFactory.from(cm); lock.tryLock() .thenCompose(result -> { if (result) { try { // manipulate protected state } finally { return lock.unlock(); } } else { // Do something else } }); }
9.4.2. ClusteredLockManager Configuration
You can configure ClusteredLockManager
to use different strategies for locks, either declaratively or programmatically, with the following attributes:
num-owners
-
Defines the total number of nodes in each cluster that store the states of clustered locks. The default value is
-1
, which replicates the value to all nodes. reliability
Controls how clustered locks behave when clusters split into partitions or multiple nodes leave a cluster. You can set the following values:
-
AVAILABLE
: Nodes in any partition can concurrently operate on locks. -
CONSISTENT
: Only nodes that belong to the majority partition can operate on locks. This is the default value.
-
The following is an example declarative configuration for ClusteredLockManager
:
<?xml version="1.0" encoding="UTF-8"?> <infinispan xmlns="urn:infinispan:config:10.1"> ... <cache-container default-cache="default"> <transport/> <local-cache name="default"> <locking concurrency-level="100" acquire-timeout="1000"/> </local-cache> <clustered-locks xmlns="urn:infinispan:config:clustered-locks:10.1" num-owners = "3" reliability="AVAILABLE"> <clustered-lock name="lock1" /> <clustered-lock name="lock2" /> </clustered-locks> </cache-container> ... </infinispan>