This is an automated email from the ASF dual-hosted git repository. dlmarion pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/accumulo.git
commit e46bee565340d99c33efac4cb3c0341e30c96df7 Merge: af4955d592 249fac277d Author: Dave Marion <[email protected]> AuthorDate: Tue Mar 10 12:37:08 2026 +0000 Merge branch '2.1' .../org/apache/accumulo/core/lock/ServiceLock.java | 152 ++++++++++++++------- .../apache/accumulo/test/lock/ServiceLockIT.java | 2 +- 2 files changed, 103 insertions(+), 51 deletions(-) diff --cc core/src/main/java/org/apache/accumulo/core/lock/ServiceLock.java index e841582fad,e52de4a036..4a007f1b32 --- a/core/src/main/java/org/apache/accumulo/core/lock/ServiceLock.java +++ b/core/src/main/java/org/apache/accumulo/core/lock/ServiceLock.java @@@ -47,10 -45,25 +47,29 @@@ import org.apache.zookeeper.data.Stat import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import com.google.common.base.Preconditions; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + + /** + * This class uses Sequential Ephemeral ZooKeeper Nodes + * (https://zookeeper.apache.org/doc/r3.9.5/zookeeperProgrammers.html#Sequence+Nodes+--+Unique+Naming) + * to implement locks for Accumulo server processes. This class will create an ephemeral sequential + * node under a base path using the prefix "zlock#" + UUID + "#". The ZooKeeper server will append a + * 10-digit zero-padded number to this prefix using a one-up counter. The base path could be an HA + * service like the Manager or a non-HA service like a TabletServer. + * + * When an instance of this class has the lowest counter number at the base path, then it has the + * lock. When an instance of this class does not have the lowest counter number, then it watches the + * node with the next lowest counter number. When the node that has the next lowest counter number + * is deleted, then this instance could acquire the lock. + * + * Instance of this class also place a Watcher on the base path node. If the base path Watcher + * receives a Session Expired event, then it calls lostLock which should end up halting the Accumulo + * server process. + */ +@SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", + justification = "Constructor validation is required for proper initialization") public class ServiceLock implements Watcher { private static final Logger LOG = LoggerFactory.getLogger(ServiceLock.class); @@@ -89,28 -119,56 +108,56 @@@ void failedToAcquireLock(Exception e); } + // the base path private final ServiceLockPath path; + - protected final ZooKeeper zooKeeper; + protected final ZooSession zooKeeper; + + // "zlock#" + UUID + "#" private final Prefix vmLockPrefix; + // A LockWatcher instance supplied to this class + // by the caller when trying to acquire the lock + // in ZooKeeper. This object does not represent + // a Watcher in ZooKeeper, but allows an instance + // of this class to communicate with the calling + // object by invoking callback methods. private LockWatcher lockWatcher; - private String lockNodeName; + + // A variable which is initially null, then set + // to the createdNodeName when the lock is acquired. + // This variable is set to null when this instance + // loses the lock. + private volatile String lockNodeName; + + // boolean to track if this instance has ever held the lock private volatile boolean lockWasAcquired; - private volatile boolean watchingParent; - private String createdNodeName; - private String watchingNodeName; + // boolean to track if there is a watcher on the base path. + private volatile boolean watchingBasePath; + + // Represents the name of the ephemeral sequential node that + // the ZooKeeper server created for us with the unique one-up + // counter. This variable is set to null when this instance + // acquires the lock. + private volatile String createdNodeName; + + // Represents the path of the ephemeral sequential node that + // has the next lowest counter value. An instance of this class + // will watch this node and will attempt to acquire the lock + // when this node is deleted. + private String watchingNodePath; - public ServiceLock(ZooKeeper zookeeper, ServiceLockPath path, UUID uuid) { + public ServiceLock(ZooSession zookeeper, ServiceLockPath path, UUID uuid) { this.zooKeeper = requireNonNull(zookeeper); this.path = requireNonNull(path); try { zooKeeper.exists(path.toString(), this); - watchingParent = true; + watchingBasePath = true; this.vmLockPrefix = new Prefix(ZLOCK_PREFIX + uuid.toString() + "#"); - } catch (Exception ex) { + } catch (KeeperException | InterruptedException ex) { LOG.error("Error setting initial watch", ex); - throw new RuntimeException(ex); + throw new IllegalStateException(ex); } } @@@ -145,12 -207,12 +196,12 @@@ } - public synchronized boolean tryLock(LockWatcher lw, byte[] data) + public synchronized boolean tryLock(LockWatcher lw, ServiceLockData lockData) throws KeeperException, InterruptedException { - LockWatcherWrapper lww = new LockWatcherWrapper(lw); + TryLockWatcherWrapper lww = new TryLockWatcherWrapper(lw); - lock(lww, data); + lock(lww, lockData); if (lww.acquiredLock) { return true; @@@ -273,16 -334,15 +321,16 @@@ List<String> children = validateAndSort(path, zooKeeper.getChildren(path.toString(), null)); - if (!children.contains(createdEphemeralNode)) { - LOG.error("Expected ephemeral node {} to be in the list of children {}", createdEphemeralNode, + if (!children.contains(createdNodeName)) { + LOG.error("Expected ephemeral node {} to be in the list of children {}", createdNodeName, children); - throw new RuntimeException("Lock attempt ephemeral node no longer exist " + createdNodeName); + throw new IllegalStateException( - "Lock attempt ephemeral node no longer exist " + createdEphemeralNode); ++ "Lock attempt ephemeral node no longer exist " + createdNodeName); } - if (children.get(0).equals(createdEphemeralNode)) { + if (children.get(0).equals(createdNodeName)) { LOG.debug("[{}] First candidate is my lock, acquiring...", vmLockPrefix); - if (!watchingParent) { + if (!watchingBasePath) { throw new IllegalStateException( "Can not acquire lock, no longer watching parent : " + path); }
