You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by hu...@apache.org on 2020/04/01 22:47:44 UTC

[helix] 49/49: Fix getClusters() in ZKHelixAdmin for multi-zk mode (#916)

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

hulee pushed a commit to branch zooscalability
in repository https://gitbox.apache.org/repos/asf/helix.git

commit be20362fd3bb2a1259a89f1c993f9220ed0dbabd
Author: Hunter Lee <hu...@linkedin.com>
AuthorDate: Mon Mar 30 10:31:14 2020 -0700

    Fix getClusters() in ZKHelixAdmin for multi-zk mode (#916)
    
    This PR fixes the logic in getClusters() so that it works in a multi-zk environment. On multi-zk mode, the API will query for raw routing data from MSDS and produce a list of all clusters in the namespace. The behavior for single-zk mode remains the same for backward-compatibility.
---
 .../java/org/apache/helix/SystemPropertyKeys.java  |  7 +++++
 .../org/apache/helix/manager/zk/ZKHelixAdmin.java  | 36 +++++++++++++++++-----
 .../multizk/TestMultiZkHelixJavaApis.java          |  8 +++++
 .../helix/msdcommon/util/ZkValidationUtil.java     |  3 +-
 .../zookeeper/api/client/RealmAwareZkClient.java   |  8 +++++
 .../zookeeper/impl/client/DedicatedZkClient.java   | 14 +++++++++
 .../zookeeper/impl/client/FederatedZkClient.java   | 12 ++++++++
 .../zookeeper/impl/client/SharedZkClient.java      | 14 +++++++++
 8 files changed, 94 insertions(+), 8 deletions(-)

diff --git a/helix-common/src/main/java/org/apache/helix/SystemPropertyKeys.java b/helix-common/src/main/java/org/apache/helix/SystemPropertyKeys.java
index a40dbe9..9870cdd 100644
--- a/helix-common/src/main/java/org/apache/helix/SystemPropertyKeys.java
+++ b/helix-common/src/main/java/org/apache/helix/SystemPropertyKeys.java
@@ -19,6 +19,9 @@ package org.apache.helix;
  * under the License.
  */
 
+import org.apache.helix.msdcommon.constant.MetadataStoreRoutingConstants;
+
+
 public class SystemPropertyKeys {
   // Task Driver
   public static final String TASK_CONFIG_LIMITATION = "helixTask.configsLimitation";
@@ -63,4 +66,8 @@ public class SystemPropertyKeys {
 
   // Multi-ZK mode enable/disable flag
   public static final String MULTI_ZK_ENABLED = "helix.multiZkEnabled";
+
+  // System Property Metadata Store Directory Server endpoint key
+  public static final String MSDS_SERVER_ENDPOINT_KEY =
+      MetadataStoreRoutingConstants.MSDS_SERVER_ENDPOINT_KEY;
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
index 65e6ec0..fad5005 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
@@ -36,6 +36,7 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
@@ -83,6 +84,7 @@ import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
 import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.zookeeper.impl.client.FederatedZkClient;
 import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+import org.apache.helix.zookeeper.util.HttpRoutingDataReader;
 import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.apache.helix.zookeeper.zkclient.exception.ZkException;
 import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
@@ -933,17 +935,37 @@ public class ZKHelixAdmin implements HelixAdmin {
 
   @Override
   public List<String> getClusters() {
+    List<String> zkToplevelPaths;
+
     if (Boolean.getBoolean(SystemPropertyKeys.MULTI_ZK_ENABLED)
         || _zkClient instanceof FederatedZkClient) {
-      String errMsg =
-          "getClusters() is not supported in multi-realm mode! Use Metadata Store Directory Service instead!";
-      LOG.error(errMsg);
-      throw new UnsupportedOperationException(errMsg);
+      // If on multi-zk mode, we retrieve cluster information from Metadata Store Directory Service.
+      Map<String, List<String>> realmToShardingKeys;
+      String msdsEndpoint = _zkClient.getRealmAwareZkConnectionConfig().getMsdsEndpoint();
+      try {
+        if (msdsEndpoint == null || msdsEndpoint.isEmpty()) {
+          realmToShardingKeys = HttpRoutingDataReader.getRawRoutingData();
+        } else {
+          realmToShardingKeys = HttpRoutingDataReader.getRawRoutingData(msdsEndpoint);
+        }
+      } catch (IOException e) {
+        throw new HelixException(
+            "ZKHelixAdmin: Failed to read raw routing data from Metadata Store Directory Service! MSDS endpoint used: "
+                + msdsEndpoint, e);
+      }
+      if (realmToShardingKeys == null || realmToShardingKeys.isEmpty()) {
+        return Collections.emptyList();
+      }
+      // Preceding "/"s are removed: e.g.) "/CLUSTER-SHARDING-KEY" -> "CLUSTER-SHARDING-KEY"
+      zkToplevelPaths = realmToShardingKeys.values().stream().flatMap(List::stream)
+          .map(shardingKey -> shardingKey.substring(1)).collect(Collectors.toList());
+    } else {
+      // single-zk mode
+      zkToplevelPaths = _zkClient.getChildren("/");
     }
 
-    List<String> zkToplevelPathes = _zkClient.getChildren("/");
-    List<String> result = new ArrayList<String>();
-    for (String pathName : zkToplevelPathes) {
+    List<String> result = new ArrayList<>();
+    for (String pathName : zkToplevelPaths) {
       if (ZKUtil.isClusterSetup(pathName, _zkClient)) {
         result.add(pathName);
       }
diff --git a/helix-core/src/test/java/org/apache/helix/integration/multizk/TestMultiZkHelixJavaApis.java b/helix-core/src/test/java/org/apache/helix/integration/multizk/TestMultiZkHelixJavaApis.java
index be8bb0b..d2a90a1 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/multizk/TestMultiZkHelixJavaApis.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/multizk/TestMultiZkHelixJavaApis.java
@@ -473,4 +473,12 @@ public class TestMultiZkHelixJavaApis {
       Assert.assertEquals(context.getWorkflowState(), wfStateFromTaskDriver);
     }
   }
+
+  /**
+   * This method tests that ZKHelixAdmin::getClusters() works in multi-zk environment.
+   */
+  @Test(dependsOnMethods = "testTaskFramework")
+  public void testGetAllClusters() {
+    Assert.assertEquals(new HashSet<>(_zkHelixAdmin.getClusters()), new HashSet<>(CLUSTER_LIST));
+  }
 }
diff --git a/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/util/ZkValidationUtil.java b/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/util/ZkValidationUtil.java
index ab8258d..0b23431 100644
--- a/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/util/ZkValidationUtil.java
+++ b/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/util/ZkValidationUtil.java
@@ -28,12 +28,13 @@ public class ZkValidationUtil {
    * /abc
    * /abc/abc/abc/abc
    * /abc/localhost:1234
+   * /abc/def.hil
    * Invalid matches:
    * null or empty string
    * /abc/
    * /abc/abc/abc/abc/
    **/
   public static boolean isPathValid(String path) {
-    return path.matches("^/|(/[\\w?:-]+)+$");
+    return path.matches("^/|(/[\\w?[$&+,:;=?@#|'<>.^*()%!-]-]+)+$");
   }
 }
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/RealmAwareZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/RealmAwareZkClient.java
index ee8c8e3..22f3678 100644
--- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/RealmAwareZkClient.java
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/RealmAwareZkClient.java
@@ -266,6 +266,14 @@ public interface RealmAwareZkClient {
 
   PathBasedZkSerializer getZkSerializer();
 
+  default RealmAwareZkConnectionConfig getRealmAwareZkConnectionConfig() {
+    throw new UnsupportedOperationException("getRealmAwareZkClientConfig() is not supported!");
+  }
+
+  default RealmAwareZkClientConfig getRealmAwareZkClientConfig() {
+    throw new UnsupportedOperationException("getRealmAwareZkClientConfig() is not supported!");
+  }
+
   /**
    * A class that wraps a default implementation of
    * {@link IZkStateListener}, which means this listener
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/DedicatedZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/DedicatedZkClient.java
index beeb085..09b66fb 100644
--- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/DedicatedZkClient.java
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/DedicatedZkClient.java
@@ -59,6 +59,8 @@ public class DedicatedZkClient implements RealmAwareZkClient {
   private final ZkClient _rawZkClient;
   private final MetadataStoreRoutingData _metadataStoreRoutingData;
   private final String _zkRealmShardingKey;
+  private final RealmAwareZkClient.RealmAwareZkConnectionConfig _connectionConfig;
+  private final RealmAwareZkClient.RealmAwareZkClientConfig _clientConfig;
 
   /**
    * DedicatedZkClient connects to a single ZK realm and supports full ZkClient functionalities
@@ -77,6 +79,8 @@ public class DedicatedZkClient implements RealmAwareZkClient {
     if (clientConfig == null) {
       throw new IllegalArgumentException("RealmAwareZkClientConfig cannot be null!");
     }
+    _connectionConfig = connectionConfig;
+    _clientConfig = clientConfig;
 
     // Get the routing data from a static Singleton HttpRoutingDataReader
     String msdsEndpoint = connectionConfig.getMsdsEndpoint();
@@ -468,6 +472,16 @@ public class DedicatedZkClient implements RealmAwareZkClient {
     return _rawZkClient.getZkSerializer();
   }
 
+  @Override
+  public RealmAwareZkConnectionConfig getRealmAwareZkConnectionConfig() {
+    return _connectionConfig;
+  }
+
+  @Override
+  public RealmAwareZkClientConfig getRealmAwareZkClientConfig() {
+    return _clientConfig;
+  }
+
   /**
    * Checks whether the given path belongs matches the ZK path sharding key this DedicatedZkClient is designated to at initialization.
    * @param path
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java
index 1bfff66..1cebc90 100644
--- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java
@@ -74,6 +74,7 @@ public class FederatedZkClient implements RealmAwareZkClient {
       DedicatedZkClientFactory.class.getSimpleName();
 
   private final MetadataStoreRoutingData _metadataStoreRoutingData;
+  private final RealmAwareZkClient.RealmAwareZkConnectionConfig _connectionConfig;
   private final RealmAwareZkClient.RealmAwareZkClientConfig _clientConfig;
 
   // ZK realm -> ZkClient
@@ -102,6 +103,7 @@ public class FederatedZkClient implements RealmAwareZkClient {
     }
 
     _isClosed = false;
+    _connectionConfig = connectionConfig;
     _clientConfig = clientConfig;
     _pathBasedZkSerializer = clientConfig.getZkSerializer();
     _zkRealmToZkClientMap = new ConcurrentHashMap<>();
@@ -477,6 +479,16 @@ public class FederatedZkClient implements RealmAwareZkClient {
     return _pathBasedZkSerializer;
   }
 
+  @Override
+  public RealmAwareZkConnectionConfig getRealmAwareZkConnectionConfig() {
+    return _connectionConfig;
+  }
+
+  @Override
+  public RealmAwareZkClientConfig getRealmAwareZkClientConfig() {
+    return _clientConfig;
+  }
+
   private String create(final String path, final Object dataObject, final List<ACL> acl,
       final CreateMode mode, final String expectedSessionId) {
     if (mode.isEphemeral()) {
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java
index dd78aba..0989136 100644
--- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java
@@ -59,6 +59,8 @@ public class SharedZkClient implements RealmAwareZkClient {
   private final MetadataStoreRoutingData _metadataStoreRoutingData;
   private final String _zkRealmShardingKey;
   private final String _zkRealmAddress;
+  private final RealmAwareZkClient.RealmAwareZkConnectionConfig _connectionConfig;
+  private final RealmAwareZkClient.RealmAwareZkClientConfig _clientConfig;
 
   public SharedZkClient(RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig,
       RealmAwareZkClient.RealmAwareZkClientConfig clientConfig)
@@ -69,6 +71,8 @@ public class SharedZkClient implements RealmAwareZkClient {
     if (clientConfig == null) {
       throw new IllegalArgumentException("RealmAwareZkClientConfig cannot be null!");
     }
+    _connectionConfig = connectionConfig;
+    _clientConfig = clientConfig;
 
     // Get the routing data from a static Singleton HttpRoutingDataReader
     String msdsEndpoint = connectionConfig.getMsdsEndpoint();
@@ -499,6 +503,16 @@ public class SharedZkClient implements RealmAwareZkClient {
   }
 
   @Override
+  public RealmAwareZkConnectionConfig getRealmAwareZkConnectionConfig() {
+    return _connectionConfig;
+  }
+
+  @Override
+  public RealmAwareZkClientConfig getRealmAwareZkClientConfig() {
+    return _clientConfig;
+  }
+
+  @Override
   public PathBasedZkSerializer getZkSerializer() {
     return _innerSharedZkClient.getZkSerializer();
   }