This is an automated email from the ASF dual-hosted git repository.

dlmarion pushed a commit to branch 2.1
in repository https://gitbox.apache.org/repos/asf/accumulo.git


The following commit(s) were added to refs/heads/2.1 by this push:
     new 7277daedd3 Added ZooZap and ServiceLock methods to delete ScanServer 
locks (#6073)
7277daedd3 is described below

commit 7277daedd31979b72d26d8e5a797e0a9af2e0ca0
Author: Dave Marion <[email protected]>
AuthorDate: Wed Feb 4 10:48:23 2026 -0500

    Added ZooZap and ServiceLock methods to delete ScanServer locks (#6073)
    
    The ScanServer locks in 2.1.x have a different format than
    the other locks and require different methods. The lock
    structure has been normalized in the planned version 4.0.
    
    Closes #6067
    
    
    
    Co-authored-by: Dom G. <[email protected]>
---
 assemble/bin/accumulo-cluster                      |  4 +-
 .../accumulo/core/fate/zookeeper/ServiceLock.java  | 38 ++++++++++++
 .../org/apache/accumulo/server/util/Admin.java     |  7 ++-
 .../org/apache/accumulo/server/util/ZooZap.java    | 21 +++++--
 .../org/apache/accumulo/server/util/AdminTest.java | 70 ++++++++++++++++++++++
 .../org/apache/accumulo/tserver/ScanServer.java    |  2 +-
 6 files changed, 133 insertions(+), 9 deletions(-)

diff --git a/assemble/bin/accumulo-cluster b/assemble/bin/accumulo-cluster
index 6e8b4e0df8..dc7711fd26 100755
--- a/assemble/bin/accumulo-cluster
+++ b/assemble/bin/accumulo-cluster
@@ -796,7 +796,7 @@ function prune() {
       read -r -a groups <<<"$ARG_SSERVER_GROUP"
     else
       # find all groups known in zookeeper, this will allow pruning entire 
groups that do not even exist in cluster.yaml
-      readarray -t groups < <(jq -r ".summaries.S_SERVER.resourceGroups | .[] 
" "$service_json")
+      readarray -t groups < <(jq -r ".summaries.S_SERVER.resourceGroups | keys 
| .[]?" "$service_json")
     fi
 
     for group in "${groups[@]}"; do
@@ -815,7 +815,7 @@ function prune() {
       read -r -a groups <<<"$ARG_COMPACTOR_GROUP"
     else
       # find all groups known in zookeeper, this will allow pruning entire 
groups that do not even exist in cluster.yaml
-      readarray -t groups < <(jq -r ".summaries.COMPACTOR.resourceGroups | .[] 
" "$service_json")
+      readarray -t groups < <(jq -r ".summaries.COMPACTOR.resourceGroups | 
keys | .[]?" "$service_json")
     fi
 
     for group in "${groups[@]}"; do
diff --git 
a/core/src/main/java/org/apache/accumulo/core/fate/zookeeper/ServiceLock.java 
b/core/src/main/java/org/apache/accumulo/core/fate/zookeeper/ServiceLock.java
index 42e57206e5..989dd718c6 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/fate/zookeeper/ServiceLock.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/fate/zookeeper/ServiceLock.java
@@ -18,6 +18,7 @@
  */
 package org.apache.accumulo.core.fate.zookeeper;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.requireNonNull;
 
 import java.util.ArrayList;
@@ -754,6 +755,43 @@ public class ServiceLock implements Watcher {
     }
   }
 
+  public static void deleteScanServerLocks(ZooReaderWriter zk, String zPath,
+      Predicate<HostAndPort> hostPortPredicate, Predicate<String> 
groupPredicate,
+      Consumer<String> messageOutput, Boolean dryRun) throws KeeperException, 
InterruptedException {
+
+    Objects.requireNonNull(zPath, "Lock path cannot be null");
+    Objects.requireNonNull(groupPredicate, "group predicate cannot be null");
+    if (!zk.exists(zPath)) {
+      throw new IllegalStateException("Path " + zPath + " does not exist");
+    }
+
+    List<String> servers = zk.getChildren(zPath);
+    if (servers.isEmpty()) {
+      throw new IllegalStateException("No server locks are held at " + zPath);
+    }
+
+    ZooKeeper z = zk.getZooKeeper();
+    for (String server : servers) {
+      if (hostPortPredicate.test(HostAndPort.fromString(server))) {
+        final String serverPath = zPath + "/" + server;
+        byte[] lockData = ServiceLock.getLockData(z, path(serverPath));
+        if (lockData == null) {
+          messageOutput.accept("Skipping server " + server + " as it's lock 
content is empty.");
+          continue;
+        }
+        String lockContent = new String(lockData, UTF_8);
+        String[] parts = lockContent.split(",");
+        if (parts.length == 2 && groupPredicate.test(parts[1])) {
+          messageOutput.accept("Deleting " + serverPath + " from zookeeper");
+          if (!dryRun) {
+            LOG.debug("Deleting all locks at path {} due to lock deletion", 
serverPath);
+            zk.recursiveDelete(serverPath, NodeMissingPolicy.SKIP);
+          }
+        }
+      }
+    }
+  }
+
   /**
    * This method will delete the top server lock for a given lock path
    *
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/util/Admin.java 
b/server/base/src/main/java/org/apache/accumulo/server/util/Admin.java
index c88b919ea8..b081922e6f 100644
--- a/server/base/src/main/java/org/apache/accumulo/server/util/Admin.java
+++ b/server/base/src/main/java/org/apache/accumulo/server/util/Admin.java
@@ -560,6 +560,7 @@ public class Admin implements KeywordExecutable {
         client -> client.shutdown(TraceUtil.traceInfo(), context.rpcCreds(), 
tabletServersToo));
   }
 
+  @SuppressWarnings("deprecation")
   private static void stopServers(final ServerContext context, List<String> 
servers,
       final boolean force)
       throws AccumuloException, AccumuloSecurityException, 
InterruptedException, KeeperException {
@@ -590,9 +591,11 @@ public class Admin implements KeywordExecutable {
         String tserversPath = Constants.ZROOT + "/" + iid + 
Constants.ZTSERVERS;
         ZooZap.removeLocks(zk, tserversPath, hostAndPort::contains, opts);
         String compactorsBasepath = Constants.ZROOT + "/" + iid + 
Constants.ZCOMPACTORS;
-        ZooZap.removeGroupedLocks(zk, compactorsBasepath, rg -> true, 
hostAndPort::contains, opts);
+        ZooZap.removeCompactorGroupedLocks(zk, compactorsBasepath, rg -> true,
+            hostAndPort::contains, opts);
         String sserversPath = Constants.ZROOT + "/" + iid + 
Constants.ZSSERVERS;
-        ZooZap.removeGroupedLocks(zk, sserversPath, rg -> true, 
hostAndPort::contains, opts);
+        ZooZap.removeScanServerGroupLocks(zk, sserversPath, 
hostAndPort::contains, rg -> true,
+            opts);
 
         String managerLockPath = Constants.ZROOT + "/" + iid + 
Constants.ZMANAGER_LOCK;
         ZooZap.removeSingletonLock(zk, managerLockPath, hostAndPort::contains, 
opts);
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/util/ZooZap.java 
b/server/base/src/main/java/org/apache/accumulo/server/util/ZooZap.java
index 3f8d1c0d6b..6954ba11bb 100644
--- a/server/base/src/main/java/org/apache/accumulo/server/util/ZooZap.java
+++ b/server/base/src/main/java/org/apache/accumulo/server/util/ZooZap.java
@@ -235,7 +235,8 @@ public class ZooZap implements KeywordExecutable {
     if (opts.zapCompactors) {
       String compactorsBasepath = Constants.ZROOT + "/" + iid + 
Constants.ZCOMPACTORS;
       try {
-        removeGroupedLocks(zoo, compactorsBasepath, groupPredicate, 
hostPortPredicate, opts);
+        removeCompactorGroupedLocks(zoo, compactorsBasepath, groupPredicate, 
hostPortPredicate,
+            opts);
       } catch (KeeperException | InterruptedException e) {
         log.error("Error deleting compactors from zookeeper", e);
       }
@@ -245,7 +246,11 @@ public class ZooZap implements KeywordExecutable {
     if (opts.zapScanServers) {
       String sserversPath = Constants.ZROOT + "/" + iid + Constants.ZSSERVERS;
       try {
-        removeGroupedLocks(zoo, sserversPath, groupPredicate, 
hostPortPredicate, opts);
+        if (opts.includeGroups == null) {
+          removeLocks(zoo, sserversPath, hostPortPredicate, opts);
+        } else {
+          removeScanServerGroupLocks(zoo, sserversPath, hostPortPredicate, 
groupPredicate, opts);
+        }
       } catch (KeeperException | InterruptedException e) {
         log.error("Error deleting scan server locks", e);
       }
@@ -263,8 +268,8 @@ public class ZooZap implements KeywordExecutable {
     }
   }
 
-  static void removeGroupedLocks(ZooReaderWriter zoo, String path, 
Predicate<String> groupPredicate,
-      Predicate<HostAndPort> hostPortPredicate, Opts opts)
+  static void removeCompactorGroupedLocks(ZooReaderWriter zoo, String path,
+      Predicate<String> groupPredicate, Predicate<HostAndPort> 
hostPortPredicate, Opts opts)
       throws KeeperException, InterruptedException {
     if (zoo.exists(path)) {
       List<String> groups = zoo.getChildren(path);
@@ -283,6 +288,14 @@ public class ZooZap implements KeywordExecutable {
     ServiceLock.deleteLocks(zoo, path, hostPortPredicate, m -> message(m, 
opts), opts.dryRun);
   }
 
+  @Deprecated(since = "2.1.5")
+  static void removeScanServerGroupLocks(ZooReaderWriter zoo, String path,
+      Predicate<HostAndPort> hostPortPredicate, Predicate<String> 
groupPredicate, Opts opts)
+      throws KeeperException, InterruptedException {
+    ServiceLock.deleteScanServerLocks(zoo, path, hostPortPredicate, 
groupPredicate,
+        m -> message(m, opts), opts.dryRun);
+  }
+
   static void removeSingletonLock(ZooReaderWriter zoo, String path,
       Predicate<HostAndPort> hostPortPredicate, Opts ops)
       throws KeeperException, InterruptedException {
diff --git 
a/server/base/src/test/java/org/apache/accumulo/server/util/AdminTest.java 
b/server/base/src/test/java/org/apache/accumulo/server/util/AdminTest.java
index 2e6754176e..15645f87bf 100644
--- a/server/base/src/test/java/org/apache/accumulo/server/util/AdminTest.java
+++ b/server/base/src/test/java/org/apache/accumulo/server/util/AdminTest.java
@@ -18,9 +18,11 @@
  */
 package org.apache.accumulo.server.util;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.UUID;
 
 import org.apache.accumulo.core.Constants;
@@ -28,6 +30,9 @@ import org.apache.accumulo.core.clientImpl.ClientContext;
 import org.apache.accumulo.core.data.InstanceId;
 import org.apache.accumulo.core.fate.zookeeper.ZooCache;
 import org.apache.accumulo.core.fate.zookeeper.ZooCache.ZcStat;
+import org.apache.accumulo.core.fate.zookeeper.ZooReaderWriter;
+import org.apache.accumulo.core.fate.zookeeper.ZooUtil.NodeMissingPolicy;
+import org.apache.zookeeper.ZooKeeper;
 import org.easymock.EasyMock;
 import org.junit.jupiter.api.Test;
 
@@ -93,4 +98,69 @@ public class AdminTest {
     EasyMock.verify(zc);
   }
 
+  /**
+   * SServer group filter should use lock data (UUID,group).
+   */
+  @SuppressWarnings("deprecation")
+  @Test
+  public void testSserverGroupFilterUsesLockData() throws Exception {
+
+    ZooReaderWriter zoo = EasyMock.createMock(ZooReaderWriter.class);
+    ZooKeeper zk = EasyMock.createMock(ZooKeeper.class);
+
+    String basePath = "/accumulo/iid/sservers";
+    String hostDefault = "host1:10000";
+    String hostOther = "host2:10001";
+    String zlock1 = "zlock#00000000-0000-0000-0000-aaaaaaaaaaaa#0000000001";
+    String zlock2 = "zlock#00000000-0000-0000-0000-bbbbbbbbbbbb#0000000001";
+
+    EasyMock.expect(zoo.exists(basePath)).andReturn(true);
+    EasyMock.expect(zoo.getChildren(basePath)).andReturn(List.of(hostDefault, 
hostOther));
+    EasyMock.expect(zoo.getZooKeeper()).andReturn(zk);
+    EasyMock.expect(zk.getChildren(basePath + "/" + hostDefault, 
null)).andReturn(List.of(zlock1));
+    EasyMock.expect(zk.getData(basePath + "/" + hostDefault + "/" + zlock1, 
false, null))
+        .andReturn((UUID.randomUUID().toString() + 
",default").getBytes(UTF_8));
+    EasyMock.expect(zk.getChildren(basePath + "/" + hostOther, 
null)).andReturn(List.of(zlock2));
+    EasyMock.expect(zk.getData(basePath + "/" + hostOther + "/" + zlock2, 
false, null))
+        .andReturn((UUID.randomUUID().toString() + ",rg1").getBytes(UTF_8));
+
+    zoo.recursiveDelete(basePath + "/" + hostDefault, NodeMissingPolicy.SKIP);
+    EasyMock.expectLastCall();
+
+    EasyMock.replay(zoo, zk);
+
+    ZooZap.Opts opts = new ZooZap.Opts();
+    ZooZap.removeScanServerGroupLocks(zoo, basePath, hp -> true, 
"default"::equals, opts);
+
+    EasyMock.verify(zoo, zk);
+
+  }
+
+  /**
+   * SServer cleanup without group filter should delete all host nodes.
+   */
+  @Test
+  public void testSserverDeleteAllNoGroupFilter() throws Exception {
+    ZooReaderWriter zoo = EasyMock.createMock(ZooReaderWriter.class);
+
+    String basePath = "/accumulo/iid/sservers";
+    String host1 = "host1:10000";
+    String host2 = "host2:10001";
+
+    EasyMock.expect(zoo.exists(basePath)).andReturn(true);
+    EasyMock.expect(zoo.getChildren(basePath)).andReturn(List.of(host1, 
host2));
+
+    zoo.recursiveDelete(basePath + "/" + host1, NodeMissingPolicy.SKIP);
+    EasyMock.expectLastCall();
+
+    zoo.recursiveDelete(basePath + "/" + host2, NodeMissingPolicy.SKIP);
+    EasyMock.expectLastCall();
+
+    EasyMock.replay(zoo);
+
+    ZooZap.Opts opts = new ZooZap.Opts();
+    ZooZap.removeLocks(zoo, basePath, hp -> true, opts);
+
+    EasyMock.verify(zoo);
+  }
 }
diff --git 
a/server/tserver/src/main/java/org/apache/accumulo/tserver/ScanServer.java 
b/server/tserver/src/main/java/org/apache/accumulo/tserver/ScanServer.java
index ec89e668af..83f57347b1 100644
--- a/server/tserver/src/main/java/org/apache/accumulo/tserver/ScanServer.java
+++ b/server/tserver/src/main/java/org/apache/accumulo/tserver/ScanServer.java
@@ -328,7 +328,7 @@ public class ScanServer extends AbstractServer
   }
 
   /**
-   * Set up nodes and locks in ZooKeeper for this Compactor
+   * Set up nodes and locks in ZooKeeper for this ScanServer
    */
   private ServiceLock announceExistence() {
     ZooReaderWriter zoo = getContext().getZooReaderWriter();

Reply via email to