Class ReentrantDistributedZookeeperLock
- All Implemented Interfaces:
Lock,DistributedLock
ReentrantLock, except that it uses Zookeeper to
share the lock state across multiple nodes, allowing for a distributed lock. The owning thread may acquire the lock multiple times, but must unlock for each time the lock is
acquired. It's important to get in the habit of unlocking immediately in a finally block to prevent orphaned locks:
Lock lock = new ReentrantDistributedZookeeperLock(zk, "/solr-update/locks", "solrUpdate_commandLock");
lock.lockInterruptibly(); //This will block until a lock is acquired or until the Thread is interrupted.
try {
//Do something in a globally locked state
} finally {
lock.unlock();
}
or
Lock lock = new ReentrantDistributedZookeeperLock(zk, "/solr-update/locks", "solrUpdate_commandLock");
if (lock.tryLock()) {
try {
//Do something in a globally locked state
} finally {
lock.unlock();
}
}
In terms of performance, the average lock time, on a single quad-core laptop running a single Zookeeper instance, takes about 7-8 milliseconds. And the average unlock time also takes around 7-8 milliseconds during a simple test on a quad core laptop that was also running a single Zookeeper instance. This is with a single thread, and is assuming no reentrant locks and no contention or waiting for locks to become available. In other words, disregarding blocking and contention, it will likely take, on average, 14-16 milliseconds to create and release a lock (approximately 55-65 locks and releases / second for any given lock). These metrics will depend on a number of things, including contention for locks, Zookeeper cluster size and configuration, hardware, etc.
- Author:
- Kelly Tisdell
-
Nested Class Summary
Nested classes/interfaces inherited from interface org.broadleafcommerce.core.util.lock.DistributedLock
DistributedLock.DistributedLockException -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThis is the base folder that all locks will be written to in Zookeeper.static final StringThis is the prefix of any lock entry.Fields inherited from interface org.broadleafcommerce.core.util.lock.DistributedLock
GLOBAL_ENV_CAN_OBTAIN_LOCK_PROPERTY_NAME -
Constructor Summary
ConstructorsConstructorDescriptionReentrantDistributedZookeeperLock(org.apache.zookeeper.ZooKeeper zk, String lockPath, String lockName) ReentrantDistributedZookeeperLock(org.apache.zookeeper.ZooKeeper zk, String lockPath, String lockName, boolean useDefaultBasePath, List<org.apache.zookeeper.data.ACL> acls) This constructor takes in theZooKeeper(non-nullable), the lock path (non-nullable and in the format of '/path/to/this/lock/folder`), and the lock name (non-nullable and non-empty string with no whitespaces, leading or trailing slashes, or special characters except '-' or '_').ReentrantDistributedZookeeperLock(org.apache.zookeeper.ZooKeeper zk, String lockPath, String lockName, List<org.apache.zookeeper.data.ACL> acls) This constructor takes in theZooKeeper(non-nullable), the lock path (non-nullable and in the format of '/path/to/this/lock/folder`), and the lock name (non-nullable and non-empty string with no whitespaces, leading or trailing slashes, or special characters except '-' or '_').ReentrantDistributedZookeeperLock(org.apache.zookeeper.ZooKeeper zk, String lockPath, String lockName, org.springframework.core.env.Environment env, boolean useDefaultBasePath, List<org.apache.zookeeper.data.ACL> acls) This constructor takes in theZooKeeper(non-nullable), the lock path (non-nullable and in the format of '/path/to/this/lock/folder`), the lock name (non-nullable and non-empty string with no whitespaces, leading or trailing slashes, or special characters except '-' or '_'), and anEnvironmentobject, which can be null.ReentrantDistributedZookeeperLock(org.apache.zookeeper.ZooKeeper zk, String lockPath, String lockName, org.springframework.core.env.Environment env, List<org.apache.zookeeper.data.ACL> acls) This constructor takes in theZookeeper(non-nullable), the lock path (non-nullable and in the format of '/path/to/this/lock/folder`), the lock name (non-nullable and non-empty string with no whitespaces, leading or trailing slashes, or special characters except '-' or '_'), and anEnvironmentobject, which can be null. -
Method Summary
Modifier and TypeMethodDescriptionbooleanAllows one to disable this locking mechanism via the 'org.broadleafcommerce.core.util.lock.DistributedLock.canParticipate' (globally) or the 'org.broadleafcommerce.core.util.lock.DistributedLock.booleanIndicates if the current thread holds the lock.protected List<org.apache.zookeeper.data.ACL>getAcls()Returns a list of Zookeeper ACLs to be used for this lock.intprotected org.springframework.core.env.Environmentprotected intThis will retry any interactions with Zookeeper this many times.protected StringReturns LOCK_PREFIX + getLockName()protected Stringprotected Stringprotected StringThe lock name provided in the constructor.protected longIf an interaction with Zookeeper fails, it will retry and this will be the pause time, in millis, between retries.protected org.apache.zookeeper.ZooKeeperprotected voidCreates the appropriate folder(s) in Zookeeper if they don't already exist.protected booleanIf this is true, the wait time in millis will be incremented by itself each time a retry occurs, up to the retry count.voidlock()protected booleanlockInternally(long waitTime) Negative number means wait indefinitely (typically until anUnsupportedOperationExceptionis thrown.voidbooleantryLock()booleanvoidunlock()
-
Field Details
-
DEFAULT_BASE_FOLDER
This is the base folder that all locks will be written to in Zookeeper. The constructors require a lock path, which will be appended to this path.- See Also:
-
LOCK_PREFIX
This is the prefix of any lock entry. It is dzlck-. A lock file (node) will be dzlck-mylock, for example.- See Also:
-
-
Constructor Details
-
ReentrantDistributedZookeeperLock
-
ReentrantDistributedZookeeperLock
public ReentrantDistributedZookeeperLock(org.apache.zookeeper.ZooKeeper zk, String lockPath, String lockName, List<org.apache.zookeeper.data.ACL> acls) This constructor takes in theZooKeeper(non-nullable), the lock path (non-nullable and in the format of '/path/to/this/lock/folder`), and the lock name (non-nullable and non-empty string with no whitespaces, leading or trailing slashes, or special characters except '-' or '_').This
Lockwill, by default, participate in or be allowed to acquire a lock.The lockPath will be prepended with '/broadleaf/app/distributed-locks'.
- Parameters:
zk-lockPath-lockName-acls-
-
ReentrantDistributedZookeeperLock
public ReentrantDistributedZookeeperLock(org.apache.zookeeper.ZooKeeper zk, String lockPath, String lockName, boolean useDefaultBasePath, List<org.apache.zookeeper.data.ACL> acls) This constructor takes in theZooKeeper(non-nullable), the lock path (non-nullable and in the format of '/path/to/this/lock/folder`), and the lock name (non-nullable and non-empty string with no whitespaces, leading or trailing slashes, or special characters except '-' or '_').This
Lockwill, by default, participate in or be allowed to acquire a lock.If use defaultBasePath is true, then the lockPath will be prepended with '/broadleaf/app/distributed-locks'.
- Parameters:
zk-lockPath-lockName-acls-
-
ReentrantDistributedZookeeperLock
public ReentrantDistributedZookeeperLock(org.apache.zookeeper.ZooKeeper zk, String lockPath, String lockName, org.springframework.core.env.Environment env, List<org.apache.zookeeper.data.ACL> acls) This constructor takes in theZookeeper(non-nullable), the lock path (non-nullable and in the format of '/path/to/this/lock/folder`), the lock name (non-nullable and non-empty string with no whitespaces, leading or trailing slashes, or special characters except '-' or '_'), and anEnvironmentobject, which can be null.This
Lockwill, by default, participate in or be allowed to acquire a lock if theEnvironmentargument is null or if the 'org.broadleafcommerce.core.util.lock.DistributedLock.${lockName}.canParticipate' property is not set or is set to false.The lockPath will be prepended with '/broadleaf/app/distributed-locks'.
- Parameters:
zk-lockPath-lockName-env-acls-
-
ReentrantDistributedZookeeperLock
public ReentrantDistributedZookeeperLock(org.apache.zookeeper.ZooKeeper zk, String lockPath, String lockName, org.springframework.core.env.Environment env, boolean useDefaultBasePath, List<org.apache.zookeeper.data.ACL> acls) This constructor takes in theZooKeeper(non-nullable), the lock path (non-nullable and in the format of '/path/to/this/lock/folder`), the lock name (non-nullable and non-empty string with no whitespaces, leading or trailing slashes, or special characters except '-' or '_'), and anEnvironmentobject, which can be null.This
Lockwill, by default, participate in or be allowed to acquire a lock if theEnvironmentargument is null or if the 'org.broadleafcommerce.core.util.lock.DistributedLock.${lockName}.canParticipate' property is not set or is set to false.If use defaultBasePath is true, then the lockPath will be prepended with '/broadleaf/app/distributed-locks'.
- Parameters:
zk-lockPath-lockName-env-useDefaultBasePath-acls-
-
-
Method Details
-
lock
public void lock() -
unlock
public void unlock() -
lockInterruptibly
- Specified by:
lockInterruptiblyin interfaceLock- Throws:
InterruptedException
-
tryLock
public boolean tryLock() -
tryLock
- Specified by:
tryLockin interfaceLock- Throws:
InterruptedException
-
newCondition
- Specified by:
newConditionin interfaceLock
-
lockInternally
Negative number means wait indefinitely (typically until anUnsupportedOperationExceptionis thrown. Zero (0) means don't wait. Positive number means wait for that number of millis.- Parameters:
waitTime-- Throws:
InterruptedException
-
initialize
protected void initialize()Creates the appropriate folder(s) in Zookeeper if they don't already exist. -
canParticipate
public boolean canParticipate()Allows one to disable this locking mechanism via the 'org.broadleafcommerce.core.util.lock.DistributedLock.canParticipate' (globally) or the 'org.broadleafcommerce.core.util.lock.DistributedLock.${lockName}.canParticipate' property. Both are true by default. If either of these properties are set to false, then this lock will never be obtained. This allows only certain environments or certain nodes (JVMs) to obtain the lock while preventing others. Assuming theEnvironmentis not null, this first checks the more specific 'org.broadleafcommerce.core.util.lock.DistributedLock.${lockName}.canParticipate' property. If that is true or null, then it checks the more global 'org.broadleafcommerce.core.util.lock.DistributedLock.canParticipate'. If that is true or null then it returns true.By default, this method returns true if the
Environmentis null or if both properties are null.If this method returns false, then the locking mechanisms and semantics will continue to work. However, the lock will always be locked and will never allow the acquisition of a lock.
- Specified by:
canParticipatein interfaceDistributedLock- Returns:
-
currentThreadHoldsLock
public boolean currentThreadHoldsLock()Description copied from interface:DistributedLockIndicates if the current thread holds the lock.- Specified by:
currentThreadHoldsLockin interfaceDistributedLock- Returns:
-
getCurrentThreadLockPermits
public int getCurrentThreadLockPermits() -
getZookeeperClient
protected org.apache.zookeeper.ZooKeeper getZookeeperClient() -
getEnvironment
protected org.springframework.core.env.Environment getEnvironment() -
getLockName
The lock name provided in the constructor.- Returns:
-
getFullLockName
Returns LOCK_PREFIX + getLockName()E.g. dzlck-myLock
- Returns:
-
getLockFolderPath
-
getFailureRetries
protected int getFailureRetries()This will retry any interactions with Zookeeper this many times. Must be a positive number. Zero (0) is OK.- Returns:
-
getRetryWaitTime
protected long getRetryWaitTime()If an interaction with Zookeeper fails, it will retry and this will be the pause time, in millis, between retries.- Returns:
-
isAdditiveWaitTtimes
protected boolean isAdditiveWaitTtimes()If this is true, the wait time in millis will be incremented by itself each time a retry occurs, up to the retry count.So, if the retries is 5 and the retryWaitTime is 100, then the max amount of time that the system will wait is 1500: 100 + 200 + 300 + 400 + 500, not including any wait time associated with I/O or execution of the Operation.
- Returns:
-
getLockAccessPropertyName
-
getAcls
Returns a list of Zookeeper ACLs to be used for this lock.- Returns:
-