You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by no...@apache.org on 2023/02/14 02:04:21 UTC

[solr] branch branch_9x updated: Refactor SolrZkclient to use a Builder pattern (#1014)

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

noble pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/branch_9x by this push:
     new 7c1584b8f56 Refactor SolrZkclient to use a Builder pattern (#1014)
7c1584b8f56 is described below

commit 7c1584b8f565c86bb2fcb8248a351a2ed2c2dd26
Author: Noble Paul <no...@users.noreply.github.com>
AuthorDate: Tue Feb 14 12:15:54 2023 +1100

    Refactor SolrZkclient to use a Builder pattern (#1014)
---
 .../core/src/java/org/apache/solr/cloud/ZkCLI.java |   9 +-
 .../java/org/apache/solr/cloud/ZkController.java   |  45 ++++---
 .../src/java/org/apache/solr/core/NodeConfig.java  |   8 +-
 .../apache/solr/packagemanager/PackageManager.java |   7 +-
 .../src/java/org/apache/solr/util/SolrCLI.java     |  78 +++++++++--
 .../apache/solr/cloud/AliasIntegrationTest.java    |   6 +-
 .../solr/cloud/ChaosMonkeyShardSplitTest.java      |   6 +-
 .../apache/solr/cloud/ConfigSetApiLockingTest.java |   7 +-
 .../apache/solr/cloud/ConnectionManagerTest.java   |  20 ++-
 .../solr/cloud/DistributedApiAsyncTrackerTest.java |  17 ++-
 .../apache/solr/cloud/DistributedQueueTest.java    |   6 +-
 .../org/apache/solr/cloud/LeaderElectionTest.java  |  14 +-
 .../OutOfBoxZkACLAndCredentialsProvidersTest.java  |  25 +++-
 ...OverriddenZkACLAndCredentialsProvidersTest.java |  27 +++-
 .../test/org/apache/solr/cloud/OverseerTest.java   |  17 ++-
 .../org/apache/solr/cloud/SolrCLIZkUtilsTest.java  |   7 +-
 .../org/apache/solr/cloud/SolrXmlInZkTest.java     |   7 +-
 .../org/apache/solr/cloud/TestConfigSetsAPI.java   | 148 ++++++++++++++-------
 .../solr/cloud/TestConfigSetsAPIZkFailure.java     |  11 +-
 .../org/apache/solr/cloud/TestDistributedMap.java  |  49 +++++--
 .../solr/cloud/TestLeaderElectionZkExpiry.java     |   5 +-
 .../solr/cloud/TestSizeLimitedDistributedMap.java  |  13 +-
 .../org/apache/solr/cloud/TestStressLiveNodes.java |   5 +-
 .../src/test/org/apache/solr/cloud/ZkCLITest.java  |  28 +++-
 .../org/apache/solr/cloud/ZkControllerTest.java    |  12 +-
 .../apache/solr/cloud/ZkDistributedLockTest.java   |   7 +-
 .../org/apache/solr/cloud/ZkSolrClientTest.java    |  16 ++-
 .../api/collections/CollectionApiLockingTest.java  |   7 +-
 .../cloud/api/collections/TestCollectionAPI.java   |   6 +-
 .../solr/cloud/overseer/ZkStateReaderTest.java     |   5 +-
 .../solr/cloud/overseer/ZkStateWriterTest.java     |  37 +++++-
 .../apache/solr/schema/TestCloudManagedSchema.java |   3 +-
 .../solr/schema/TestManagedSchemaThreadSafety.java |   6 +-
 .../security/hadoop/SaslZkACLProviderTest.java     |  11 +-
 .../hadoop/TestDelegationWithHadoopAuth.java       |  10 +-
 .../hadoop/TestSolrCloudWithDelegationTokens.java  |  10 +-
 .../org/apache/solr/common/cloud/SolrZkClient.java | 146 +++++++++++---------
 .../apache/solr/common/cloud/ZkStateReader.java    |  44 +++---
 .../apache/solr/common/cloud/SolrZkClientTest.java |  22 ++-
 .../solr/common/cloud/TestZkConfigSetService.java  |  22 ++-
 .../solr/common/cloud/TestZkMaintenanceUtils.java  |  25 ++--
 .../cloud/SolrZkClientCompressedDataTest.java      |   7 +-
 ...DigestZkACLAndCredentialsProvidersTestBase.java |  50 +++++--
 .../solr/cloud/AbstractDistribZkTestBase.java      |   6 +-
 .../apache/solr/cloud/MiniSolrCloudCluster.java    |  15 ++-
 .../org/apache/solr/cloud/MockSolrZkClient.java    |   2 +-
 .../java/org/apache/solr/cloud/ZkTestServer.java   |  28 +++-
 47 files changed, 769 insertions(+), 293 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkCLI.java b/solr/core/src/java/org/apache/solr/cloud/ZkCLI.java
index 2a35c8cf11d..f8ae0e194de 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkCLI.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkCLI.java
@@ -29,6 +29,7 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.Paths;
 import java.util.List;
 import java.util.Properties;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.regex.Pattern;
 import javax.xml.parsers.ParserConfigurationException;
@@ -257,7 +258,13 @@ public class ZkCLI implements CLIO {
       }
       SolrZkClient zkClient = null;
       try {
-        zkClient = new SolrZkClient(zkServerAddress, 30000, 30000, () -> {});
+        zkClient =
+            new SolrZkClient.Builder()
+                .withUrl(zkServerAddress)
+                .withTimeout(30000, TimeUnit.MILLISECONDS)
+                .withConnTimeOut(30000, TimeUnit.MILLISECONDS)
+                .withReconnectListener(() -> {})
+                .build();
 
         if (line.getOptionValue(CMD).equalsIgnoreCase(BOOTSTRAP)) {
           if (!line.hasOption(SOLRHOME)) {
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 9b9e247ac99..a8e2ab47de9 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -380,25 +380,16 @@ public class ZkController implements Closeable {
             : cc.getResourceLoader().newInstance(stateCompressionProviderClass, Compressor.class);
 
     zkClient =
-        new SolrZkClient(
-            zkServerAddress,
-            clientTimeout,
-            zkClientConnectTimeout,
-            strat,
-            () -> onReconnect(descriptorsSupplier),
-            () -> {
-              try {
-                this.overseer.close();
-              } catch (Exception e) {
-                log.error("Error trying to stop any Overseer threads", e);
-              }
-              closeOutstandingElections(descriptorsSupplier);
-              markAllAsNotLeader(descriptorsSupplier);
-            },
-            zkACLProvider,
-            cc::isShutDown,
-            compressor);
-
+        new SolrZkClient.Builder()
+            .withUrl(zkServerAddress)
+            .withTimeout(clientTimeout, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(zkClientConnectTimeout, TimeUnit.MILLISECONDS)
+            .withConnStrategy(strat)
+            .withBeforeConnect(() -> beforeReconnect(descriptorsSupplier))
+            .withAclProvider(zkACLProvider)
+            .withClosedCheck(cc::isShutDown)
+            .withCompressor(compressor)
+            .build();
     // Refuse to start if ZK has a non empty /clusterstate.json
     checkNoOldClusterstate(zkClient);
 
@@ -430,6 +421,16 @@ public class ZkController implements Closeable {
     assert ObjectReleaseTracker.track(this);
   }
 
+  private void beforeReconnect(Supplier<List<CoreDescriptor>> descriptorsSupplier) {
+    try {
+      this.overseer.close();
+    } catch (Exception e) {
+      log.error("Error trying to stop any Overseer threads", e);
+    }
+    closeOutstandingElections(descriptorsSupplier);
+    markAllAsNotLeader(descriptorsSupplier);
+  }
+
   private void onReconnect(Supplier<List<CoreDescriptor>> descriptorsSupplier)
       throws SessionExpiredException {
     // on reconnect, reload cloud info
@@ -1191,7 +1192,11 @@ public class ZkController implements Closeable {
     String chrootPath = zkHost.substring(zkHost.indexOf("/"), zkHost.length());
 
     SolrZkClient tmpClient =
-        new SolrZkClient(zkHost.substring(0, zkHost.indexOf("/")), 60000, 30000);
+        new SolrZkClient.Builder()
+            .withUrl(zkHost.substring(0, zkHost.indexOf("/")))
+            .withTimeout(60000, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(30000, TimeUnit.MILLISECONDS)
+            .build();
     boolean exists = tmpClient.exists(chrootPath, true);
     if (!exists && create) {
       log.info("creating chroot {}", chrootPath);
diff --git a/solr/core/src/java/org/apache/solr/core/NodeConfig.java b/solr/core/src/java/org/apache/solr/core/NodeConfig.java
index c6763cf2205..398596dc2fd 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeConfig.java
@@ -31,6 +31,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.solr.common.SolrException;
@@ -209,7 +210,12 @@ public class NodeConfig {
     if (!StringUtils.isEmpty(zkHost)) {
       int startUpZkTimeOut = Integer.getInteger("waitForZk", 30);
       startUpZkTimeOut *= 1000;
-      try (SolrZkClient zkClient = new SolrZkClient(zkHost, startUpZkTimeOut, startUpZkTimeOut)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(zkHost)
+              .withTimeout(startUpZkTimeOut, TimeUnit.MILLISECONDS)
+              .withConnTimeOut(startUpZkTimeOut, TimeUnit.MILLISECONDS)
+              .build()) {
         if (zkClient.exists("/solr.xml", true)) {
           log.info("solr.xml found in ZooKeeper. Loading...");
           byte[] data = zkClient.getData("/solr.xml", null, null, true);
diff --git a/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java b/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
index 53aef653b88..94d6cb54573 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
@@ -35,6 +35,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Scanner;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import org.apache.commons.collections4.MultiValuedMap;
 import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
@@ -79,7 +80,11 @@ public class PackageManager implements Closeable {
   public PackageManager(HttpSolrClient solrClient, String solrBaseUrl, String zkHost) {
     this.solrBaseUrl = solrBaseUrl;
     this.solrClient = solrClient;
-    this.zkClient = new SolrZkClient(zkHost, 30000);
+    this.zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkHost)
+            .withTimeout(30000, TimeUnit.MILLISECONDS)
+            .build();
     log.info("Done initializing a zkClient instance...");
   }
 
diff --git a/solr/core/src/java/org/apache/solr/util/SolrCLI.java b/solr/core/src/java/org/apache/solr/util/SolrCLI.java
index 47214df9e97..ae9d9e4d75f 100755
--- a/solr/core/src/java/org/apache/solr/util/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/util/SolrCLI.java
@@ -1959,7 +1959,11 @@ public class SolrCLI implements CLIO {
       }
 
       String confName = cli.getOptionValue("confname");
-      try (SolrZkClient zkClient = new SolrZkClient(zkHost, 30000)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(zkHost)
+              .withTimeout(10000, TimeUnit.MILLISECONDS)
+              .build()) {
         echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ...", cli);
         Path confPath =
             ConfigSetService.getConfigsetPath(
@@ -2031,7 +2035,11 @@ public class SolrCLI implements CLIO {
                 + " is running in standalone server mode, downconfig can only be used when running in SolrCloud mode.\n");
       }
 
-      try (SolrZkClient zkClient = new SolrZkClient(zkHost, 30000)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(zkHost)
+              .withTimeout(10000, TimeUnit.MILLISECONDS)
+              .build()) {
         echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ...", cli);
         String confName = cli.getOptionValue("confname");
         String confDir = cli.getOptionValue("confdir");
@@ -2110,7 +2118,11 @@ public class SolrCLI implements CLIO {
         throw new SolrServerException("You may not remove the root ZK node ('/')!");
       }
       echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ...", cli);
-      try (SolrZkClient zkClient = new SolrZkClient(zkHost, 30000)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(zkHost)
+              .withTimeout(10000, TimeUnit.MILLISECONDS)
+              .build()) {
         if (recurse == false && zkClient.getChildren(znode, null, true).size() != 0) {
           throw new SolrServerException(
               "ZooKeeper node " + znode + " has children and recurse has NOT been specified.");
@@ -2172,7 +2184,11 @@ public class SolrCLI implements CLIO {
                 + " is running in standalone server mode, 'zk ls' can only be used when running in SolrCloud mode.\n");
       }
 
-      try (SolrZkClient zkClient = new SolrZkClient(zkHost, 30000)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(zkHost)
+              .withTimeout(10000, TimeUnit.MILLISECONDS)
+              .build()) {
         echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ...", cli);
 
         String znode = cli.getOptionValue("path");
@@ -2234,7 +2250,11 @@ public class SolrCLI implements CLIO {
                 + " is running in standalone server mode, 'zk mkroot' can only be used when running in SolrCloud mode.\n");
       }
 
-      try (SolrZkClient zkClient = new SolrZkClient(zkHost, 30000)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(zkHost)
+              .withTimeout(10000, TimeUnit.MILLISECONDS)
+              .build()) {
         echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ...", cli);
 
         String znode = cli.getOptionValue("path");
@@ -2294,7 +2314,11 @@ public class SolrCLI implements CLIO {
                 + " is running in standalone server mode, cp can only be used when running in SolrCloud mode.\n");
       }
 
-      try (SolrZkClient zkClient = new SolrZkClient(zkHost, 30000)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(zkHost)
+              .withTimeout(10000, TimeUnit.MILLISECONDS)
+              .build()) {
         echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ...", cli);
         String src = cli.getOptionValue("src");
         String dst = cli.getOptionValue("dst");
@@ -2373,7 +2397,11 @@ public class SolrCLI implements CLIO {
                 + " is running in standalone server mode, downconfig can only be used when running in SolrCloud mode.\n");
       }
 
-      try (SolrZkClient zkClient = new SolrZkClient(zkHost, 30000)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(zkHost)
+              .withTimeout(10000, TimeUnit.MILLISECONDS)
+              .build()) {
         echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ...", cli);
         String src = cli.getOptionValue("src");
         String dst = cli.getOptionValue("dst");
@@ -4243,7 +4271,11 @@ public class SolrCLI implements CLIO {
 
             // check if security is already enabled or not
             if (!zkInaccessible) {
-              try (SolrZkClient zkClient = new SolrZkClient(zkHost, 10000)) {
+              try (SolrZkClient zkClient =
+                  new SolrZkClient.Builder()
+                      .withUrl(zkHost)
+                      .withTimeout(10000, TimeUnit.MILLISECONDS)
+                      .build()) {
                 if (zkClient.exists("/security.json", true)) {
                   byte oldSecurityBytes[] = zkClient.getData("/security.json", null, null, true);
                   if (!"{}".equals(new String(oldSecurityBytes, StandardCharsets.UTF_8).trim())) {
@@ -4268,7 +4300,11 @@ public class SolrCLI implements CLIO {
           if (!updateIncludeFileOnly) {
             if (!zkInaccessible) {
               echoIfVerbose("Uploading following security.json: " + securityJson, cli);
-              try (SolrZkClient zkClient = new SolrZkClient(zkHost, 10000)) {
+              try (SolrZkClient zkClient =
+                  new SolrZkClient.Builder()
+                      .withUrl(zkHost)
+                      .withTimeout(10000, TimeUnit.MILLISECONDS)
+                      .build()) {
                 zkClient.setData(
                     "/security.json", securityJson.getBytes(StandardCharsets.UTF_8), true);
               } catch (Exception ex) {
@@ -4316,7 +4352,11 @@ public class SolrCLI implements CLIO {
 
             echoIfVerbose("Uploading following security.json: {}", cli);
 
-            try (SolrZkClient zkClient = new SolrZkClient(zkHost, 10000)) {
+            try (SolrZkClient zkClient =
+                new SolrZkClient.Builder()
+                    .withUrl(zkHost)
+                    .withTimeout(10000, TimeUnit.MILLISECONDS)
+                    .build()) {
               zkClient.setData("/security.json", "{}".getBytes(StandardCharsets.UTF_8), true);
             }
           }
@@ -4394,7 +4434,11 @@ public class SolrCLI implements CLIO {
             }
 
             // check if security is already enabled or not
-            try (SolrZkClient zkClient = new SolrZkClient(zkHost, 10000)) {
+            try (SolrZkClient zkClient =
+                new SolrZkClient.Builder()
+                    .withUrl(zkHost)
+                    .withTimeout(10000, TimeUnit.MILLISECONDS)
+                    .build()) {
               if (zkClient.exists("/security.json", true)) {
                 byte oldSecurityBytes[] = zkClient.getData("/security.json", null, null, true);
                 if (!"{}".equals(new String(oldSecurityBytes, StandardCharsets.UTF_8).trim())) {
@@ -4461,7 +4505,11 @@ public class SolrCLI implements CLIO {
 
           if (!updateIncludeFileOnly) {
             echoIfVerbose("Uploading following security.json: " + securityJson, cli);
-            try (SolrZkClient zkClient = new SolrZkClient(zkHost, 10000)) {
+            try (SolrZkClient zkClient =
+                new SolrZkClient.Builder()
+                    .withUrl(zkHost)
+                    .withTimeout(10000, TimeUnit.MILLISECONDS)
+                    .build()) {
               zkClient.setData(
                   "/security.json", securityJson.getBytes(StandardCharsets.UTF_8), true);
             }
@@ -4510,7 +4558,11 @@ public class SolrCLI implements CLIO {
 
             echoIfVerbose("Uploading following security.json: {}", cli);
 
-            try (SolrZkClient zkClient = new SolrZkClient(zkHost, 10000)) {
+            try (SolrZkClient zkClient =
+                new SolrZkClient.Builder()
+                    .withUrl(zkHost)
+                    .withTimeout(10000, TimeUnit.MILLISECONDS)
+                    .build()) {
               zkClient.setData("/security.json", "{}".getBytes(StandardCharsets.UTF_8), true);
             }
           }
diff --git a/solr/core/src/test/org/apache/solr/cloud/AliasIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/AliasIntegrationTest.java
index 4f9dc745c2f..3c57c892c8d 100644
--- a/solr/core/src/test/org/apache/solr/cloud/AliasIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/AliasIntegrationTest.java
@@ -203,7 +203,11 @@ public class AliasIntegrationTest extends SolrCloudTestCase {
 
     // now check that an independently constructed ZkStateReader can see what we've done.
     // i.e. the data is really in zookeeper
-    try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(), 30000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(30000, TimeUnit.MILLISECONDS)
+            .build()) {
       ZkController.createClusterZkNodes(zkClient);
       try (ZkStateReader zkStateReader2 = new ZkStateReader(zkClient)) {
         zkStateReader2.createClusterStateWatchersAndUpdate();
diff --git a/solr/core/src/test/org/apache/solr/cloud/ChaosMonkeyShardSplitTest.java b/solr/core/src/test/org/apache/solr/cloud/ChaosMonkeyShardSplitTest.java
index 8baa2c5880c..8d3631d7c8c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ChaosMonkeyShardSplitTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ChaosMonkeyShardSplitTest.java
@@ -248,7 +248,11 @@ public class ChaosMonkeyShardSplitTest extends ShardSplitTest {
    */
   private SolrZkClient electNewOverseer(String address)
       throws KeeperException, InterruptedException, IOException {
-    SolrZkClient zkClient = new SolrZkClient(address, TIMEOUT);
+    SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(address)
+            .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     ZkStateReader reader = new ZkStateReader(zkClient);
     LeaderElector overseerElector = new LeaderElector(zkClient);
     UpdateShardHandler updateShardHandler =
diff --git a/solr/core/src/test/org/apache/solr/cloud/ConfigSetApiLockingTest.java b/solr/core/src/test/org/apache/solr/cloud/ConfigSetApiLockingTest.java
index 58dce5a1863..30edcbd8a93 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ConfigSetApiLockingTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ConfigSetApiLockingTest.java
@@ -18,6 +18,7 @@
 package org.apache.solr.cloud;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.cloud.SolrZkClient;
 import org.junit.Test;
@@ -39,7 +40,11 @@ public class ConfigSetApiLockingTest extends SolrTestCaseJ4 {
     ZkTestServer server = new ZkTestServer(createTempDir("zkData"));
     try {
       server.run();
-      try (SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .build()) {
         ConfigSetApiLockFactory apiLockFactory =
             new ConfigSetApiLockFactory(
                 new ZkDistributedConfigSetLockFactory(zkClient, "/apiLockTestRoot"));
diff --git a/solr/core/src/test/org/apache/solr/cloud/ConnectionManagerTest.java b/solr/core/src/test/org/apache/solr/cloud/ConnectionManagerTest.java
index 14dec585f45..d48ba5bc3cc 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ConnectionManagerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ConnectionManagerTest.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.nio.file.Path;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.cloud.ConnectionManager;
@@ -47,7 +48,11 @@ public class ConnectionManagerTest extends SolrTestCaseJ4 {
     try {
       server.run();
 
-      SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT);
+      SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       ConnectionManager cm = zkClient.getConnectionManager();
       try {
         assertFalse(cm.isLikelyExpired());
@@ -77,7 +82,11 @@ public class ConnectionManagerTest extends SolrTestCaseJ4 {
     try {
       server.run();
 
-      SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT);
+      SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       ConnectionManager cm = zkClient.getConnectionManager();
       try {
         assertFalse(cm.isLikelyExpired());
@@ -123,7 +132,12 @@ public class ConnectionManagerTest extends SolrTestCaseJ4 {
       server.run();
 
       MockZkClientConnectionStrategy strategy = new MockZkClientConnectionStrategy();
-      SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT, strategy, null);
+      SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .withConnStrategy(strategy)
+              .build();
       ConnectionManager cm = zkClient.getConnectionManager();
 
       try {
diff --git a/solr/core/src/test/org/apache/solr/cloud/DistributedApiAsyncTrackerTest.java b/solr/core/src/test/org/apache/solr/cloud/DistributedApiAsyncTrackerTest.java
index c46d733b4c6..60ebfef8ec9 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DistributedApiAsyncTrackerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DistributedApiAsyncTrackerTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.solr.cloud;
 
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.response.RequestStatusState;
 import org.apache.solr.common.cloud.SolrZkClient;
@@ -35,7 +36,11 @@ public class DistributedApiAsyncTrackerTest extends SolrTestCaseJ4 {
 
     zkServer = new ZkTestServer(createTempDir("zookeeperDir"));
     zkServer.run();
-    zkClient = new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT);
+    zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
   }
 
   @Override
@@ -108,7 +113,10 @@ public class DistributedApiAsyncTrackerTest extends SolrTestCaseJ4 {
         permanentDaat.createNewAsyncJobTracker(asyncPermanent));
 
     try (SolrZkClient transientZkClient =
-        new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       DistributedApiAsyncTracker transientDaat =
           new DistributedApiAsyncTracker(transientZkClient, TRACKER_ROOT);
       assertTrue(
@@ -170,7 +178,10 @@ public class DistributedApiAsyncTrackerTest extends SolrTestCaseJ4 {
     final String asyncId = "theId";
 
     try (SolrZkClient transientZkClient =
-        new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       DistributedApiAsyncTracker transientDaat =
           new DistributedApiAsyncTracker(transientZkClient, TRACKER_ROOT);
       assertTrue(
diff --git a/solr/core/src/test/org/apache/solr/cloud/DistributedQueueTest.java b/solr/core/src/test/org/apache/solr/cloud/DistributedQueueTest.java
index bf69aa31a4d..7dd57605a9d 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DistributedQueueTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DistributedQueueTest.java
@@ -353,7 +353,11 @@ public class DistributedQueueTest extends SolrTestCaseJ4 {
     zkServer = new ZkTestServer(createTempDir("zkData"));
     zkServer.run();
     System.setProperty("zkHost", zkServer.getZkAddress());
-    zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+    zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     assertTrue(zkClient.isConnected());
   }
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/LeaderElectionTest.java b/solr/core/src/test/org/apache/solr/cloud/LeaderElectionTest.java
index 4136c8f8a89..9ac03aad882 100644
--- a/solr/core/src/test/org/apache/solr/cloud/LeaderElectionTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/LeaderElectionTest.java
@@ -67,7 +67,11 @@ public class LeaderElectionTest extends SolrTestCaseJ4 {
     server.setTheTickTime(1000);
     server.run();
 
-    zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT);
+    zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(server.getZkAddress())
+            .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     zkStateReader = new ZkStateReader(zkClient);
     seqToThread = Collections.synchronizedMap(new HashMap<Integer, Thread>());
     zkClient.makePath("/collections/collection1", true);
@@ -107,7 +111,13 @@ public class LeaderElectionTest extends SolrTestCaseJ4 {
     LeaderElector elector;
 
     public ElectorSetup(OnReconnect onReconnect) {
-      zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT, TIMEOUT, onReconnect);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .withConnTimeOut(TIMEOUT, TimeUnit.MILLISECONDS)
+              .withReconnectListener(onReconnect)
+              .build();
       zkStateReader = new ZkStateReader(zkClient);
       elector = new LeaderElector(zkClient);
       zkController = MockSolrSource.makeSimpleMock(null, zkStateReader, null);
diff --git a/solr/core/src/test/org/apache/solr/cloud/OutOfBoxZkACLAndCredentialsProvidersTest.java b/solr/core/src/test/org/apache/solr/cloud/OutOfBoxZkACLAndCredentialsProvidersTest.java
index 73b5f47c765..8c45324946c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/OutOfBoxZkACLAndCredentialsProvidersTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/OutOfBoxZkACLAndCredentialsProvidersTest.java
@@ -22,6 +22,7 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.cloud.SecurityAwareZkACLProvider;
 import org.apache.solr.common.cloud.SolrZkClient;
@@ -70,11 +71,19 @@ public class OutOfBoxZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
 
     System.setProperty("zkHost", zkServer.getZkAddress());
 
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT);
+    SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     zkClient.makePath("/solr", false, true);
     zkClient.close();
 
-    zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+    zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     zkClient.create(
         "/protectedCreateNode", "content".getBytes(DATA_ENCODING), CreateMode.PERSISTENT, false);
     zkClient.makePath(
@@ -107,7 +116,11 @@ public class OutOfBoxZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
 
   @Test
   public void testOutOfBoxSolrZkClient() throws Exception {
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+    SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     try {
       AbstractDigestZkACLAndCredentialsProvidersTestBase.doTest(
           zkClient, true, true, true, true, true, true, true, true, true, true);
@@ -118,7 +131,11 @@ public class OutOfBoxZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
 
   @Test
   public void testOpenACLUnsafeAllover() throws Exception {
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT);
+    SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     try {
       List<String> verifiedList = new ArrayList<>();
       assertOpenACLUnsafeAllover(zkClient, "/", verifiedList);
diff --git a/solr/core/src/test/org/apache/solr/cloud/OverriddenZkACLAndCredentialsProvidersTest.java b/solr/core/src/test/org/apache/solr/cloud/OverriddenZkACLAndCredentialsProvidersTest.java
index 9ada4f014b2..79fdac41acf 100644
--- a/solr/core/src/test/org/apache/solr/cloud/OverriddenZkACLAndCredentialsProvidersTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/OverriddenZkACLAndCredentialsProvidersTest.java
@@ -28,6 +28,7 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.cloud.DigestZkACLProvider;
 import org.apache.solr.common.cloud.DigestZkCredentialsProvider;
@@ -203,7 +204,9 @@ public class OverriddenZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
 
     SolrZkClient zkClient =
         new SolrZkClientUsingVMParamsProvidersButWithDifferentVMParamsNames(
-            zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+            new SolrZkClient.Builder()
+                .withUrl(zkServer.getZkAddress())
+                .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS));
     try {
       AbstractDigestZkACLAndCredentialsProvidersTestBase.doTest(
           zkClient, false, false, false, false, false, false, false, false, false, false);
@@ -220,7 +223,9 @@ public class OverriddenZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
 
     SolrZkClient zkClient =
         new SolrZkClientUsingVMParamsProvidersButWithDifferentVMParamsNames(
-            zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+            new SolrZkClient.Builder()
+                .withUrl(zkServer.getZkAddress())
+                .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS));
     try {
       AbstractDigestZkACLAndCredentialsProvidersTestBase.doTest(
           zkClient, false, false, false, false, false, false, false, false, false, false);
@@ -237,7 +242,9 @@ public class OverriddenZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
 
     SolrZkClient zkClient =
         new SolrZkClientUsingVMParamsProvidersButWithDifferentVMParamsNames(
-            zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+            new SolrZkClient.Builder()
+                .withUrl(zkServer.getZkAddress())
+                .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS));
     try {
       AbstractDigestZkACLAndCredentialsProvidersTestBase.doTest(
           zkClient, true, true, true, true, true, true, true, true, true, true);
@@ -254,7 +261,9 @@ public class OverriddenZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
 
     SolrZkClient zkClient =
         new SolrZkClientUsingVMParamsProvidersButWithDifferentVMParamsNames(
-            zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+            new SolrZkClient.Builder()
+                .withUrl(zkServer.getZkAddress())
+                .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS));
     try {
       AbstractDigestZkACLAndCredentialsProvidersTestBase.doTest(
           zkClient, true, true, false, false, false, false, false, false, false, false);
@@ -295,7 +304,11 @@ public class OverriddenZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
     }
 
     public SolrZkClient getSolrZkClient(String zkServerAddress, int zkClientTimeout) {
-      return new SolrZkClient(zkServerAddress, zkClientTimeout) {
+
+      return new SolrZkClient(
+          new SolrZkClient.Builder()
+              .withUrl(zkServerAddress)
+              .withTimeout(zkClientTimeout, TimeUnit.MILLISECONDS)) {
 
         @Override
         protected ZkCredentialsProvider createZkCredentialsToAddAutomatically() {
@@ -321,8 +334,8 @@ public class OverriddenZkACLAndCredentialsProvidersTest extends SolrTestCaseJ4 {
             "alternative" + DEFAULT_DIGEST_READONLY_PASSWORD_VM_PARAM_NAME);
 
     public SolrZkClientUsingVMParamsProvidersButWithDifferentVMParamsNames(
-        String zkServerAddress, int zkClientTimeout) {
-      super(zkServerAddress, zkClientTimeout);
+        SolrZkClient.Builder builder) {
+      super(builder);
     }
 
     @Override
diff --git a/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java b/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
index 83a7a8764a0..42cd7ad8b14 100644
--- a/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
@@ -142,7 +142,11 @@ public class OverseerTest extends SolrTestCaseJ4 {
         throws InterruptedException, IOException, KeeperException {
       this.overseers = overseers;
       this.nodeName = nodeName;
-      zkClient = new SolrZkClient(zkAddress, TIMEOUT);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(zkAddress)
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
 
       ZkController.createClusterZkNodes(zkClient);
 
@@ -1829,7 +1833,11 @@ public class OverseerTest extends SolrTestCaseJ4 {
   private SolrZkClient electNewOverseer(String address)
       throws InterruptedException, IOException, KeeperException, NoSuchFieldException,
           SecurityException, IllegalAccessException {
-    SolrZkClient zkClient = new SolrZkClient(address, TIMEOUT);
+    SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(address)
+            .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     zkClients.add(zkClient);
     ZkStateReader reader = new ZkStateReader(zkClient);
     readers.add(reader);
@@ -1875,7 +1883,10 @@ public class OverseerTest extends SolrTestCaseJ4 {
 
     if (zkClient == null) {
       SolrZkClient newZkClient =
-          new SolrZkClient(server.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       doAnswer(
               new Answer<Void>() {
                 @Override
diff --git a/solr/core/src/test/org/apache/solr/cloud/SolrCLIZkUtilsTest.java b/solr/core/src/test/org/apache/solr/cloud/SolrCLIZkUtilsTest.java
index 2442ab1a4bd..0ee5d289456 100644
--- a/solr/core/src/test/org/apache/solr/cloud/SolrCLIZkUtilsTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/SolrCLIZkUtilsTest.java
@@ -30,6 +30,7 @@ import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.common.cloud.SolrZkClient;
 import org.apache.solr.common.cloud.ZkMaintenanceUtils;
 import org.apache.solr.util.SolrCLI;
@@ -48,7 +49,11 @@ public class SolrCLIZkUtilsTest extends SolrCloudTestCase {
             "conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
         .configure();
     zkAddr = cluster.getZkServer().getZkAddress();
-    zkClient = new SolrZkClient(zkAddr, 30000);
+    zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkAddr)
+            .withTimeout(30000, TimeUnit.MILLISECONDS)
+            .build();
   }
 
   @AfterClass
diff --git a/solr/core/src/test/org/apache/solr/cloud/SolrXmlInZkTest.java b/solr/core/src/test/org/apache/solr/cloud/SolrXmlInZkTest.java
index 36413dccd02..a9473e1249d 100644
--- a/solr/core/src/test/org/apache/solr/cloud/SolrXmlInZkTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/SolrXmlInZkTest.java
@@ -21,6 +21,7 @@ import java.lang.invoke.MethodHandles;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.util.Properties;
+import java.util.concurrent.TimeUnit;
 import org.apache.commons.io.FileUtils;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.SolrException;
@@ -62,7 +63,11 @@ public class SolrXmlInZkTest extends SolrTestCaseJ4 {
     System.setProperty("zkHost", zkServer.getZkAddress());
     zkServer.buildZooKeeper("solrconfig.xml", "schema.xml");
 
-    zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+    zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
 
     if (toZk) {
       zkClient.makePath("solr.xml", XML_FOR_ZK.getBytes(StandardCharsets.UTF_8), true);
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
index 7e7ff345d4e..ba97eb6371d 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
@@ -48,6 +48,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 import javax.script.ScriptEngineManager;
@@ -211,8 +212,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     uploadConfigSetWithAssertions(configsetName, trustedConfigsetSuffix, "solr");
     uploadConfigSetWithAssertions(configsetName, untrustedConfigsetSuffix, null);
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       assertTrue(isTrusted(zkClient, configsetName, trustedConfigsetSuffix));
       assertFalse(isTrusted(zkClient, configsetName, untrustedConfigsetSuffix));
       // base trusted -> untrusted
@@ -310,11 +314,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
       setupBaseConfigSet(baseConfigSetName, oldProps);
 
       try (SolrZkClient zkClient =
-          new SolrZkClient(
-              cluster.getZkServer().getZkAddress(),
-              AbstractZkTestCase.TIMEOUT,
-              AbstractZkTestCase.TIMEOUT,
-              null)) {
+          new SolrZkClient.Builder()
+              .withUrl(cluster.getZkServer().getZkAddress())
+              .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+              .withConnTimeOut(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+              .build()) {
         assertFalse(getConfigSetService().checkConfigExists(configSetName));
 
         ConfigSetAdminResponse response =
@@ -437,8 +441,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     assertEquals(400l, statusCode);
 
     SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null);
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build();
 
     // Create dummy config files in zookeeper
     zkClient.makePath("/configs/myconf", true);
@@ -490,8 +497,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
 
   public void testUploadDisabled(boolean v2) throws Exception {
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
 
       ignoreException("Configset upload feature is disabled");
       for (boolean enabled : new boolean[] {true, false}) {
@@ -516,8 +526,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
   public void testUploadLegacyManagedSchemaFile() throws Exception {
     String configSetName = "legacy-managed-schema";
     SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null);
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build();
     try {
       long statusCode = uploadConfigSet(configSetName, "", null, zkClient, true);
       assertEquals(0l, statusCode);
@@ -572,8 +585,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffix = "testOverwrite-1-" + v2;
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       int solrconfigZkVersion =
           getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
       ignoreException("The configuration regulartestOverwrite-1 already exists in zookeeper");
@@ -610,8 +626,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffix = "testOverwriteWithCleanup-1-" + v2;
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       String configPath = "/configs/" + configsetName + configsetSuffix;
       List<String> extraFiles =
           Arrays.asList(
@@ -654,8 +673,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffix = "testOverwriteWithForbiddenFiles-1-" + v2;
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       String configPath = "/configs/" + configsetName + configsetSuffix;
       assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, null, true, false, v2, true));
       for (String fileEnding : ZkMaintenanceUtils.DEFAULT_FORBIDDEN_FILE_TYPES) {
@@ -682,8 +704,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffix = "testOverwriteWithTrust-1-" + v2;
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       assertFalse(isTrusted(zkClient, configsetName, configsetSuffix));
       int solrconfigZkVersion =
           getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
@@ -797,8 +822,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffix = "testSinglePathOverwrite-1-" + v2;
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       int solrconfigZkVersion =
           getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
       ignoreException("The configuration regulartestOverwrite-1 already exists in zookeeper");
@@ -852,8 +880,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffix = "testSinglePathNew-1-" + v2;
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       assertEquals(
           0,
           uploadSingleConfigSetFile(
@@ -942,8 +973,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffix = "testSinglePathCleanup-1-" + v2;
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       ignoreException("ConfigSet uploads do not allow cleanup=true when filePath is used.");
       assertEquals(
           400,
@@ -981,8 +1015,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffix = "testSinglePathTrusted-1-" + v2;
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, "solr");
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       assertEquals(
           0,
           uploadSingleConfigSetFile(
@@ -1066,8 +1103,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffix = "testSingleFileForbiddenType-1-" + v2;
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, "solr");
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       for (String fileType : ZkMaintenanceUtils.DEFAULT_FORBIDDEN_FILE_TYPES) {
         ignoreException("is forbidden for use in configSets");
         assertEquals(
@@ -1112,8 +1152,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
 
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       // New file with trusted request
 
       assertEquals(
@@ -1245,8 +1288,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     String configsetSuffixTrusted = "testSinglePathNewConfig-1-" + v2;
     String configsetSuffixUntrusted = "testSinglePathNewConfig-2-" + v2;
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null)) {
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       // New file with trusted request
       assertEquals(
           0,
@@ -1430,15 +1476,15 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
 
   private void uploadConfigSetWithAssertions(String configSetName, String suffix, String username)
       throws Exception {
-    SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(), AbstractZkTestCase.TIMEOUT, 45000, null);
-    try {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(45000, TimeUnit.MILLISECONDS)
+            .build()) {
       long statusCode = uploadConfigSet(configSetName, suffix, username, zkClient, true);
       assertEquals(0l, statusCode);
       assertConfigsetFiles(configSetName, suffix, zkClient);
-    } finally {
-      zkClient.close();
     }
   }
 
@@ -1875,11 +1921,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
   private void assertDelete(SolrClient solrClient, String configSet, boolean assertExists)
       throws IOException, SolrServerException {
     SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(),
-            AbstractZkTestCase.TIMEOUT,
-            AbstractZkTestCase.TIMEOUT,
-            null);
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     try {
       assertEquals(assertExists, getConfigSetService().checkConfigExists(configSet));
 
@@ -1899,11 +1945,11 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
     final SolrClient solrClient = getHttpSolrClient(baseUrl);
 
     SolrZkClient zkClient =
-        new SolrZkClient(
-            cluster.getZkServer().getZkAddress(),
-            AbstractZkTestCase.TIMEOUT,
-            AbstractZkTestCase.TIMEOUT,
-            null);
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     try {
       // test empty
       ConfigSetAdminRequest.List list = new ConfigSetAdminRequest.List();
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPIZkFailure.java b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPIZkFailure.java
index 8d41f303c2c..3183f2e15ce 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPIZkFailure.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPIZkFailure.java
@@ -29,6 +29,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.commons.io.FileUtils;
 import org.apache.jute.InputArchive;
@@ -116,11 +117,11 @@ public class TestConfigSetsAPIZkFailure extends SolrTestCaseJ4 {
     setupBaseConfigSet(BASE_CONFIGSET_NAME, oldProps);
 
     SolrZkClient zkClient =
-        new SolrZkClient(
-            solrCluster.getZkServer().getZkAddress(),
-            AbstractZkTestCase.TIMEOUT,
-            AbstractZkTestCase.TIMEOUT,
-            null);
+        new SolrZkClient.Builder()
+            .withUrl(solrCluster.getZkServer().getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     try {
 
       assertFalse(configSetService.checkConfigExists(CONFIGSET_NAME));
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestDistributedMap.java b/solr/core/src/test/org/apache/solr/cloud/TestDistributedMap.java
index 088250c2140..0b3325e96e5 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestDistributedMap.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestDistributedMap.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Path;
 import java.util.Locale;
+import java.util.concurrent.TimeUnit;
 import org.apache.commons.io.file.PathUtils;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.cloud.SolrZkClient;
@@ -55,7 +56,11 @@ public class TestDistributedMap extends SolrTestCaseJ4 {
   }
 
   public void testPut() throws KeeperException, InterruptedException {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       DistributedMap map = createMap(zkClient, path);
       assertFalse(zkClient.exists(path + "/" + DistributedMap.PREFIX + "foo", true));
@@ -65,7 +70,11 @@ public class TestDistributedMap extends SolrTestCaseJ4 {
   }
 
   public void testGet() throws KeeperException, InterruptedException {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       byte[] data = "data".getBytes(Charset.defaultCharset());
       zkClient.makePath(
@@ -81,7 +90,11 @@ public class TestDistributedMap extends SolrTestCaseJ4 {
   }
 
   public void testContains() throws KeeperException, InterruptedException {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       DistributedMap map = createMap(zkClient, path);
       assertFalse(map.contains("foo"));
@@ -97,7 +110,11 @@ public class TestDistributedMap extends SolrTestCaseJ4 {
   }
 
   public void testRemove() throws KeeperException, InterruptedException {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       DistributedMap map = createMap(zkClient, path);
       assertFalse(map.remove("foo"));
@@ -115,7 +132,11 @@ public class TestDistributedMap extends SolrTestCaseJ4 {
   }
 
   public void testSize() throws KeeperException, InterruptedException {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       DistributedMap map = createMap(zkClient, path);
       assertEquals(0, map.size());
@@ -131,7 +152,11 @@ public class TestDistributedMap extends SolrTestCaseJ4 {
   }
 
   public void testPutIfAbsent() throws KeeperException, InterruptedException {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       DistributedMap map = createMap(zkClient, path);
       assertEquals(0, map.size());
@@ -152,7 +177,11 @@ public class TestDistributedMap extends SolrTestCaseJ4 {
   }
 
   public void testKeys() throws KeeperException, InterruptedException {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       DistributedMap map = createMap(zkClient, path);
       assertEquals(0, map.keys().size());
@@ -172,7 +201,11 @@ public class TestDistributedMap extends SolrTestCaseJ4 {
   }
 
   public void testClear() throws KeeperException, InterruptedException {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       DistributedMap map = createMap(zkClient, path);
       map.clear();
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestLeaderElectionZkExpiry.java b/solr/core/src/test/org/apache/solr/cloud/TestLeaderElectionZkExpiry.java
index 5b2471437ad..884978298cc 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestLeaderElectionZkExpiry.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestLeaderElectionZkExpiry.java
@@ -79,7 +79,10 @@ public class TestLeaderElectionZkExpiry extends SolrTestCaseJ4 {
                 }
               });
           try (SolrZkClient zc =
-              new SolrZkClient(server.getZkAddress(), LeaderElectionTest.TIMEOUT)) {
+              new SolrZkClient.Builder()
+                  .withUrl(server.getZkAddress())
+                  .withTimeout(LeaderElectionTest.TIMEOUT, TimeUnit.MILLISECONDS)
+                  .build()) {
             boolean found = false;
             TimeOut timeout = new TimeOut(60, TimeUnit.SECONDS, TimeSource.NANO_TIME);
             while (!timeout.hasTimedOut()) {
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestSizeLimitedDistributedMap.java b/solr/core/src/test/org/apache/solr/cloud/TestSizeLimitedDistributedMap.java
index 6c9e01d7d7b..493b7bf062b 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestSizeLimitedDistributedMap.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestSizeLimitedDistributedMap.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.common.cloud.SolrZkClient;
 import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.common.util.SolrNamedThreadFactory;
@@ -35,7 +36,11 @@ public class TestSizeLimitedDistributedMap extends TestDistributedMap {
     final Set<String> expectedKeys = new HashSet<>();
     int numResponsesToStore = TEST_NIGHTLY ? Overseer.NUM_RESPONSES_TO_STORE : 100;
 
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       DistributedMap map =
           new SizeLimitedDistributedMap(
@@ -77,7 +82,11 @@ public class TestSizeLimitedDistributedMap extends TestDistributedMap {
     final List<String> deletedItems = new ArrayList<>();
     int numResponsesToStore = TEST_NIGHTLY ? Overseer.NUM_RESPONSES_TO_STORE : 100;
 
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       String path = getAndMakeInitialPath(zkClient);
       DistributedMap map =
           new SizeLimitedDistributedMap(
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestStressLiveNodes.java b/solr/core/src/test/org/apache/solr/cloud/TestStressLiveNodes.java
index de5742a414e..eb3bf597e4b 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestStressLiveNodes.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestStressLiveNodes.java
@@ -82,7 +82,10 @@ public class TestStressLiveNodes extends SolrCloudTestCase {
   private static SolrZkClient newSolrZkClient() {
     assertNotNull(ZK_SERVER_ADDR);
     // WTF is CloudConfigBuilder.DEFAULT_ZK_CLIENT_TIMEOUT private?
-    return new SolrZkClient(ZK_SERVER_ADDR, 15000);
+    return new SolrZkClient.Builder()
+        .withUrl(ZK_SERVER_ADDR)
+        .withTimeout(15000, TimeUnit.MILLISECONDS)
+        .build();
   }
 
   /** returns the true set of live nodes (currently in zk) as a sorted list */
diff --git a/solr/core/src/test/org/apache/solr/cloud/ZkCLITest.java b/solr/core/src/test/org/apache/solr/cloud/ZkCLITest.java
index e5e8399249c..58101f0c7c6 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ZkCLITest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ZkCLITest.java
@@ -27,6 +27,7 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.filefilter.RegexFileFilter;
@@ -91,11 +92,19 @@ public class ZkCLITest extends SolrTestCaseJ4 {
     zkServer = new ZkTestServer(zkDir);
     zkServer.run();
     System.setProperty("zkHost", zkServer.getZkAddress());
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT);
+    SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     zkClient.makePath("/solr", false, true);
     zkClient.close();
 
-    this.zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+    this.zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
 
     if (log.isInfoEnabled()) {
       log.info("####SETUP_END {}", getTestName());
@@ -412,9 +421,11 @@ public class ZkCLITest extends SolrTestCaseJ4 {
     SolrException ex =
         expectThrows(
             SolrException.class,
-            () -> {
-              new SolrZkClient("----------:33332", 100);
-            });
+            () ->
+                new SolrZkClient.Builder()
+                    .withUrl("----------:33332")
+                    .withTimeout(100, TimeUnit.MILLISECONDS)
+                    .build());
     zkClient.close();
   }
 
@@ -471,8 +482,11 @@ public class ZkCLITest extends SolrTestCaseJ4 {
 
     boolean excepted = false;
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            zkServer.getZkAddress(), AbstractDistribZkTestBase.DEFAULT_CONNECTION_TIMEOUT)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(
+                AbstractDistribZkTestBase.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       zkClient.getData("/", null, null, true);
     } catch (KeeperException.NoAuthException e) {
       excepted = true;
diff --git a/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java b/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java
index 004741b1801..85514bc44c0 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java
@@ -94,7 +94,11 @@ public class ZkControllerTest extends SolrTestCaseJ4 {
     try {
       server.run();
 
-      try (SolrZkClient client = new SolrZkClient(server.getZkAddress(), TIMEOUT)) {
+      try (SolrZkClient client =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .build()) {
 
         ZkController.createClusterZkNodes(client);
 
@@ -349,7 +353,11 @@ public class ZkControllerTest extends SolrTestCaseJ4 {
     ZkTestServer server = new ZkTestServer(zkDir);
     try {
       server.run();
-      try (SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .build()) {
         CoreContainer cc = getCoreContainer();
         try {
           CloudConfig cloudConfig =
diff --git a/solr/core/src/test/org/apache/solr/cloud/ZkDistributedLockTest.java b/solr/core/src/test/org/apache/solr/cloud/ZkDistributedLockTest.java
index a28c56f2ed0..2c767b91fef 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ZkDistributedLockTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ZkDistributedLockTest.java
@@ -18,6 +18,7 @@
 package org.apache.solr.cloud;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.cloud.SolrZkClient;
 import org.apache.solr.common.params.CollectionParams;
@@ -48,7 +49,11 @@ public class ZkDistributedLockTest extends SolrTestCaseJ4 {
     ZkTestServer server = new ZkTestServer(createTempDir("zkData"));
     try {
       server.run();
-      try (SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .build()) {
         DistributedCollectionLockFactory collLockFactory =
             new ZkDistributedCollectionLockFactory(zkClient, "/lockTestCollectionRoot");
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/ZkSolrClientTest.java b/solr/core/src/test/org/apache/solr/cloud/ZkSolrClientTest.java
index 8c1ad8beea5..a9ed66f70ff 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ZkSolrClientTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ZkSolrClientTest.java
@@ -58,7 +58,11 @@ public class ZkSolrClientTest extends SolrTestCaseJ4 {
       server = new ZkTestServer(zkDir);
       server.run();
 
-      zkClient = new SolrZkClient(server.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
     }
 
     public ZkTestServer getServer() {
@@ -87,7 +91,10 @@ public class ZkSolrClientTest extends SolrTestCaseJ4 {
   public void testMakeRootNode() throws Exception {
     try (ZkConnection conn = new ZkConnection()) {
       final SolrZkClient zkClient =
-          new SolrZkClient(conn.getServer().getZkHost(), AbstractZkTestCase.TIMEOUT);
+          new SolrZkClient.Builder()
+              .withUrl(conn.getServer().getZkHost())
+              .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       try {
         assertTrue(zkClient.exists("/solr", true));
       } finally {
@@ -118,7 +125,10 @@ public class ZkSolrClientTest extends SolrTestCaseJ4 {
     server = new ZkTestServer(zkDir);
     server.run();
     try (SolrZkClient zkClient =
-        new SolrZkClient(server.getZkAddress(), AbstractZkTestCase.TIMEOUT); ) {
+        new SolrZkClient.Builder()
+            .withUrl(server.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
 
       String shardsPath = "/collections/collection1/shards";
       zkClient.makePath(shardsPath, false, true);
diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionApiLockingTest.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionApiLockingTest.java
index 04e55e2ffa6..fb943f6a5e6 100644
--- a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionApiLockingTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionApiLockingTest.java
@@ -18,6 +18,7 @@
 package org.apache.solr.cloud.api.collections;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.cloud.DistributedMultiLock;
 import org.apache.solr.cloud.ZkDistributedCollectionLockFactory;
@@ -42,7 +43,11 @@ public class CollectionApiLockingTest extends SolrTestCaseJ4 {
     ZkTestServer server = new ZkTestServer(createTempDir("zkData"));
     try {
       server.run();
-      try (SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT)) {
+      try (SolrZkClient zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .build()) {
         CollectionApiLockFactory apiLockFactory =
             new CollectionApiLockFactory(
                 new ZkDistributedCollectionLockFactory(zkClient, "/apiLockTestRoot"));
diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java
index c95724bc9fd..4cf897aaa4b 100644
--- a/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java
+++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java
@@ -1260,7 +1260,11 @@ public class TestCollectionAPI extends ReplicaPropertiesBase {
   public void testRecreateCollectionAfterFailure() throws Exception {
     // Upload a bad configset
     SolrZkClient zkClient =
-        new SolrZkClient(zkServer.getZkHost(), ZkTestServer.TIMEOUT, ZkTestServer.TIMEOUT, null);
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(ZkTestServer.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(ZkTestServer.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     ZkTestServer.putConfig(
         "badconf",
         zkClient,
diff --git a/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateReaderTest.java b/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateReaderTest.java
index 51a712221d9..38886c5e35f 100644
--- a/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateReaderTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateReaderTest.java
@@ -114,7 +114,10 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
     ZkTestServer server = new ZkTestServer(zkDir);
     server.run();
     SolrZkClient zkClient =
-        new SolrZkClient(server.getZkAddress(), OverseerTest.DEFAULT_CONNECTION_TIMEOUT);
+        new SolrZkClient.Builder()
+            .withUrl(server.getZkAddress())
+            .withTimeout(OverseerTest.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     ZkController.createClusterZkNodes(zkClient);
 
     ZkStateReader reader = new ZkStateReader(zkClient);
diff --git a/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateWriterTest.java b/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateWriterTest.java
index f5b2d4a25e1..12d5f9eaa02 100644
--- a/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateWriterTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateWriterTest.java
@@ -21,6 +21,7 @@ import java.nio.file.Path;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.lucene.util.IOUtils;
 import org.apache.solr.SolrTestCaseJ4;
@@ -77,7 +78,11 @@ public class ZkStateWriterTest extends SolrTestCaseJ4 {
     try {
       server.run();
 
-      zkClient = new SolrZkClient(server.getZkAddress(), OverseerTest.DEFAULT_CONNECTION_TIMEOUT);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(OverseerTest.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       ZkController.createClusterZkNodes(zkClient);
 
       try (ZkStateReader reader = new ZkStateReader(zkClient)) {
@@ -141,7 +146,11 @@ public class ZkStateWriterTest extends SolrTestCaseJ4 {
     try {
       server.run();
 
-      zkClient = new SolrZkClient(server.getZkAddress(), OverseerTest.DEFAULT_CONNECTION_TIMEOUT);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(OverseerTest.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       ZkController.createClusterZkNodes(zkClient);
 
       try (ZkStateReader reader = new ZkStateReader(zkClient)) {
@@ -220,7 +229,11 @@ public class ZkStateWriterTest extends SolrTestCaseJ4 {
     try {
       server.run();
 
-      zkClient = new SolrZkClient(server.getZkAddress(), OverseerTest.DEFAULT_CONNECTION_TIMEOUT);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(OverseerTest.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       ZkController.createClusterZkNodes(zkClient);
 
       try (ZkStateReader reader = new ZkStateReader(zkClient)) {
@@ -301,7 +314,11 @@ public class ZkStateWriterTest extends SolrTestCaseJ4 {
     try {
       server.run();
 
-      zkClient = new SolrZkClient(server.getZkAddress(), OverseerTest.DEFAULT_CONNECTION_TIMEOUT);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(OverseerTest.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       ZkController.createClusterZkNodes(zkClient);
 
       try (ZkStateReader reader = new ZkStateReader(zkClient)) {
@@ -350,7 +367,11 @@ public class ZkStateWriterTest extends SolrTestCaseJ4 {
     try {
       server.run();
 
-      zkClient = new SolrZkClient(server.getZkAddress(), OverseerTest.DEFAULT_CONNECTION_TIMEOUT);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(OverseerTest.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       ZkController.createClusterZkNodes(zkClient);
 
       try (ZkStateReader reader = new ZkStateReader(zkClient)) {
@@ -450,7 +471,11 @@ public class ZkStateWriterTest extends SolrTestCaseJ4 {
     try {
       server.run();
 
-      zkClient = new SolrZkClient(server.getZkAddress(), OverseerTest.DEFAULT_CONNECTION_TIMEOUT);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withConnTimeOut(OverseerTest.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
+              .build();
       ZkController.createClusterZkNodes(zkClient);
 
       try (ZkStateReader reader = new ZkStateReader(zkClient)) {
diff --git a/solr/core/src/test/org/apache/solr/schema/TestCloudManagedSchema.java b/solr/core/src/test/org/apache/solr/schema/TestCloudManagedSchema.java
index 590993ad107..c34b38ef5e7 100644
--- a/solr/core/src/test/org/apache/solr/schema/TestCloudManagedSchema.java
+++ b/solr/core/src/test/org/apache/solr/schema/TestCloudManagedSchema.java
@@ -69,7 +69,8 @@ public class TestCloudManagedSchema extends AbstractFullDistribZkTestBase {
           collectionSchema);
     }
 
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 30000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder().withUrl(zkServer.getZkHost() + ":30000)").build()) {
       // Make sure "DO NOT EDIT" is in the content of the managed schema
       String fileContent =
           getFileContentFromZooKeeper(zkClient, "/solr/configs/conf1/managed-schema.xml");
diff --git a/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaThreadSafety.java b/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaThreadSafety.java
index 38f32739efd..bd452178f63 100644
--- a/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaThreadSafety.java
+++ b/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaThreadSafety.java
@@ -25,6 +25,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.cloud.ZkController;
@@ -56,7 +57,10 @@ public class TestManagedSchemaThreadSafety extends SolrTestCaseJ4 {
     AtomicReference<Thread> slowpoke = new AtomicReference<>();
 
     private SuspendingZkClient(String zkServerAddress) {
-      super(zkServerAddress, ZK_CLIENT_TIMEOUT);
+      super(
+          new Builder()
+              .withUrl(zkServerAddress)
+              .withTimeout(ZK_CLIENT_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
     boolean isSlowpoke() {
diff --git a/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/SaslZkACLProviderTest.java b/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/SaslZkACLProviderTest.java
index 2d18e84c283..206c84eb764 100644
--- a/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/SaslZkACLProviderTest.java
+++ b/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/SaslZkACLProviderTest.java
@@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
+import java.util.concurrent.TimeUnit;
 import org.apache.lucene.tests.util.QuickPatchThreadsFilter;
 import org.apache.lucene.util.Constants;
 import org.apache.solr.SolrIgnoredThreadsFilter;
@@ -178,7 +179,10 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
   private static class SolrZkClientWithACLs extends SolrZkClient {
 
     public SolrZkClientWithACLs(String zkServerAddress, int zkClientTimeout) {
-      super(zkServerAddress, zkClientTimeout);
+      super(
+          new Builder()
+              .withUrl(zkServerAddress)
+              .withTimeout(zkClientTimeout, TimeUnit.MILLISECONDS));
     }
 
     @Override
@@ -191,7 +195,10 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
   private static class SolrZkClientNoACLs extends SolrZkClient {
 
     public SolrZkClientNoACLs(String zkServerAddress, int zkClientTimeout) {
-      super(zkServerAddress, zkClientTimeout);
+      super(
+          new Builder()
+              .withUrl(zkServerAddress)
+              .withTimeout(zkClientTimeout, TimeUnit.MILLISECONDS));
     }
 
     @Override
diff --git a/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/TestDelegationWithHadoopAuth.java b/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/TestDelegationWithHadoopAuth.java
index 6325719f68f..7acfa926aae 100644
--- a/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/TestDelegationWithHadoopAuth.java
+++ b/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/TestDelegationWithHadoopAuth.java
@@ -20,6 +20,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import org.apache.hadoop.security.authentication.client.PseudoAuthenticator;
 import org.apache.hadoop.util.Time;
 import org.apache.http.HttpStatus;
@@ -354,12 +355,13 @@ public class TestDelegationWithHadoopAuth extends SolrCloudTestCase {
   @Test
   public void testZNodePaths() throws Exception {
     getDelegationToken(null, USER_1, primarySolrClient);
-    SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(), 1000);
-    try {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(cluster.getZkServer().getZkAddress())
+            .withTimeout(1000, TimeUnit.MILLISECONDS)
+            .build()) {
       assertTrue(zkClient.exists("/security/zkdtsm", true));
       assertTrue(zkClient.exists("/security/token", true));
-    } finally {
-      zkClient.close();
     }
   }
 
diff --git a/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/TestSolrCloudWithDelegationTokens.java b/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/TestSolrCloudWithDelegationTokens.java
index 407a3026288..34815b0620e 100644
--- a/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/TestSolrCloudWithDelegationTokens.java
+++ b/solr/modules/hadoop-auth/src/test/org/apache/solr/security/hadoop/TestSolrCloudWithDelegationTokens.java
@@ -23,6 +23,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import org.apache.hadoop.util.Time;
 import org.apache.http.HttpStatus;
 import org.apache.solr.SolrTestCaseJ4;
@@ -392,12 +393,13 @@ public class TestSolrCloudWithDelegationTokens extends SolrTestCaseJ4 {
   @Test
   public void testZNodePaths() throws Exception {
     getDelegationToken(null, "bar", solrClientPrimary);
-    SolrZkClient zkClient = new SolrZkClient(miniCluster.getZkServer().getZkAddress(), 1000);
-    try {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(miniCluster.getZkServer().getZkAddress())
+            .withTimeout(1000, TimeUnit.MILLISECONDS)
+            .build()) {
       assertTrue(zkClient.exists("/security/zkdtsm", true));
       assertTrue(zkClient.exists("/security/token", true));
-    } finally {
-      zkClient.close();
     }
   }
 
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/SolrZkClient.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/SolrZkClient.java
index 361bcc0b7a3..bafa7f4a6ad 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/SolrZkClient.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/SolrZkClient.java
@@ -27,6 +27,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.LongAdder;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -106,72 +107,20 @@ public class SolrZkClient implements Closeable {
     return zkClientTimeout;
   }
 
-  // expert: for tests
-  public SolrZkClient() {}
-
-  public SolrZkClient(String zkServerAddress, int zkClientTimeout) {
-    this(zkServerAddress, zkClientTimeout, DEFAULT_CLIENT_CONNECT_TIMEOUT);
-  }
-
-  public SolrZkClient(String zkServerAddress, int zkClientTimeout, int zkClientConnectTimeout) {
-    this(zkServerAddress, zkClientTimeout, zkClientConnectTimeout, null);
-  }
-
-  public SolrZkClient(
-      String zkServerAddress,
-      int zkClientTimeout,
-      int zkClientConnectTimeout,
-      OnReconnect onReconnect) {
-    this(zkServerAddress, zkClientTimeout, zkClientConnectTimeout, null, onReconnect);
-  }
-
-  public SolrZkClient(
-      String zkServerAddress,
-      int zkClientTimeout,
-      ZkClientConnectionStrategy strat,
-      final OnReconnect onReconnect) {
-    this(zkServerAddress, zkClientTimeout, DEFAULT_CLIENT_CONNECT_TIMEOUT, strat, onReconnect);
-  }
-
-  public SolrZkClient(
-      String zkServerAddress,
-      int zkClientTimeout,
-      int clientConnectTimeout,
-      ZkClientConnectionStrategy strat,
-      final OnReconnect onReconnect) {
-    this(
-        zkServerAddress,
-        zkClientTimeout,
-        clientConnectTimeout,
-        strat,
-        onReconnect,
-        null,
-        null,
-        null);
-  }
-
-  public SolrZkClient(
-      String zkServerAddress,
-      int zkClientTimeout,
-      int clientConnectTimeout,
-      ZkClientConnectionStrategy strat,
-      final OnReconnect onReconnect,
-      BeforeReconnect beforeReconnect,
-      ZkACLProvider zkACLProvider,
-      IsClosed higherLevelIsClosed) {
+  public SolrZkClient(Builder builder) {
     this(
-        zkServerAddress,
-        zkClientTimeout,
-        clientConnectTimeout,
-        strat,
-        onReconnect,
-        beforeReconnect,
-        zkACLProvider,
-        higherLevelIsClosed,
-        null);
-  }
-
-  public SolrZkClient(
+        builder.zkServerAddress,
+        builder.zkClientTimeout,
+        builder.zkClientConnectTimeout,
+        builder.connectionStrategy,
+        builder.onReconnect,
+        builder.beforeReconnect,
+        builder.zkACLProvider,
+        builder.higherLevelIsClosed,
+        builder.compressor);
+  }
+
+  private SolrZkClient(
       String zkServerAddress,
       int zkClientTimeout,
       int clientConnectTimeout,
@@ -181,6 +130,11 @@ public class SolrZkClient implements Closeable {
       ZkACLProvider zkACLProvider,
       IsClosed higherLevelIsClosed,
       Compressor compressor) {
+
+    if (zkServerAddress == null) {
+      // only tests should create one without server address
+      return;
+    }
     this.zkServerAddress = zkServerAddress;
     this.higherLevelIsClosed = higherLevelIsClosed;
     if (strat == null) {
@@ -1152,4 +1106,66 @@ public class SolrZkClient implements Closeable {
       this.data = data;
     }
   }
+
+  public static class Builder {
+    public String zkServerAddress;
+    public int zkClientTimeout = DEFAULT_CLIENT_CONNECT_TIMEOUT;
+    public int zkClientConnectTimeout = DEFAULT_CLIENT_CONNECT_TIMEOUT;
+    public OnReconnect onReconnect;
+    public BeforeReconnect beforeReconnect;
+    public ZkClientConnectionStrategy connectionStrategy;
+    public ZkACLProvider zkACLProvider;
+    public IsClosed higherLevelIsClosed;
+
+    public Compressor compressor;
+
+    public Builder withUrl(String server) {
+      this.zkServerAddress = server;
+      return this;
+    }
+
+    public Builder withTimeout(int i, TimeUnit unit) {
+      this.zkClientTimeout = (int) unit.toMillis(i);
+      return this;
+    }
+
+    public Builder withConnTimeOut(int i, TimeUnit unit) {
+      this.zkClientConnectTimeout = (int) unit.toMillis(i);
+      return this;
+    }
+
+    public Builder withReconnectListener(OnReconnect onReconnect) {
+      this.onReconnect = onReconnect;
+      return this;
+    }
+
+    public Builder withConnStrategy(ZkClientConnectionStrategy strat) {
+      this.connectionStrategy = strat;
+      return this;
+    }
+
+    public Builder withBeforeConnect(BeforeReconnect beforeReconnect) {
+      this.beforeReconnect = beforeReconnect;
+      return this;
+    }
+
+    public Builder withAclProvider(ZkACLProvider zkACLProvider) {
+      this.zkACLProvider = zkACLProvider;
+      return this;
+    }
+
+    public Builder withClosedCheck(IsClosed higherLevelIsClosed) {
+      this.higherLevelIsClosed = higherLevelIsClosed;
+      return this;
+    }
+
+    public Builder withCompressor(Compressor c) {
+      this.compressor = c;
+      return this;
+    }
+
+    public SolrZkClient build() {
+      return new SolrZkClient(this);
+    }
+  }
 }
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java
index d714dfee54c..739baa254c1 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java
@@ -413,29 +413,27 @@ public class ZkStateReader implements SolrCloseable {
 
   public ZkStateReader(String zkServerAddress, int zkClientTimeout, int zkClientConnectTimeout) {
     this.zkClient =
-        new SolrZkClient(
-            zkServerAddress,
-            zkClientTimeout,
-            zkClientConnectTimeout,
-            // on reconnect, reload cloud info
-            new OnReconnect() {
-              @Override
-              public void command() {
-                try {
-                  ZkStateReader.this.createClusterStateWatchersAndUpdate();
-                } catch (KeeperException e) {
-                  log.error("A ZK error has occurred", e);
-                  throw new ZooKeeperException(
-                      SolrException.ErrorCode.SERVER_ERROR, "A ZK error has occurred", e);
-                } catch (InterruptedException e) {
-                  // Restore the interrupted status
-                  Thread.currentThread().interrupt();
-                  log.error("Interrupted", e);
-                  throw new ZooKeeperException(
-                      SolrException.ErrorCode.SERVER_ERROR, "Interrupted", e);
-                }
-              }
-            });
+        new SolrZkClient.Builder()
+            .withUrl(zkServerAddress)
+            .withTimeout(zkClientTimeout, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(zkClientConnectTimeout, TimeUnit.MILLISECONDS)
+            .withReconnectListener(
+                () -> {
+                  // on reconnect, reload cloud info
+                  try {
+                    this.createClusterStateWatchersAndUpdate();
+                  } catch (KeeperException e) {
+                    log.error("A ZK error has occurred", e);
+                    throw new ZooKeeperException(
+                        ErrorCode.SERVER_ERROR, "A ZK error has occurred", e);
+                  } catch (InterruptedException e) {
+                    // Restore the interrupted status
+                    Thread.currentThread().interrupt();
+                    log.error("Interrupted", e);
+                    throw new ZooKeeperException(ErrorCode.SERVER_ERROR, "Interrupted", e);
+                  }
+                })
+            .build();
     this.closeClient = true;
     this.securityNodeListener = null;
 
diff --git a/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/SolrZkClientTest.java b/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/SolrZkClientTest.java
index a86e9264225..60a60dc4e0f 100644
--- a/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/SolrZkClientTest.java
+++ b/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/SolrZkClientTest.java
@@ -73,16 +73,27 @@ public class SolrZkClientTest extends SolrCloudTestCase {
     zkServer = new ZkTestServer(zkDir);
     zkServer.run();
 
-    try (SolrZkClient client = new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT)) {
+    try (SolrZkClient client =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       // Set up chroot
       client.makePath("/solr", false, true);
     }
 
-    defaultClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+    defaultClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     defaultClient.makePath(PATH, true);
 
     aclClient =
-        new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT) {
+        new SolrZkClient(
+            new SolrZkClient.Builder()
+                .withUrl(zkServer.getZkAddress())
+                .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)) {
           @Override
           protected ZkACLProvider createZkACLProvider() {
             return new DefaultZkACLProvider() {
@@ -100,7 +111,10 @@ public class SolrZkClientTest extends SolrCloudTestCase {
         };
 
     credentialsClient =
-        new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT) {
+        new SolrZkClient(
+            new SolrZkClient.Builder()
+                .withUrl(zkServer.getZkAddress())
+                .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)) {
           @Override
           protected ZkCredentialsProvider createZkCredentialsToAddAutomatically() {
             return new DefaultZkCredentialsProvider() {
diff --git a/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkConfigSetService.java b/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkConfigSetService.java
index eccc739729d..3981176a615 100644
--- a/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkConfigSetService.java
+++ b/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkConfigSetService.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Properties;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrJettyTestBase;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.cloud.AbstractZkTestCase;
@@ -74,7 +75,11 @@ public class TestZkConfigSetService extends SolrTestCaseJ4 {
 
     zkServer.ensurePathExists("/solr");
 
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress("/solr"), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress("/solr"))
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       ConfigSetService configSetService = new ZkConfigSetService(zkClient);
 
       assertEquals(0, configSetService.listConfigs().size());
@@ -222,7 +227,11 @@ public class TestZkConfigSetService extends SolrTestCaseJ4 {
     }
 
     // Client with no auth whatsoever can't even get the list of configs
-    try (SolrZkClient client = new SolrZkClient(zkServer.getZkAddress("/acl"), 10000)) {
+    try (SolrZkClient client =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress("/acl"))
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       ConfigSetService configSetService = new ZkConfigSetService(client);
       configSetService.listConfigs();
       fail("Should have thrown an ACL exception");
@@ -239,7 +248,11 @@ public class TestZkConfigSetService extends SolrTestCaseJ4 {
     CoreContainer cc = new CoreContainer(Paths.get(solrHome), new Properties());
     System.setProperty("zkHost", zkServer.getZkAddress());
 
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT);
+    SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     zkClient.makePath("/solr", false, true);
     cc.setCoreConfigService(new ZkConfigSetService(zkClient));
     assertFalse(cc.getConfigSetService().checkConfigExists("collection1"));
@@ -253,7 +266,8 @@ public class TestZkConfigSetService extends SolrTestCaseJ4 {
       String zkAddress,
       final ZkACLProvider aclProvider,
       final ZkCredentialsProvider credentialsProvider) {
-    return new SolrZkClient(zkAddress, 10000) {
+    return new SolrZkClient(
+        new SolrZkClient.Builder().withUrl(zkAddress).withTimeout(10000, TimeUnit.MILLISECONDS)) {
       @Override
       protected ZkCredentialsProvider createZkCredentialsToAddAutomatically() {
         return credentialsProvider;
diff --git a/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkMaintenanceUtils.java b/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkMaintenanceUtils.java
index b2c92e86cde..89677753f76 100644
--- a/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkMaintenanceUtils.java
+++ b/solr/solrj-zookeeper/src/test/org/apache/solr/common/cloud/TestZkMaintenanceUtils.java
@@ -18,13 +18,13 @@ package org.apache.solr.common.cloud;
 
 import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import org.apache.commons.io.file.PathUtils;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrServerException;
@@ -67,13 +67,14 @@ public class TestZkMaintenanceUtils extends SolrTestCaseJ4 {
    * @throws InterruptedException when having trouble creating test nodes
    * @throws KeeperException error when talking to zookeeper
    * @throws SolrServerException when having trouble connecting to solr
-   * @throws UnsupportedEncodingException when getBytes() uses unknown encoding
    */
   @Test
-  public void testClean()
-      throws KeeperException, InterruptedException, SolrServerException,
-          UnsupportedEncodingException {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+  public void testClean() throws KeeperException, InterruptedException, SolrServerException {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       /* PREPARE */
       String path = "/myPath/isTheBest";
       String data1 = "myStringData1";
@@ -132,7 +133,11 @@ public class TestZkMaintenanceUtils extends SolrTestCaseJ4 {
 
   @Test
   public void testTraverseZkTree() throws Exception {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       zkClient.makePath("/testTraverseZkTree/1/1", true, true);
       zkClient.makePath("/testTraverseZkTree/1/2", false, true);
       zkClient.makePath("/testTraverseZkTree/2", false, true);
@@ -160,7 +165,11 @@ public class TestZkMaintenanceUtils extends SolrTestCaseJ4 {
   // SOLR-14993
   @Test
   public void testOneByteFile() throws Exception {
-    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), 10000)) {
+    try (SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       byte[] oneByte = new byte[1];
       oneByte[0] = 0x30;
       zkClient.makePath("/test1byte/one", oneByte, true);
diff --git a/solr/solrj/src/test/org/apache/solr/common/cloud/SolrZkClientCompressedDataTest.java b/solr/solrj/src/test/org/apache/solr/common/cloud/SolrZkClientCompressedDataTest.java
index b8ab6f1e3d8..82682e5e122 100644
--- a/solr/solrj/src/test/org/apache/solr/common/cloud/SolrZkClientCompressedDataTest.java
+++ b/solr/solrj/src/test/org/apache/solr/common/cloud/SolrZkClientCompressedDataTest.java
@@ -20,6 +20,7 @@ package org.apache.solr.common.cloud;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 import org.apache.lucene.util.IOUtils;
 import org.apache.solr.SolrTestCase;
 import org.apache.solr.cloud.ZkController;
@@ -44,7 +45,11 @@ public class SolrZkClientCompressedDataTest extends SolrTestCase {
     try {
       server.run();
 
-      zkClient = new SolrZkClient(server.getZkAddress(), 60000);
+      zkClient =
+          new SolrZkClient.Builder()
+              .withUrl(server.getZkAddress())
+              .withTimeout(60000, TimeUnit.MILLISECONDS)
+              .build();
       ZkController.createClusterZkNodes(zkClient);
       zkClient.makePath(ZkStateReader.COLLECTIONS_ZKNODE + "/c1", true);
 
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractDigestZkACLAndCredentialsProvidersTestBase.java b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractDigestZkACLAndCredentialsProvidersTestBase.java
index f02eab7150b..80dfaa93f01 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractDigestZkACLAndCredentialsProvidersTestBase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractDigestZkACLAndCredentialsProvidersTestBase.java
@@ -30,6 +30,7 @@ import java.nio.file.Path;
 import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.cloud.DigestZkACLProvider;
 import org.apache.solr.common.cloud.DigestZkCredentialsProvider;
@@ -97,13 +98,20 @@ public class AbstractDigestZkACLAndCredentialsProvidersTestBase extends SolrTest
         AllAndReadonlyCredentialZkCredentialsInjector.class.getName());
 
     SolrZkClient zkClient =
-        new SolrZkClient(
-            zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT, AbstractZkTestCase.TIMEOUT);
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
 
     zkClient.makePath("/solr", false, true);
     zkClient.close();
 
-    zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+    zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     zkClient.create(
         "/protectedCreateNode", "content".getBytes(DATA_ENCODING), CreateMode.PERSISTENT, false);
     zkClient.makePath(
@@ -118,7 +126,11 @@ public class AbstractDigestZkACLAndCredentialsProvidersTestBase extends SolrTest
 
     clearSecuritySystemProperties();
 
-    zkClient = new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT);
+    zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     // Currently, no credentials on ZK connection, because those same VM-params are used for adding
     // ACLs, and here we want
     // no (or completely open) ACLs added. Therefore, hack your way into being authorized for
@@ -262,7 +274,10 @@ public class AbstractDigestZkACLAndCredentialsProvidersTestBase extends SolrTest
       setUp();
       testZkCredentialsInjector.setSystemProps();
       try (SolrZkClient zkClient =
-          new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
+          new SolrZkClient.Builder()
+              .withUrl(zkServer.getZkAddress())
+              .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+              .build()) {
         doTest(
             zkClient,
             getData,
@@ -283,7 +298,10 @@ public class AbstractDigestZkACLAndCredentialsProvidersTestBase extends SolrTest
   public void testRepairACL() throws Exception {
     clearSecuritySystemProperties();
     try (SolrZkClient zkClient =
-        new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       // Currently, no credentials on ZK connection, because those same VM-params are used for
       // adding ACLs, and here we want
       // no (or completely open) ACLs added. Therefore, hack your way into being authorized for
@@ -302,7 +320,10 @@ public class AbstractDigestZkACLAndCredentialsProvidersTestBase extends SolrTest
 
     setSecuritySystemProperties();
     try (SolrZkClient zkClient =
-        new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       ZkController.createClusterZkNodes(zkClient);
       assertNotEquals(OPEN_ACL_UNSAFE, zkClient.getACL("/security.json", null, false));
     }
@@ -310,7 +331,10 @@ public class AbstractDigestZkACLAndCredentialsProvidersTestBase extends SolrTest
     useZkCredentialsInjector(ConnectWithReadonlyCredsInjector.class);
     // useReadonlyCredentials();
     try (SolrZkClient zkClient =
-        new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       NoAuthException e =
           assertThrows(
               NoAuthException.class, () -> zkClient.getData("/security.json", null, null, false));
@@ -419,7 +443,10 @@ public class AbstractDigestZkACLAndCredentialsProvidersTestBase extends SolrTest
     useVMParamsAllCredentialsFromFile();
 
     try (SolrZkClient zkClient =
-        new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       doTest(zkClient, true, true, true, true, true, true, true, true, true, true);
     }
   }
@@ -429,7 +456,10 @@ public class AbstractDigestZkACLAndCredentialsProvidersTestBase extends SolrTest
     useVMParamsReadonlyCredentialsFromFile();
 
     try (SolrZkClient zkClient =
-        new SolrZkClient(zkServer.getZkAddress(), AbstractZkTestCase.TIMEOUT)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       doTest(zkClient, true, true, false, false, false, false, false, false, false, false);
     }
   }
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractDistribZkTestBase.java b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractDistribZkTestBase.java
index c74396c9bdd..9f904f11f8c 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractDistribZkTestBase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractDistribZkTestBase.java
@@ -383,7 +383,11 @@ public abstract class AbstractDistribZkTestBase extends BaseDistributedSearchTes
   }
 
   protected void printLayout() throws Exception {
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT);
+    SolrZkClient zkClient =
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build();
     zkClient.printLayoutToStream(System.out);
     zkClient.close();
   }
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
index 335263c6715..08c46fbac63 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
@@ -351,7 +351,10 @@ public class MiniSolrCloudCluster {
     this.zkServer = zkTestServer;
 
     try (SolrZkClient zkClient =
-        new SolrZkClient(zkServer.getZkHost(), AbstractZkTestCase.TIMEOUT)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkHost())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       if (!zkClient.exists("/solr/solr.xml", true)) {
         zkClient.makePath("/solr/solr.xml", solrXml.getBytes(Charset.defaultCharset()), true);
         if (jettyConfig.sslConfig != null && jettyConfig.sslConfig.isSSLMode()) {
@@ -627,11 +630,11 @@ public class MiniSolrCloudCluster {
    */
   public void uploadConfigSet(Path configDir, String configName) throws IOException {
     try (SolrZkClient zkClient =
-        new SolrZkClient(
-            zkServer.getZkAddress(),
-            AbstractZkTestCase.TIMEOUT,
-            AbstractZkTestCase.TIMEOUT,
-            null)) {
+        new SolrZkClient.Builder()
+            .withUrl(zkServer.getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .build()) {
       ZkMaintenanceUtils.uploadToZK(
           zkClient,
           configDir,
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/MockSolrZkClient.java b/solr/test-framework/src/java/org/apache/solr/cloud/MockSolrZkClient.java
index 9ab12d46854..c1c73fb853a 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/MockSolrZkClient.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/MockSolrZkClient.java
@@ -25,7 +25,7 @@ import org.apache.zookeeper.data.Stat;
 public class MockSolrZkClient extends SolrZkClient {
 
   public MockSolrZkClient() {
-    super();
+    super(new Builder());
   }
 
   @Override
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java b/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
index 9bd2fd73ccf..6584bf0904d 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
@@ -36,6 +36,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 import javax.management.JMException;
 import org.apache.solr.SolrTestCaseJ4;
@@ -441,10 +442,20 @@ public class ZkTestServer {
 
   private void init(boolean solrFormat) throws Exception {
     try {
-      rootClient = new SolrZkClient(getZkHost(), TIMEOUT, 30000);
+      rootClient =
+          new SolrZkClient.Builder()
+              .withUrl(getZkHost())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .withConnTimeOut(30000, TimeUnit.MILLISECONDS)
+              .build();
     } catch (Exception e) {
       log.error("error making rootClient, trying one more time", e);
-      rootClient = new SolrZkClient(getZkHost(), TIMEOUT, 30000);
+      rootClient =
+          new SolrZkClient.Builder()
+              .withUrl(getZkHost())
+              .withTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+              .withConnTimeOut(30000, TimeUnit.MILLISECONDS)
+              .build();
     }
 
     if (solrFormat) {
@@ -452,7 +463,12 @@ public class ZkTestServer {
       makeSolrZkNode();
     }
 
-    chRootClient = new SolrZkClient(getZkAddress(), AbstractZkTestCase.TIMEOUT, 30000);
+    chRootClient =
+        new SolrZkClient.Builder()
+            .withUrl(getZkAddress())
+            .withTimeout(AbstractZkTestCase.TIMEOUT, TimeUnit.MILLISECONDS)
+            .withConnTimeOut(30000, TimeUnit.MILLISECONDS)
+            .build();
   }
 
   public String getZkHost() {
@@ -484,7 +500,11 @@ public class ZkTestServer {
    * @throws IOException if an IO exception occurs
    */
   public void ensurePathExists(String path) throws IOException {
-    try (SolrZkClient client = new SolrZkClient(getZkHost(), 10000)) {
+    try (SolrZkClient client =
+        new SolrZkClient.Builder()
+            .withUrl(getZkHost())
+            .withTimeout(10000, TimeUnit.MILLISECONDS)
+            .build()) {
       client.makePath(path, null, CreateMode.PERSISTENT, null, false, true, 0);
     } catch (InterruptedException | KeeperException e) {
       log.error("Error checking path {}", path, e);