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:41 UTC

[helix] 46/49: Use Java Generics and inheritance to reduce duplicate code in Helix API Builders (#899)

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 503e589caa233ef1f44ffad21be13ac1d142084c
Author: Hunter Lee <hu...@linkedin.com>
AuthorDate: Fri Mar 20 21:28:38 2020 -0700

    Use Java Generics and inheritance to reduce duplicate code in Helix API Builders (#899)
    
    This PR removes duplicate logic and refactors the ZK helix API Builder logic into one single public abstract class so that other Builders can inherit from it. It makes use of Builder inheritance and Java Generics. This PR promotes code reuse and better craftsmanship.
---
 .../main/java/org/apache/helix/ConfigAccessor.java |  96 +---------
 .../manager/zk/GenericBaseDataAccessorBuilder.java | 145 +++++++++++++++
 .../helix/manager/zk/GenericZkHelixApiBuilder.java | 139 ++++++++++++++
 .../org/apache/helix/manager/zk/ZKHelixAdmin.java  |  94 +---------
 .../helix/manager/zk/ZkBaseDataAccessor.java       | 173 +++--------------
 .../helix/manager/zk/ZkCacheBaseDataAccessor.java  | 204 ++-------------------
 .../java/org/apache/helix/tools/ClusterSetup.java  |  88 +--------
 .../BestPossibleExternalViewVerifier.java          |  39 ++--
 .../ClusterVerifiers/ClusterLiveNodesVerifier.java |  30 +--
 .../StrictMatchExternalViewVerifier.java           |  37 +---
 .../ClusterVerifiers/ZkHelixClusterVerifier.java   | 103 +++++------
 .../zookeeper/api/client/RealmAwareZkClient.java   |   6 +
 .../helix/zookeeper/api/client/ZkClientType.java   |  42 +++++
 13 files changed, 469 insertions(+), 727 deletions(-)

diff --git a/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java b/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java
index d0b3bba..a92f91c 100644
--- a/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
+import org.apache.helix.manager.zk.GenericZkHelixApiBuilder;
 import org.apache.helix.manager.zk.ZKUtil;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ConfigScope;
@@ -82,32 +83,9 @@ public class ConfigAccessor {
   // This is used for close() to determine how ConfigAccessor should close the underlying ZkClient
   private final boolean _usesExternalZkClient;
 
-  /**
-   * Constructor that creates a realm-aware ConfigAccessor using a builder.
-   * @param builder
-   */
-  private ConfigAccessor(Builder builder) {
-    switch (builder._realmMode) {
-      case MULTI_REALM:
-        try {
-          _zkClient = new FederatedZkClient(builder._realmAwareZkConnectionConfig,
-              builder._realmAwareZkClientConfig.setZkSerializer(new ZNRecordSerializer()));
-        } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
-          throw new HelixException("Failed to create ConfigAccessor!", e);
-        }
-        break;
-      case SINGLE_REALM:
-        // Create a HelixZkClient: Use a SharedZkClient because ConfigAccessor does not need to do
-        // ephemeral operations
-        _zkClient = SharedZkClientFactory.getInstance()
-            .buildZkClient(new HelixZkClient.ZkConnectionConfig(builder._zkAddress),
-                builder._realmAwareZkClientConfig.createHelixZkClientConfig()
-                    .setZkSerializer(new ZNRecordSerializer()));
-        break;
-      default:
-        throw new HelixException("Invalid RealmMode given: " + builder._realmMode);
-    }
-    _usesExternalZkClient = false;
+  private ConfigAccessor(RealmAwareZkClient zkClient, boolean usesExternalZkClient) {
+    _zkClient = zkClient;
+    _usesExternalZkClient = usesExternalZkClient;
   }
 
   /**
@@ -932,73 +910,15 @@ public class ConfigAccessor {
     }
   }
 
-  public static class Builder {
-    private String _zkAddress;
-    private RealmAwareZkClient.RealmMode _realmMode;
-    private RealmAwareZkClient.RealmAwareZkConnectionConfig _realmAwareZkConnectionConfig;
-    private RealmAwareZkClient.RealmAwareZkClientConfig _realmAwareZkClientConfig;
-
+  public static class Builder extends GenericZkHelixApiBuilder<Builder> {
     public Builder() {
     }
 
-    public Builder setZkAddress(String zkAddress) {
-      _zkAddress = zkAddress;
-      return this;
-    }
-
-    public Builder setRealmMode(RealmAwareZkClient.RealmMode realmMode) {
-      _realmMode = realmMode;
-      return this;
-    }
-
-    public Builder setRealmAwareZkConnectionConfig(
-        RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
-      _realmAwareZkConnectionConfig = realmAwareZkConnectionConfig;
-      return this;
-    }
-
-    public Builder setRealmAwareZkClientConfig(
-        RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
-      _realmAwareZkClientConfig = realmAwareZkClientConfig;
-      return this;
-    }
-
     public ConfigAccessor build() {
       validate();
-      return new ConfigAccessor(this);
-    }
-
-    /**
-     * Validate the given parameters before creating an instance of ConfigAccessor.
-     */
-    private void validate() {
-      // Resolve RealmMode based on other parameters
-      boolean isZkAddressSet = _zkAddress != null && !_zkAddress.isEmpty();
-      if (_realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM && !isZkAddressSet) {
-        throw new HelixException(
-            "ConfigAccessor: RealmMode cannot be single-realm without a valid ZkAddress set!");
-      }
-      if (_realmMode == RealmAwareZkClient.RealmMode.MULTI_REALM && isZkAddressSet) {
-        throw new HelixException(
-            "ConfigAccessor: You cannot set the ZkAddress on multi-realm mode!");
-      }
-
-      if (_realmMode == null) {
-        _realmMode = isZkAddressSet ? RealmAwareZkClient.RealmMode.SINGLE_REALM
-            : RealmAwareZkClient.RealmMode.MULTI_REALM;
-      }
-
-      // Resolve RealmAwareZkClientConfig
-      if (_realmAwareZkClientConfig == null) {
-        _realmAwareZkClientConfig = new RealmAwareZkClient.RealmAwareZkClientConfig();
-      }
-
-      // Resolve RealmAwareZkConnectionConfig
-      if (_realmAwareZkConnectionConfig == null) {
-        // If not set, create a default one
-        _realmAwareZkConnectionConfig =
-            new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder().build();
-      }
+      return new ConfigAccessor(
+          createZkClient(_realmMode, _realmAwareZkConnectionConfig, _realmAwareZkClientConfig,
+              _zkAddress), false);
     }
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/GenericBaseDataAccessorBuilder.java b/helix-core/src/main/java/org/apache/helix/manager/zk/GenericBaseDataAccessorBuilder.java
new file mode 100644
index 0000000..d19ebb1
--- /dev/null
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/GenericBaseDataAccessorBuilder.java
@@ -0,0 +1,145 @@
+package org.apache.helix.manager.zk;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.helix.HelixException;
+import org.apache.helix.msdcommon.exception.InvalidRoutingDataException;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
+import org.apache.helix.zookeeper.api.client.ZkClientType;
+import org.apache.helix.zookeeper.impl.client.FederatedZkClient;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+
+
+/**
+ * GenericBaseDataAccessorBuilder serves as the abstract parent class for Builders used by
+ * BaseDataAccessor APIs that create ZK connections. By having this class, we promote code-reuse.
+ * @param <B>
+ */
+public class GenericBaseDataAccessorBuilder<B extends GenericBaseDataAccessorBuilder<B>> extends GenericZkHelixApiBuilder<B> {
+  /** ZK-based BaseDataAccessor-specific parameter **/
+  private ZkClientType _zkClientType;
+
+  /**
+   * Sets the ZkClientType.
+   * If this is set to either DEDICATED or SHARED, this accessor will be created on
+   * single-realm mode.
+   * If this is set to FEDERATED, multi-realm mode will be used.
+   * @param zkClientType
+   * @return
+   */
+  public B setZkClientType(ZkBaseDataAccessor.ZkClientType zkClientType) {
+    return setZkClientType(Enum.valueOf(ZkClientType.class, zkClientType.name()));
+  }
+
+  public B setZkClientType(ZkClientType zkClientType) {
+    _zkClientType = zkClientType;
+    return self();
+  }
+
+  /**
+   * Validates the given parameters before building an instance of ZkBaseDataAccessor.
+   */
+  @Override
+  protected void validate() {
+    super.validate();
+    validateZkClientType(_zkClientType, _realmMode);
+  }
+
+  /**
+   * This method contains construction logic for ZK-based BaseDataAccessor
+   * implementations.
+   * It uses an implementation of GenericBaseDataAccessorBuilder to construct the right
+   * RealmAwareZkClient based on a host of configs provided in the Builder.
+   * @return RealmAwareZkClient
+   */
+  @Override
+  protected RealmAwareZkClient createZkClient(RealmAwareZkClient.RealmMode realmMode,
+      RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig,
+      RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, String zkAddress) {
+    RealmAwareZkClient zkClient;
+    switch (realmMode) {
+      case MULTI_REALM:
+        try {
+          if (_zkClientType == ZkClientType.DEDICATED) {
+            // Use a realm-aware dedicated zk client
+            zkClient = DedicatedZkClientFactory.getInstance()
+                .buildZkClient(connectionConfig, clientConfig);
+          } else if (_zkClientType == ZkClientType.SHARED) {
+            // Use a realm-aware shared zk client
+            zkClient =
+                SharedZkClientFactory.getInstance().buildZkClient(connectionConfig, clientConfig);
+          } else {
+            zkClient = new FederatedZkClient(connectionConfig, clientConfig);
+          }
+        } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
+          throw new HelixException("Not able to connect on multi-realm mode.", e);
+        }
+        break;
+
+      case SINGLE_REALM:
+        // Create a HelixZkClient: Use a SharedZkClient because ZkBaseDataAccessor does not need to
+        // do ephemeral operations.
+        if (_zkClientType == ZkClientType.DEDICATED) {
+          // If DEDICATED, then we use a dedicated HelixZkClient because we must support ephemeral
+          // operations
+          zkClient = DedicatedZkClientFactory.getInstance()
+              .buildZkClient(new HelixZkClient.ZkConnectionConfig(zkAddress),
+                  clientConfig.createHelixZkClientConfig());
+        } else {
+          zkClient = SharedZkClientFactory.getInstance()
+              .buildZkClient(new HelixZkClient.ZkConnectionConfig(zkAddress),
+                  clientConfig.createHelixZkClientConfig());
+          zkClient
+              .waitUntilConnected(HelixZkClient.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);
+        }
+        break;
+      default:
+        throw new HelixException("Invalid RealmMode given: " + realmMode);
+    }
+    return zkClient;
+  }
+
+  /**
+   * Validate ZkClientType based on RealmMode.
+   * @param zkClientType
+   * @param realmMode
+   */
+  private void validateZkClientType(ZkClientType zkClientType,
+      RealmAwareZkClient.RealmMode realmMode) {
+    boolean isZkClientTypeSet = zkClientType != null;
+    // If ZkClientType is set, RealmMode must either be single-realm or not set.
+    if (isZkClientTypeSet && realmMode == RealmAwareZkClient.RealmMode.MULTI_REALM) {
+      throw new HelixException("ZkClientType cannot be set on multi-realm mode!");
+    }
+    // If ZkClientType is not set and realmMode is single-realm, default to SHARED
+    if (!isZkClientTypeSet && realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM) {
+      zkClientType = ZkClientType.SHARED;
+    }
+    if (realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM
+        && zkClientType == ZkClientType.FEDERATED) {
+      throw new HelixException("FederatedZkClient cannot be set on single-realm mode!");
+    }
+  }
+}
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/GenericZkHelixApiBuilder.java b/helix-core/src/main/java/org/apache/helix/manager/zk/GenericZkHelixApiBuilder.java
new file mode 100644
index 0000000..f6015c4
--- /dev/null
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/GenericZkHelixApiBuilder.java
@@ -0,0 +1,139 @@
+package org.apache.helix.manager.zk;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.helix.HelixException;
+import org.apache.helix.msdcommon.exception.InvalidRoutingDataException;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
+import org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer;
+import org.apache.helix.zookeeper.impl.client.FederatedZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+
+
+/**
+ * GenericZkHelixApiBuilder serves as the abstract parent class for Builders used by Helix Java APIs
+ * that create ZK connections. By having this class, we reduce duplicate code as much as possible.
+ * @param <B>
+ */
+public abstract class GenericZkHelixApiBuilder<B extends GenericZkHelixApiBuilder<B>> {
+  protected String _zkAddress;
+  protected RealmAwareZkClient.RealmMode _realmMode;
+  protected RealmAwareZkClient.RealmAwareZkConnectionConfig _realmAwareZkConnectionConfig;
+  protected RealmAwareZkClient.RealmAwareZkClientConfig _realmAwareZkClientConfig;
+
+  public B setZkAddress(String zkAddress) {
+    _zkAddress = zkAddress;
+    return self();
+  }
+
+  public B setRealmMode(RealmAwareZkClient.RealmMode realmMode) {
+    _realmMode = realmMode;
+    return self();
+  }
+
+  public B setRealmAwareZkConnectionConfig(
+      RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
+    _realmAwareZkConnectionConfig = realmAwareZkConnectionConfig;
+    return self();
+  }
+
+  public B setRealmAwareZkClientConfig(
+      RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
+    _realmAwareZkClientConfig = realmAwareZkClientConfig;
+    return self();
+  }
+
+  /**
+   * Validates the given Builder parameters using a generic validation logic.
+   */
+  protected void validate() {
+    // Resolve RealmMode based on whether ZK address has been set
+    boolean isZkAddressSet = _zkAddress != null && !_zkAddress.isEmpty();
+    if (_realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM && !isZkAddressSet) {
+      throw new HelixException("RealmMode cannot be single-realm without a valid ZkAddress set!");
+    }
+    if (_realmMode == RealmAwareZkClient.RealmMode.MULTI_REALM && isZkAddressSet) {
+      throw new HelixException("ZkAddress cannot be set on multi-realm mode!");
+    }
+    if (_realmMode == null) {
+      _realmMode = isZkAddressSet ? RealmAwareZkClient.RealmMode.SINGLE_REALM
+          : RealmAwareZkClient.RealmMode.MULTI_REALM;
+    }
+
+    initializeConfigsIfNull();
+  }
+
+  /**
+   * Initializes Realm-aware ZkConnection and ZkClient configs if they haven't been set.
+   */
+  protected void initializeConfigsIfNull() {
+    // Resolve all default values
+    if (_realmAwareZkConnectionConfig == null) {
+      _realmAwareZkConnectionConfig =
+          new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder().build();
+    }
+
+    // For Helix APIs, ZNRecord should be the default data model
+    if (_realmAwareZkClientConfig == null) {
+      _realmAwareZkClientConfig = new RealmAwareZkClient.RealmAwareZkClientConfig()
+          .setZkSerializer(new ZNRecordSerializer());
+    }
+  }
+
+  /**
+   * Creates a RealmAwareZkClient based on the parameters set.
+   * To be used in Helix ZK APIs' constructors: ConfigAccessor, ClusterSetup, ZKHelixAdmin
+   * @return
+   */
+  protected RealmAwareZkClient createZkClient(RealmAwareZkClient.RealmMode realmMode,
+      RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig,
+      RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, String zkAddress) {
+    switch (realmMode) {
+      case MULTI_REALM:
+        try {
+          return new FederatedZkClient(connectionConfig,
+              clientConfig.setZkSerializer(new ZNRecordSerializer()));
+        } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
+          throw new HelixException("Failed to create FederatedZkClient!", e);
+        }
+      case SINGLE_REALM:
+        // Create a HelixZkClient: Use a SharedZkClient because ClusterSetup does not need to do
+        // ephemeral operations
+        return SharedZkClientFactory.getInstance()
+            .buildZkClient(new HelixZkClient.ZkConnectionConfig(zkAddress),
+                clientConfig.createHelixZkClientConfig().setZkSerializer(new ZNRecordSerializer()));
+      default:
+        throw new HelixException("Invalid RealmMode given: " + realmMode);
+    }
+  }
+
+  /**
+   * Returns an instance of a subclass-Builder in order to reduce duplicate code.
+   * SuppressWarnings is used to rid of IDE warnings.
+   * @return an instance of a subclass-Builder. E.g.) ConfigAccessor.Builder
+   */
+  @SuppressWarnings("unchecked")
+  final B self() {
+    return (B) this;
+  }
+}
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 5804dd9..8cc327a 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
@@ -160,31 +160,10 @@ public class ZKHelixAdmin implements HelixAdmin {
     _usesExternalZkClient = false;
   }
 
-  private ZKHelixAdmin(Builder builder) {
-    RealmAwareZkClient zkClient;
-    switch (builder.realmMode) {
-      case MULTI_REALM:
-        try {
-          zkClient = new FederatedZkClient(builder.realmAwareZkConnectionConfig,
-              builder.realmAwareZkClientConfig);
-        } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
-          throw new HelixException("Not able to connect on multi-realm mode.", e);
-        }
-        break;
-      case SINGLE_REALM:
-        // Create a HelixZkClient: Use a SharedZkClient because ZKHelixAdmin does not need to do
-        // ephemeral operations
-        zkClient = SharedZkClientFactory.getInstance()
-            .buildZkClient(new HelixZkClient.ZkConnectionConfig(builder.zkAddress),
-                builder.realmAwareZkClientConfig.createHelixZkClientConfig());
-        break;
-      default:
-        throw new HelixException("Invalid RealmMode given: " + builder.realmMode);
-    }
-
+  private ZKHelixAdmin(RealmAwareZkClient zkClient, boolean usesExternalZkClient) {
     _zkClient = zkClient;
     _configAccessor = new ConfigAccessor(_zkClient);
-    _usesExternalZkClient = false;
+    _usesExternalZkClient = usesExternalZkClient;
   }
 
   @Override
@@ -1873,76 +1852,15 @@ public class ZKHelixAdmin implements HelixAdmin {
     return true;
   }
 
-  // TODO: refactor builder to reduce duplicate code with other Helix Java APIs
-  public static class Builder {
-    private String zkAddress;
-    private RealmAwareZkClient.RealmMode realmMode;
-    private RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig;
-    private RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig;
-
+  public static class Builder extends GenericZkHelixApiBuilder<Builder> {
     public Builder() {
     }
 
-    public ZKHelixAdmin.Builder setZkAddress(String zkAddress) {
-      this.zkAddress = zkAddress;
-      return this;
-    }
-
-    public ZKHelixAdmin.Builder setRealmMode(RealmAwareZkClient.RealmMode realmMode) {
-      this.realmMode = realmMode;
-      return this;
-    }
-
-    public ZKHelixAdmin.Builder setRealmAwareZkConnectionConfig(
-        RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
-      realmAwareZkConnectionConfig = realmAwareZkConnectionConfig;
-      return this;
-    }
-
-    public ZKHelixAdmin.Builder setRealmAwareZkClientConfig(
-        RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
-      realmAwareZkClientConfig = realmAwareZkClientConfig;
-      return this;
-    }
-
     public ZKHelixAdmin build() {
       validate();
-      return new ZKHelixAdmin(this);
-    }
-
-    /*
-     * Validates the given parameters before creating an instance of ZKHelixAdmin.
-     */
-    private void validate() {
-      // Resolve RealmMode based on other parameters
-      boolean isZkAddressSet = zkAddress != null && !zkAddress.isEmpty();
-      if (realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM && !isZkAddressSet) {
-        throw new HelixException(
-            "RealmMode cannot be single-realm without a valid ZkAddress set!");
-      }
-      if (realmMode == RealmAwareZkClient.RealmMode.MULTI_REALM && isZkAddressSet) {
-        throw new HelixException(
-            "ZkAddress cannot be set on multi-realm mode!");
-      }
-      if (realmMode == null) {
-        realmMode = isZkAddressSet ? RealmAwareZkClient.RealmMode.SINGLE_REALM
-            : RealmAwareZkClient.RealmMode.MULTI_REALM;
-      }
-
-      // Resolve RealmAwareZkClientConfig
-      if (realmAwareZkClientConfig == null) {
-        // ZkHelixAdmin should have ZNRecordSerializer set by default
-        realmAwareZkClientConfig = new RealmAwareZkClient.RealmAwareZkClientConfig()
-            .setZkSerializer(
-                new org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer());
-      }
-
-      // Resolve RealmAwareZkConnectionConfig
-      if (realmAwareZkConnectionConfig == null) {
-        // If not set, create a default one
-        realmAwareZkConnectionConfig =
-            new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder().build();
-      }
+      return new ZKHelixAdmin(
+          createZkClient(_realmMode, _realmAwareZkConnectionConfig, _realmAwareZkClientConfig,
+              _zkAddress), false);
     }
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBaseDataAccessor.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBaseDataAccessor.java
index 32f33f8..a7596f4 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBaseDataAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBaseDataAccessor.java
@@ -65,7 +65,6 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
 
   // Designates which mode ZkBaseDataAccessor should be created in. If not specified, it will be
   // created on SHARED mode.
-  // TODO: move this to RealmAwareZkClient
   public enum ZkClientType {
     /**
      * When ZkBaseDataAccessor is created with the DEDICATED type, it supports ephemeral node
@@ -141,51 +140,9 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
     _usesExternalZkClient = true;
   }
 
-  private ZkBaseDataAccessor(Builder<T> builder) {
-    switch (builder.realmMode) {
-      case MULTI_REALM:
-        try {
-          if (builder.zkClientType == ZkClientType.DEDICATED) {
-            // Use a realm-aware dedicated zk client
-            _zkClient = DedicatedZkClientFactory.getInstance()
-                .buildZkClient(builder.realmAwareZkConnectionConfig,
-                    builder.realmAwareZkClientConfig);
-          } else if (builder.zkClientType == ZkClientType.SHARED) {
-            // Use a realm-aware shared zk client
-            _zkClient = SharedZkClientFactory.getInstance()
-                .buildZkClient(builder.realmAwareZkConnectionConfig,
-                    builder.realmAwareZkClientConfig);
-          } else {
-            _zkClient = new FederatedZkClient(builder.realmAwareZkConnectionConfig,
-                builder.realmAwareZkClientConfig);
-          }
-        } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
-          throw new HelixException("Not able to connect on multi-realm mode.", e);
-        }
-        break;
-
-      case SINGLE_REALM:
-        // Create a HelixZkClient: Use a SharedZkClient because ZkBaseDataAccessor does not need to
-        // do ephemeral operations.
-        if (builder.zkClientType == ZkClientType.DEDICATED) {
-          // If DEDICATED, then we use a dedicated HelixZkClient because we must support ephemeral
-          // operations
-          _zkClient = DedicatedZkClientFactory.getInstance()
-              .buildZkClient(new HelixZkClient.ZkConnectionConfig(builder.zkAddress),
-                  builder.realmAwareZkClientConfig.createHelixZkClientConfig());
-        } else {
-          _zkClient = SharedZkClientFactory.getInstance()
-              .buildZkClient(new HelixZkClient.ZkConnectionConfig(builder.zkAddress),
-                  builder.realmAwareZkClientConfig.createHelixZkClientConfig());
-          _zkClient
-              .waitUntilConnected(HelixZkClient.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);
-        }
-        break;
-      default:
-        throw new HelixException("Invalid RealmMode given: " + builder.realmMode);
-    }
-
-    _usesExternalZkClient = false;
+  private ZkBaseDataAccessor(RealmAwareZkClient zkClient, boolean usesExternalZkClient) {
+    _zkClient = zkClient;
+    _usesExternalZkClient = usesExternalZkClient;
   }
 
   /**
@@ -260,10 +217,9 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
   @Deprecated
   public ZkBaseDataAccessor(String zkAddress, ZkSerializer zkSerializer,
       ZkClientType zkClientType) {
-    RealmAwareZkClient.RealmAwareZkClientConfig clientConfig =
-        new RealmAwareZkClient.RealmAwareZkClientConfig().setZkSerializer(zkSerializer);
-
-    _zkClient = buildRealmAwareZkClient(clientConfig, zkAddress, zkClientType);
+    _zkClient = buildRealmAwareZkClientWithDefaultConfigs(
+        new RealmAwareZkClient.RealmAwareZkClientConfig().setZkSerializer(zkSerializer), zkAddress,
+        zkClientType);
     _usesExternalZkClient = false;
   }
 
@@ -282,10 +238,9 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
   @Deprecated
   public ZkBaseDataAccessor(String zkAddress, PathBasedZkSerializer pathBasedZkSerializer,
       ZkClientType zkClientType) {
-    RealmAwareZkClient.RealmAwareZkClientConfig clientConfig =
-        new RealmAwareZkClient.RealmAwareZkClientConfig().setZkSerializer(pathBasedZkSerializer);
-
-    _zkClient = buildRealmAwareZkClient(clientConfig, zkAddress, zkClientType);
+    _zkClient = buildRealmAwareZkClientWithDefaultConfigs(
+        new RealmAwareZkClient.RealmAwareZkClientConfig().setZkSerializer(pathBasedZkSerializer),
+        zkAddress, zkClientType);
     _usesExternalZkClient = false;
   }
 
@@ -1308,7 +1263,7 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
   }
 
   /**
-   * Subscrie to zookeeper data changes
+   * Subscribe to zookeeper data changes
    */
   @Override
   public List<String> subscribeChildChanges(String path, IZkChildListener listener) {
@@ -1316,7 +1271,7 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
   }
 
   /**
-   * Unsubscrie to zookeeper data changes
+   * Unsubscribe to zookeeper data changes
    */
   @Override
   public void unsubscribeChildChanges(String path, IZkChildListener childListener) {
@@ -1341,44 +1296,10 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
     }
   }
 
-  // TODO: refactor Builder class to remove duplicate code with other Helix Java APIs
-  public static class Builder<T> {
-    private String zkAddress;
-    private ZkBaseDataAccessor.ZkClientType zkClientType;
-    private RealmAwareZkClient.RealmMode realmMode;
-    private RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig;
-    private RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig;
-
+  public static class Builder<T> extends GenericBaseDataAccessorBuilder<Builder<T>> {
     public Builder() {
     }
 
-    public Builder<T> setZkAddress(String zkAddress) {
-      this.zkAddress = zkAddress;
-      return this;
-    }
-
-    public Builder<T> setRealmMode(RealmAwareZkClient.RealmMode realmMode) {
-      this.realmMode = realmMode;
-      return this;
-    }
-
-    public Builder<T> setZkClientType(ZkClientType zkClientType) {
-      this.zkClientType = zkClientType;
-      return this;
-    }
-
-    public Builder<T> setRealmAwareZkConnectionConfig(
-        RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
-      this.realmAwareZkConnectionConfig = realmAwareZkConnectionConfig;
-      return this;
-    }
-
-    public Builder<T> setRealmAwareZkClientConfig(
-        RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
-      this.realmAwareZkClientConfig = realmAwareZkClientConfig;
-      return this;
-    }
-
     /**
      * Returns a <code>ZkBaseDataAccessor</code> instance.
      * <p>
@@ -1388,67 +1309,27 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
      */
     public ZkBaseDataAccessor<T> build() {
       validate();
-      return new ZkBaseDataAccessor<>(this);
-    }
-
-    /*
-     * Validates the given parameters before building an instance of ZkBaseDataAccessor.
-     */
-    private void validate() {
-      // Resolve RealmMode based on other parameters
-      boolean isZkAddressSet = zkAddress != null && !zkAddress.isEmpty();
-      boolean isZkClientTypeSet = zkClientType != null;
-
-      // If ZkClientType is set, RealmMode must either be single-realm or not set.
-      if (isZkClientTypeSet && realmMode == RealmAwareZkClient.RealmMode.MULTI_REALM) {
-        throw new HelixException("ZkClientType cannot be set on multi-realm mode!");
-      }
-      // If ZkClientType is not set and realmMode is single-realm, default to SHARED
-      if (!isZkClientTypeSet && realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM) {
-        zkClientType = ZkClientType.SHARED;
-      }
-
-      if (realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM && !isZkAddressSet) {
-        throw new HelixException("RealmMode cannot be single-realm without a valid ZkAddress set!");
-      }
-
-      if (realmMode == RealmAwareZkClient.RealmMode.MULTI_REALM && isZkAddressSet) {
-        throw new HelixException("ZkAddress cannot be set on multi-realm mode!");
-      }
-
-      if (realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM
-          && zkClientType == ZkClientType.FEDERATED) {
-        throw new HelixException("FederatedZkClient cannot be set on single-realm mode!");
-      }
-
-      if (realmMode == null) {
-        realmMode = isZkAddressSet ? RealmAwareZkClient.RealmMode.SINGLE_REALM
-            : RealmAwareZkClient.RealmMode.MULTI_REALM;
-      }
-
-      // Resolve RealmAwareZkClientConfig
-      if (realmAwareZkClientConfig == null) {
-        realmAwareZkClientConfig = new RealmAwareZkClient.RealmAwareZkClientConfig()
-            .setZkSerializer(new ZNRecordSerializer());
-      }
-
-      // Resolve RealmAwareZkConnectionConfig
-      if (realmAwareZkConnectionConfig == null) {
-        // If not set, create a default one
-        realmAwareZkConnectionConfig =
-            new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder().build();
-      }
+      return new ZkBaseDataAccessor<>(
+          createZkClient(_realmMode, _realmAwareZkConnectionConfig, _realmAwareZkClientConfig,
+              _zkAddress));
     }
   }
 
-  /*
-   * This is used for constructors that do not take a Builder in as a parameter because of
-   * keeping backward-compatibility.
+  /**
+   * This method is used for constructors that are not based on the Builder for
+   * backward-compatibility.
+   * It checks if there is a System Property config set for Multi-ZK mode and determines if a
+   * FederatedZkClient should be created.
+   * @param clientConfig default RealmAwareZkClientConfig with ZK serializer set
+   * @param zkAddress
+   * @param zkClientType
+   * @return
    */
-  private RealmAwareZkClient buildRealmAwareZkClient(
+  static RealmAwareZkClient buildRealmAwareZkClientWithDefaultConfigs(
       RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, String zkAddress,
       ZkClientType zkClientType) {
     if (Boolean.getBoolean(SystemPropertyKeys.MULTI_ZK_ENABLED)) {
+      // If the multi ZK config is enabled, use multi-realm mode with FederatedZkClient
       try {
         return new FederatedZkClient(
             new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder().build(), clientConfig);
@@ -1458,7 +1339,6 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
     }
 
     RealmAwareZkClient zkClient;
-
     switch (zkClientType) {
       case DEDICATED:
         zkClient = DedicatedZkClientFactory.getInstance()
@@ -1475,7 +1355,6 @@ public class ZkBaseDataAccessor<T> implements BaseDataAccessor<T> {
             .waitUntilConnected(HelixZkClient.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);
         break;
     }
-
     return zkClient;
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheBaseDataAccessor.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheBaseDataAccessor.java
index 72d5601..fdae5dc 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheBaseDataAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheBaseDataAccessor.java
@@ -19,31 +19,22 @@ package org.apache.helix.manager.zk;
  * under the License.
  */
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixException;
-import org.apache.helix.SystemPropertyKeys;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor.RetCode;
-import org.apache.helix.msdcommon.exception.InvalidRoutingDataException;
 import org.apache.helix.store.HelixPropertyListener;
 import org.apache.helix.store.HelixPropertyStore;
 import org.apache.helix.store.zk.ZNode;
 import org.apache.helix.util.PathUtils;
-import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
-import org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer;
-import org.apache.helix.zookeeper.impl.client.FederatedZkClient;
-import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
-import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.apache.helix.zookeeper.zkclient.IZkChildListener;
 import org.apache.helix.zookeeper.zkclient.IZkDataListener;
@@ -126,41 +117,9 @@ public class ZkCacheBaseDataAccessor<T> implements HelixPropertyStore<T> {
   public ZkCacheBaseDataAccessor(String zkAddress, ZkSerializer serializer, String chrootPath,
       List<String> wtCachePaths, List<String> zkCachePaths, String monitorType, String monitorkey,
       ZkBaseDataAccessor.ZkClientType zkClientType) {
-
-    // If the multi ZK config is enabled, use multi-realm mode with FederatedZkClient
-    if (Boolean.parseBoolean(System.getProperty(SystemPropertyKeys.MULTI_ZK_ENABLED))) {
-      try {
-        RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder connectionConfigBuilder =
-            new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder();
-        RealmAwareZkClient.RealmAwareZkClientConfig clientConfig =
-            new RealmAwareZkClient.RealmAwareZkClientConfig();
-        clientConfig.setZkSerializer(serializer).setMonitorType(monitorType)
-            .setMonitorKey(monitorkey);
-        // Use a federated zk client
-        _zkClient = new FederatedZkClient(connectionConfigBuilder.build(), clientConfig);
-      } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
-        // Note: IllegalStateException is for HttpRoutingDataReader if MSDS endpoint cannot be
-        // found
-        throw new HelixException("Failed to create ZkCacheBaseDataAccessor!", e);
-      }
-    } else {
-      HelixZkClient.ZkClientConfig clientConfig = new HelixZkClient.ZkClientConfig();
-      clientConfig.setZkSerializer(serializer).setMonitorType(monitorType)
-          .setMonitorKey(monitorkey);
-      switch (zkClientType) {
-        case DEDICATED:
-          _zkClient = DedicatedZkClientFactory.getInstance()
-              .buildZkClient(new HelixZkClient.ZkConnectionConfig(zkAddress),
-                  new HelixZkClient.ZkClientConfig().setZkSerializer(serializer));
-          break;
-        case SHARED:
-        default:
-          _zkClient = SharedZkClientFactory.getInstance()
-              .buildZkClient(new HelixZkClient.ZkConnectionConfig(zkAddress), clientConfig);
-      }
-      _zkClient.waitUntilConnected(HelixZkClient.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);
-    }
-
+    _zkClient = ZkBaseDataAccessor.buildRealmAwareZkClientWithDefaultConfigs(
+        new RealmAwareZkClient.RealmAwareZkClientConfig().setZkSerializer(serializer)
+            .setMonitorType(monitorType).setMonitorKey(monitorkey), zkAddress, zkClientType);
     _baseAccessor = new ZkBaseDataAccessor<>(_zkClient);
 
     if (chrootPath == null || chrootPath.equals("/")) {
@@ -176,65 +135,15 @@ public class ZkCacheBaseDataAccessor<T> implements HelixPropertyStore<T> {
     start();
   }
 
-  /**
-   * Constructor using a Builder that allows users to set connection and client configs.
-   * @param builder
-   */
-  private ZkCacheBaseDataAccessor(Builder builder) {
-    _chrootPath = builder._chrootPath;
-    _wtCachePaths = builder._wtCachePaths;
-    _zkCachePaths = builder._zkCachePaths;
-
-    RealmAwareZkClient zkClient;
-    switch (builder._realmMode) {
-      case MULTI_REALM:
-        try {
-          if (builder._zkClientType == ZkBaseDataAccessor.ZkClientType.DEDICATED) {
-            // Use a realm-aware dedicated zk client
-            zkClient = DedicatedZkClientFactory.getInstance()
-                .buildZkClient(builder._realmAwareZkConnectionConfig,
-                    builder._realmAwareZkClientConfig);
-          } else if (builder._zkClientType == ZkBaseDataAccessor.ZkClientType.SHARED) {
-            // Use a realm-aware shared zk client
-            zkClient = SharedZkClientFactory.getInstance()
-                .buildZkClient(builder._realmAwareZkConnectionConfig,
-                    builder._realmAwareZkClientConfig);
-          } else {
-            // Use a federated zk client
-            zkClient = new FederatedZkClient(builder._realmAwareZkConnectionConfig,
-                builder._realmAwareZkClientConfig);
-          }
-          break; // Must break out of the switch statement here since zkClient has been created
-        } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
-          // Note: IllegalStateException is for HttpRoutingDataReader if MSDS endpoint cannot be
-          // found
-          throw new HelixException("Failed to create ZkCacheBaseDataAccessor!", e);
-        }
-      case SINGLE_REALM:
-        switch (builder._zkClientType) {
-          case DEDICATED:
-            // If DEDICATED, then we use a dedicated HelixZkClient because we must support ephemeral
-            // operations
-            zkClient = DedicatedZkClientFactory.getInstance()
-                .buildZkClient(new HelixZkClient.ZkConnectionConfig(builder._zkAddress),
-                    builder._realmAwareZkClientConfig.createHelixZkClientConfig());
-            break;
-          case SHARED:
-          default:
-            zkClient = SharedZkClientFactory.getInstance()
-                .buildZkClient(new HelixZkClient.ZkConnectionConfig(builder._zkAddress),
-                    builder._realmAwareZkClientConfig.createHelixZkClientConfig());
-            zkClient.waitUntilConnected(HelixZkClient.DEFAULT_CONNECTION_TIMEOUT,
-                TimeUnit.MILLISECONDS);
-            break;
-        }
-      default:
-        throw new HelixException("Invalid RealmMode given: " + builder._realmMode);
-    }
-
+  private ZkCacheBaseDataAccessor(RealmAwareZkClient zkClient, String chrootPath,
+      List<String> wtCachePaths, List<String> zkCachePaths) {
     _zkClient = zkClient;
     _baseAccessor = new ZkBaseDataAccessor<>(_zkClient);
 
+    _chrootPath = chrootPath;
+    _wtCachePaths = wtCachePaths;
+    _zkCachePaths = zkCachePaths;
+
     start();
   }
 
@@ -920,43 +829,15 @@ public class ZkCacheBaseDataAccessor<T> implements HelixPropertyStore<T> {
     }
   }
 
-  public static class Builder<T> {
-    private String _zkAddress;
-    private RealmAwareZkClient.RealmMode _realmMode;
-    private RealmAwareZkClient.RealmAwareZkConnectionConfig _realmAwareZkConnectionConfig;
-    private RealmAwareZkClient.RealmAwareZkClientConfig _realmAwareZkClientConfig;
-
+  public static class Builder<T> extends GenericBaseDataAccessorBuilder<Builder<T>> {
     /** ZkCacheBaseDataAccessor-specific parameters */
     private String _chrootPath;
     private List<String> _wtCachePaths;
     private List<String> _zkCachePaths;
-    private ZkBaseDataAccessor.ZkClientType _zkClientType;
 
     public Builder() {
     }
 
-    public Builder<T> setZkAddress(String zkAddress) {
-      _zkAddress = zkAddress;
-      return this;
-    }
-
-    public Builder<T> setRealmMode(RealmAwareZkClient.RealmMode realmMode) {
-      _realmMode = realmMode;
-      return this;
-    }
-
-    public Builder<T> setRealmAwareZkConnectionConfig(
-        RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
-      _realmAwareZkConnectionConfig = realmAwareZkConnectionConfig;
-      return this;
-    }
-
-    public Builder<T> setRealmAwareZkClientConfig(
-        RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
-      _realmAwareZkClientConfig = realmAwareZkClientConfig;
-      return this;
-    }
-
     public Builder<T> setChrootPath(String chrootPath) {
       _chrootPath = chrootPath;
       return this;
@@ -972,70 +853,11 @@ public class ZkCacheBaseDataAccessor<T> implements HelixPropertyStore<T> {
       return this;
     }
 
-    /**
-     * Sets the ZkClientType. If this is set, ZkCacheBaseDataAccessor will be created on
-     * single-realm mode.
-     * @param zkClientType
-     * @return
-     */
-    public Builder<T> setZkClientType(ZkBaseDataAccessor.ZkClientType zkClientType) {
-      _zkClientType = zkClientType;
-      return this;
-    }
-
     public ZkCacheBaseDataAccessor<T> build() {
       validate();
-      return new ZkCacheBaseDataAccessor<>(this);
-    }
-
-    private void validate() {
-      // Resolve RealmMode based on other parameters
-      boolean isZkAddressSet = _zkAddress != null && !_zkAddress.isEmpty();
-      boolean isZkClientTypeSet = _zkClientType != null;
-
-      // If ZkClientType is set, RealmMode must either be single-realm or not set.
-      if (isZkClientTypeSet && _realmMode == RealmAwareZkClient.RealmMode.MULTI_REALM) {
-        throw new HelixException(
-            "ZkCacheBaseDataAccessor: you cannot set ZkClientType on multi-realm mode!");
-      }
-      // If ZkClientType is not set and realmMode is single-realm, default to SHARED
-      if (!isZkClientTypeSet && _realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM) {
-        _zkClientType = ZkBaseDataAccessor.ZkClientType.SHARED;
-      }
-
-      if (_realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM && !isZkAddressSet) {
-        throw new HelixException(
-            "ZkCacheBaseDataAccessor: RealmMode cannot be single-realm without a valid ZkAddress set!");
-      }
-
-      if (_realmMode == RealmAwareZkClient.RealmMode.MULTI_REALM && isZkAddressSet) {
-        throw new HelixException(
-            "ZkCacheBaseDataAccessor: You cannot set the ZkAddress on multi-realm mode!");
-      }
-
-      if (_realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM
-          && _zkClientType == ZkBaseDataAccessor.ZkClientType.FEDERATED) {
-        throw new HelixException(
-            "ZkCacheBaseDataAccessor: You cannot use FederatedZkClient on single-realm mode!");
-      }
-
-      if (_realmMode == null) {
-        _realmMode = isZkAddressSet ? RealmAwareZkClient.RealmMode.SINGLE_REALM
-            : RealmAwareZkClient.RealmMode.MULTI_REALM;
-      }
-
-      // Resolve RealmAwareZkClientConfig
-      if (_realmAwareZkClientConfig == null) {
-        _realmAwareZkClientConfig = new RealmAwareZkClient.RealmAwareZkClientConfig()
-            .setZkSerializer(new ZNRecordSerializer());
-      }
-
-      // Resolve RealmAwareZkConnectionConfig
-      if (_realmAwareZkConnectionConfig == null) {
-        // If not set, create a default one
-        _realmAwareZkConnectionConfig =
-            new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder().build();
-      }
+      return new ZkCacheBaseDataAccessor<>(
+          createZkClient(_realmMode, _realmAwareZkConnectionConfig, _realmAwareZkClientConfig,
+              _zkAddress), _chrootPath, _wtCachePaths, _zkCachePaths);
     }
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
index 4fcfc97..672d5a4 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
@@ -41,6 +41,7 @@ import org.apache.helix.HelixConstants;
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.SystemPropertyKeys;
+import org.apache.helix.manager.zk.GenericZkHelixApiBuilder;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
@@ -182,29 +183,10 @@ public class ClusterSetup {
     _usesExternalZkClient = true;
   }
 
-  private ClusterSetup(Builder builder) {
-    switch (builder._realmMode) {
-      case MULTI_REALM:
-        try {
-          _zkClient = new FederatedZkClient(builder._realmAwareZkConnectionConfig,
-              builder._realmAwareZkClientConfig.setZkSerializer(new ZNRecordSerializer()));
-          break;
-        } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
-          throw new HelixException("Failed to create ClusterSetup!", e);
-        }
-      case SINGLE_REALM:
-        // Create a HelixZkClient: Use a SharedZkClient because ClusterSetup does not need to do
-        // ephemeral operations
-        _zkClient = SharedZkClientFactory.getInstance()
-            .buildZkClient(new HelixZkClient.ZkConnectionConfig(builder._zkAddress),
-                builder._realmAwareZkClientConfig.createHelixZkClientConfig()
-                    .setZkSerializer(new ZNRecordSerializer()));
-        break;
-      default:
-        throw new HelixException("Invalid RealmMode given: " + builder._realmMode);
-    }
+  private ClusterSetup(RealmAwareZkClient zkClient, boolean usesExternalZkClient) {
+    _zkClient = zkClient;
     _admin = new ZKHelixAdmin(_zkClient);
-    _usesExternalZkClient = false;
+    _usesExternalZkClient = usesExternalZkClient;
   }
 
   /**
@@ -1612,69 +1594,15 @@ public class ClusterSetup {
     System.exit(ret);
   }
 
-  public static class Builder {
-    private String _zkAddress;
-    private RealmAwareZkClient.RealmMode _realmMode;
-    private RealmAwareZkClient.RealmAwareZkConnectionConfig _realmAwareZkConnectionConfig;
-    private RealmAwareZkClient.RealmAwareZkClientConfig _realmAwareZkClientConfig;
-
+  public static class Builder extends GenericZkHelixApiBuilder<Builder> {
     public Builder() {
     }
 
-    public Builder setZkAddress(String zkAddress) {
-      _zkAddress = zkAddress;
-      return this;
-    }
-
-    public Builder setRealmMode(RealmAwareZkClient.RealmMode realmMode) {
-      _realmMode = realmMode;
-      return this;
-    }
-
-    public Builder setRealmAwareZkConnectionConfig(
-        RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
-      _realmAwareZkConnectionConfig = realmAwareZkConnectionConfig;
-      return this;
-    }
-
-    public Builder setRealmAwareZkClientConfig(
-        RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
-      _realmAwareZkClientConfig = realmAwareZkClientConfig;
-      return this;
-    }
-
     public ClusterSetup build() {
       validate();
-      return new ClusterSetup(this);
-    }
-
-    private void validate() {
-      // Resolve RealmMode based on other parameters
-      boolean isZkAddressSet = _zkAddress != null && !_zkAddress.isEmpty();
-      if (_realmMode == RealmAwareZkClient.RealmMode.SINGLE_REALM && !isZkAddressSet) {
-        throw new HelixException(
-            "ClusterSetup: RealmMode cannot be single-realm without a valid ZkAddress set!");
-      }
-      if (_realmMode == RealmAwareZkClient.RealmMode.MULTI_REALM && isZkAddressSet) {
-        throw new HelixException(
-            "ClusterSetup: You cannot set the ZkAddress on multi-realm mode!");
-      }
-      if (_realmMode == null) {
-        _realmMode = isZkAddressSet ? RealmAwareZkClient.RealmMode.SINGLE_REALM
-            : RealmAwareZkClient.RealmMode.MULTI_REALM;
-      }
-
-      // Resolve RealmAwareZkClientConfig
-      if (_realmAwareZkClientConfig == null) {
-        _realmAwareZkClientConfig = new RealmAwareZkClient.RealmAwareZkClientConfig();
-      }
-
-      // Resolve RealmAwareZkConnectionConfig
-      if (_realmAwareZkConnectionConfig == null) {
-        // If not set, create a default one
-        _realmAwareZkConnectionConfig =
-            new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder().build();
-      }
+      return new ClusterSetup(
+          createZkClient(_realmMode, _realmAwareZkConnectionConfig, _realmAwareZkClientConfig,
+              _zkAddress), false);
     }
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java
index 6d601c4..12cdad4 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java
@@ -111,19 +111,22 @@ public class BestPossibleExternalViewVerifier extends ZkHelixClusterVerifier {
     _dataProvider = new ResourceControllerDataProvider();
   }
 
-  private BestPossibleExternalViewVerifier(Builder builder) {
-    super(builder);
+  private BestPossibleExternalViewVerifier(RealmAwareZkClient zkClient, String clusterName,
+      Map<String, Map<String, String>> errStates, Set<String> resources,
+      Set<String> expectLiveInstances) {
+    super(zkClient, clusterName);
     // Deep copy data from Builder
     _errStates = new HashMap<>();
-    if (builder._errStates != null) {
-      builder._errStates.forEach((k, v) -> _errStates.put(k, new HashMap<>(v)));
+    if (errStates != null) {
+      errStates.forEach((k, v) -> _errStates.put(k, new HashMap<>(v)));
     }
-    _resources = new HashSet<>(builder._resources);
-    _expectLiveInstances = new HashSet<>(builder._expectLiveInstances);
+    _resources = resources == null ? new HashSet<>() : new HashSet<>(resources);
+    _expectLiveInstances =
+        expectLiveInstances == null ? new HashSet<>() : new HashSet<>(expectLiveInstances);
     _dataProvider = new ResourceControllerDataProvider();
   }
 
-  public static class Builder extends ZkHelixClusterVerifier.Builder {
+  public static class Builder extends ZkHelixClusterVerifier.Builder<Builder> {
     private final String _clusterName;
     private Map<String, Map<String, String>> _errStates;
     private Set<String> _resources;
@@ -151,7 +154,10 @@ public class BestPossibleExternalViewVerifier extends ZkHelixClusterVerifier {
       }
 
       validate();
-      return new BestPossibleExternalViewVerifier(this);
+      return new BestPossibleExternalViewVerifier(
+          createZkClient(RealmAwareZkClient.RealmMode.SINGLE_REALM, _realmAwareZkConnectionConfig,
+              _realmAwareZkClientConfig, _zkAddress), _clusterName, _errStates, _resources,
+          _expectLiveInstances);
     }
 
     public String getClusterName() {
@@ -193,23 +199,6 @@ public class BestPossibleExternalViewVerifier extends ZkHelixClusterVerifier {
       _zkClient = zkClient;
       return this;
     }
-
-    @Override
-    public Builder setZkAddr(String zkAddress) {
-      return (Builder) super.setZkAddr(zkAddress);
-    }
-
-    @Override
-    public Builder setRealmAwareZkConnectionConfig(
-        RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
-      return (Builder) super.setRealmAwareZkConnectionConfig(realmAwareZkConnectionConfig);
-    }
-
-    @Override
-    public Builder setRealmAwareZkClientConfig(
-        RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
-      return (Builder) super.setRealmAwareZkClientConfig(realmAwareZkClientConfig);
-    }
   }
 
   @Override
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ClusterLiveNodesVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ClusterLiveNodesVerifier.java
index 8aeb64a..4a46f18 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ClusterLiveNodesVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ClusterLiveNodesVerifier.java
@@ -38,9 +38,10 @@ public class ClusterLiveNodesVerifier extends ZkHelixClusterVerifier {
     _expectLiveNodes = new HashSet<>(expectLiveNodes);
   }
 
-  private ClusterLiveNodesVerifier(Builder builder) {
-    super(builder);
-    _expectLiveNodes = new HashSet<>(builder._expectLiveNodes);
+  private ClusterLiveNodesVerifier(RealmAwareZkClient zkClient, String clusterName,
+      Set<String> expectLiveNodes) {
+    super(zkClient, clusterName);
+    _expectLiveNodes = expectLiveNodes == null ? new HashSet<>() : new HashSet<>(expectLiveNodes);
   }
 
   @Override
@@ -58,7 +59,7 @@ public class ClusterLiveNodesVerifier extends ZkHelixClusterVerifier {
     return _expectLiveNodes.equals(actualLiveNodes);
   }
 
-  public static class Builder extends ZkHelixClusterVerifier.Builder {
+  public static class Builder extends ZkHelixClusterVerifier.Builder<Builder> {
     private final String _clusterName; // This is the ZK path sharding key
     private final Set<String> _expectLiveNodes;
 
@@ -73,24 +74,9 @@ public class ClusterLiveNodesVerifier extends ZkHelixClusterVerifier {
       }
 
       validate();
-      return new ClusterLiveNodesVerifier(this);
-    }
-
-    @Override
-    public Builder setZkAddr(String zkAddress) {
-      return (Builder) super.setZkAddr(zkAddress);
-    }
-
-    @Override
-    public Builder setRealmAwareZkConnectionConfig(
-        RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
-      return (Builder) super.setRealmAwareZkConnectionConfig(realmAwareZkConnectionConfig);
-    }
-
-    @Override
-    public Builder setRealmAwareZkClientConfig(
-        RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
-      return (Builder) super.setRealmAwareZkClientConfig(realmAwareZkClientConfig);
+      return new ClusterLiveNodesVerifier(
+          createZkClient(RealmAwareZkClient.RealmMode.SINGLE_REALM, _realmAwareZkConnectionConfig,
+              _realmAwareZkClientConfig, _zkAddress), _clusterName, _expectLiveNodes);
     }
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java
index 76f0d09..987599e 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java
@@ -78,23 +78,16 @@ public class StrictMatchExternalViewVerifier extends ZkHelixClusterVerifier {
     _isDeactivatedNodeAware = isDeactivatedNodeAware;
   }
 
-  @Deprecated
   private StrictMatchExternalViewVerifier(RealmAwareZkClient zkClient, String clusterName,
       Set<String> resources, Set<String> expectLiveInstances, boolean isDeactivatedNodeAware) {
     super(zkClient, clusterName);
-    _resources = resources;
-    _expectLiveInstances = expectLiveInstances;
+    _resources = resources == null ? new HashSet<>() : new HashSet<>(resources);
+    _expectLiveInstances =
+        expectLiveInstances == null ? new HashSet<>() : new HashSet<>(expectLiveInstances);
     _isDeactivatedNodeAware = isDeactivatedNodeAware;
   }
 
-  private StrictMatchExternalViewVerifier(Builder builder) {
-    super(builder);
-    _resources = new HashSet<>(builder._resources);
-    _expectLiveInstances = new HashSet<>(builder._expectLiveInstances);
-    _isDeactivatedNodeAware = builder._isDeactivatedNodeAware;
-  }
-
-  public static class Builder extends ZkHelixClusterVerifier.Builder {
+  public static class Builder extends ZkHelixClusterVerifier.Builder<Builder> {
     private final String _clusterName; // This is the ZK path sharding key
     private Set<String> _resources;
     private Set<String> _expectLiveInstances;
@@ -119,7 +112,10 @@ public class StrictMatchExternalViewVerifier extends ZkHelixClusterVerifier {
       }
 
       validate();
-      return new StrictMatchExternalViewVerifier(this);
+      return new StrictMatchExternalViewVerifier(
+          createZkClient(RealmAwareZkClient.RealmMode.SINGLE_REALM, _realmAwareZkConnectionConfig,
+              _realmAwareZkClientConfig, _zkAddress), _clusterName, _resources,
+          _expectLiveInstances, _isDeactivatedNodeAware);
     }
 
     public Builder(String clusterName) {
@@ -167,23 +163,6 @@ public class StrictMatchExternalViewVerifier extends ZkHelixClusterVerifier {
       return this;
     }
 
-    @Override
-    public Builder setZkAddr(String zkAddress) {
-      return (Builder) super.setZkAddr(zkAddress);
-    }
-
-    @Override
-    public Builder setRealmAwareZkConnectionConfig(
-        RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
-      return (Builder) super.setRealmAwareZkConnectionConfig(realmAwareZkConnectionConfig);
-    }
-
-    @Override
-    public Builder setRealmAwareZkClientConfig(
-        RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
-      return (Builder) super.setRealmAwareZkClientConfig(realmAwareZkClientConfig);
-    }
-
     protected void validate() {
       super.validate();
       if (!_clusterName.equals(_realmAwareZkConnectionConfig.getZkRealmShardingKey())) {
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java
index 449b659..e2022e7 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java
@@ -31,6 +31,7 @@ import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.SystemPropertyKeys;
 import org.apache.helix.api.listeners.PreFetch;
+import org.apache.helix.manager.zk.GenericZkHelixApiBuilder;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.msdcommon.exception.InvalidRoutingDataException;
@@ -93,13 +94,7 @@ public abstract class ZkHelixClusterVerifier
     }
   }
 
-  /**
-   * Deprecated because we discourage passing in a ZkClient directly.
-   * @param zkClient
-   * @param clusterName
-   */
-  @Deprecated
-  public ZkHelixClusterVerifier(RealmAwareZkClient zkClient, String clusterName) {
+  protected ZkHelixClusterVerifier(RealmAwareZkClient zkClient, String clusterName) {
     if (zkClient == null || clusterName == null) {
       throw new IllegalArgumentException("requires zkClient|clusterName");
     }
@@ -144,27 +139,6 @@ public abstract class ZkHelixClusterVerifier
     _keyBuilder = _accessor.keyBuilder();
   }
 
-  protected ZkHelixClusterVerifier(Builder builder) {
-    if (Boolean.parseBoolean(System.getProperty(SystemPropertyKeys.MULTI_ZK_ENABLED))) {
-      try {
-        // First, try to create a RealmAwareZkClient that's a DedicatedZkClient
-        _zkClient = DedicatedZkClientFactory.getInstance()
-            .buildZkClient(builder._realmAwareZkConnectionConfig,
-                builder._realmAwareZkClientConfig);
-      } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
-        throw new HelixException("ZkHelixClusterVerifier: failed to create ZkClient!", e);
-      }
-    } else {
-      _zkClient = DedicatedZkClientFactory.getInstance()
-          .buildZkClient(new HelixZkClient.ZkConnectionConfig(builder._zkAddress));
-    }
-    _usesExternalZkClient = false;
-    _zkClient.setZkSerializer(new ZNRecordSerializer());
-    _clusterName = builder._realmAwareZkConnectionConfig.getZkRealmShardingKey();
-    _accessor = new ZKHelixDataAccessor(_clusterName, new ZkBaseDataAccessor<>(_zkClient));
-    _keyBuilder = _accessor.keyBuilder();
-  }
-
   /**
    * Verify the cluster.
    * The method will be blocked at most {@code timeout}.
@@ -345,36 +319,36 @@ public abstract class ZkHelixClusterVerifier
     return _clusterName;
   }
 
-  // TODO: refactor Builders for Java APIs
-  protected abstract static class Builder {
-    // Note: ZkHelixClusterVerifier is a single-realm API, so RealmMode is assumed to be
-    // SINGLE-REALM
-    protected String _zkAddress;
-    protected RealmAwareZkClient.RealmAwareZkConnectionConfig _realmAwareZkConnectionConfig;
-    protected RealmAwareZkClient.RealmAwareZkClientConfig _realmAwareZkClientConfig;
-
+  protected abstract static class Builder<B extends Builder<B>> extends GenericZkHelixApiBuilder<B> {
     public Builder() {
+      // Note: ZkHelixClusterVerifier is a single-realm API, so RealmMode is assumed to be
+      // SINGLE-REALM
+      setRealmMode(RealmAwareZkClient.RealmMode.SINGLE_REALM);
     }
 
-    public Builder setZkAddr(String zkAddress) {
-      _zkAddress = zkAddress;
-      return this;
-    }
-
-    public Builder setRealmAwareZkConnectionConfig(
-        RealmAwareZkClient.RealmAwareZkConnectionConfig realmAwareZkConnectionConfig) {
-      _realmAwareZkConnectionConfig = realmAwareZkConnectionConfig;
-      return this;
+    /**
+     * Use setZkAddress() instead. Deprecated but left here for backward-compatibility.
+     * @param zkAddress
+     * @return
+     */
+    @Deprecated
+    public B setZkAddr(String zkAddress) {
+      return setZkAddress(zkAddress);
     }
 
-    public Builder setRealmAwareZkClientConfig(
-        RealmAwareZkClient.RealmAwareZkClientConfig realmAwareZkClientConfig) {
-      _realmAwareZkClientConfig = realmAwareZkClientConfig;
-      return this;
+    public String getClusterName() {
+      if (_realmAwareZkConnectionConfig != null && (
+          _realmAwareZkConnectionConfig.getZkRealmShardingKey() != null
+              && !_realmAwareZkConnectionConfig.getZkRealmShardingKey().isEmpty())) {
+        // Need to remove the first "/" from sharding key given
+        return _realmAwareZkConnectionConfig.getZkRealmShardingKey().substring(1);
+      }
+      throw new HelixException(
+          "Failed to get the cluster name! Either RealmAwareZkConnectionConfig is null or its sharding key is null or empty!");
     }
 
     protected void validate() {
-      // Validate that either ZkAddress or ZkRealmShardingKey is set.
+      // Validate that either ZkAddress or ZkRealmShardingKey is set
       if (_zkAddress == null || _zkAddress.isEmpty()) {
         if (_realmAwareZkConnectionConfig == null
             || _realmAwareZkConnectionConfig.getZkRealmShardingKey() == null
@@ -384,14 +358,29 @@ public abstract class ZkHelixClusterVerifier
                   + _zkAddress + " RealmAwareZkConnectionConfig: " + _realmAwareZkConnectionConfig);
         }
       }
+      initializeConfigsIfNull();
+    }
 
-      // Resolve all default values
-      if (_realmAwareZkConnectionConfig == null) {
-        _realmAwareZkConnectionConfig =
-            new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder().build();
-      }
-      if (_realmAwareZkClientConfig == null) {
-        _realmAwareZkClientConfig = new RealmAwareZkClient.RealmAwareZkClientConfig();
+    /**
+     * Creates a RealmAwareZkClient for ZkHelixClusterVerifiers.
+     * Note that DedicatedZkClient is used whether it's multi-realm or single-realm.
+     * @return
+     */
+    @Override
+    protected RealmAwareZkClient createZkClient(RealmAwareZkClient.RealmMode realmMode,
+        RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig,
+        RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, String zkAddress) {
+      if (Boolean.parseBoolean(System.getProperty(SystemPropertyKeys.MULTI_ZK_ENABLED))) {
+        try {
+          // First, try to create a RealmAwareZkClient that's a DedicatedZkClient
+          return DedicatedZkClientFactory.getInstance()
+              .buildZkClient(connectionConfig, clientConfig);
+        } catch (IOException | InvalidRoutingDataException | IllegalStateException e) {
+          throw new HelixException("ZkHelixClusterVerifier: failed to create ZkClient!", e);
+        }
+      } else {
+        return DedicatedZkClientFactory.getInstance()
+            .buildZkClient(new HelixZkClient.ZkConnectionConfig(zkAddress));
       }
     }
   }
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 40b6c54..0e461b7 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
@@ -419,6 +419,12 @@ public interface RealmAwareZkClient {
         return this;
       }
 
+      /**
+       * Note: this must be a valid ZK path. For example, if you are trying to create a sharding key
+       * out of a cluster name "CLUSTER", then your sharding key should be "/CLUSTER".
+       * @param shardingKey
+       * @return
+       */
       public Builder setZkRealmShardingKey(String shardingKey) {
         _zkRealmShardingKey = shardingKey;
         return this;
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/ZkClientType.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/ZkClientType.java
new file mode 100644
index 0000000..4db06a8
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/ZkClientType.java
@@ -0,0 +1,42 @@
+package org.apache.helix.zookeeper.api.client;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+public enum ZkClientType {
+  /**
+   * If a Helix API is created with the DEDICATED type, it supports ephemeral node
+   * creation, callback functionality, and session management. But note that this is more
+   * resource-heavy since it creates a dedicated ZK connection so should be used sparingly only
+   * when the aforementioned features are needed.
+   */
+  DEDICATED,
+
+  /**
+   * If a Helix API is created with the SHARED type, it only supports CRUD
+   * functionalities. This will be the default mode of creation.
+   */
+  SHARED,
+
+  /**
+   * Uses FederatedZkClient (applicable on multi-realm mode only) that queries Metadata Store
+   * Directory Service for routing data.
+   */
+  FEDERATED
+}