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

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


The following commit(s) were added to refs/heads/main by this push:
     new 7cf16cdba67 SOLR-17877: Move distributedCollectionCommandRunner to 
ZkController (#3507)
7cf16cdba67 is described below

commit 7cf16cdba67aae50b36edba12e0536f6d7b7e4c6
Author: David Smiley <[email protected]>
AuthorDate: Thu Aug 28 21:02:48 2025 -0400

    SOLR-17877: Move distributedCollectionCommandRunner to ZkController (#3507)
    
    A refactoring to improve separation-of-concerns.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] 
<[email protected]>
    Co-authored-by: David Smiley <[email protected]>
---
 .../java/org/apache/solr/cloud/ZkController.java   | 29 +++++++++++-
 .../DistributedCollectionCommandContext.java       | 18 +++-----
 ...istributedCollectionConfigSetCommandRunner.java |  8 ++--
 .../cloud/api/collections/OverseerStatusCmd.java   |  2 +-
 .../java/org/apache/solr/core/CoreContainer.java   | 43 +-----------------
 .../solr/handler/admin/CollectionsHandler.java     | 52 ++++++++--------------
 .../solr/handler/admin/api/AddReplicaProperty.java |  3 +-
 .../solr/handler/admin/api/AdminAPIBase.java       |  6 +--
 .../solr/handler/admin/api/AliasProperty.java      |  3 +-
 .../solr/handler/admin/api/BalanceReplicas.java    |  3 +-
 .../apache/solr/handler/admin/api/CreateAlias.java |  3 +-
 .../solr/handler/admin/api/CreateCollection.java   |  3 +-
 .../handler/admin/api/CreateCollectionBackup.java  |  3 +-
 .../admin/api/CreateCollectionSnapshot.java        |  3 +-
 .../apache/solr/handler/admin/api/DeleteAlias.java |  3 +-
 .../solr/handler/admin/api/DeleteCollection.java   |  3 +-
 .../admin/api/DeleteCollectionSnapshot.java        |  3 +-
 .../apache/solr/handler/admin/api/DeleteNode.java  |  3 +-
 .../handler/admin/api/DeleteReplicaProperty.java   |  3 +-
 .../solr/handler/admin/api/InstallShardData.java   |  3 +-
 .../solr/handler/admin/api/MigrateReplicas.java    |  3 +-
 .../apache/solr/handler/admin/api/ReplaceNode.java |  3 +-
 .../solr/handler/admin/api/RestoreCollection.java  |  3 +-
 .../solr/handler/configsets/ConfigSetAPIBase.java  | 16 +++----
 .../admin/api/AddReplicaPropertyAPITest.java       |  7 ++-
 .../handler/admin/api/MigrateReplicasAPITest.java  |  7 ++-
 .../solr/handler/admin/api/ReplaceNodeAPITest.java |  7 ++-
 27 files changed, 98 insertions(+), 145 deletions(-)

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 5309047943a..6d3d52eb402 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -66,6 +66,7 @@ import 
org.apache.solr.client.solrj.impl.SolrClientCloudManager;
 import org.apache.solr.client.solrj.impl.SolrZkClientTimeout;
 import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
 import org.apache.solr.client.solrj.request.CoreAdminRequest.WaitForState;
+import 
org.apache.solr.cloud.api.collections.DistributedCollectionConfigSetCommandRunner;
 import org.apache.solr.cloud.overseer.ClusterStateMutator;
 import org.apache.solr.cloud.overseer.OverseerAction;
 import org.apache.solr.cloud.overseer.SliceMutator;
@@ -223,6 +224,8 @@ public class ZkController implements Closeable {
 
   private final DistributedClusterStateUpdater distributedClusterStateUpdater;
 
+  private final Optional<DistributedCollectionConfigSetCommandRunner> 
distributedCommandRunner;
+
   private LeaderElector overseerElector;
 
   private Map<String, ReplicateFromLeader> replicateFromLeaders = new 
ConcurrentHashMap<>();
@@ -370,6 +373,11 @@ public class ZkController implements Closeable {
     // Refuse to start if ZK has a non empty /clusterstate.json or a /solr.xml 
file
     checkNoOldClusterstate(zkClient);
 
+    this.distributedCommandRunner =
+        cloudConfig.getDistributedCollectionConfigSetExecution()
+            ? Optional.of(new DistributedCollectionConfigSetCommandRunner(cc, 
zkClient))
+            : Optional.empty();
+
     this.overseerRunningMap = Overseer.getRunningMap(zkClient);
     this.overseerCompletedMap = Overseer.getCompletedMap(zkClient);
     this.overseerFailureMap = Overseer.getFailureMap(zkClient);
@@ -681,6 +689,22 @@ public class ZkController implements Closeable {
     return sysPropsCacher;
   }
 
+  /** Non-empty if the Collection API is executed in a distributed way 
(Overseer is disabled). */
+  public Optional<DistributedCollectionConfigSetCommandRunner> 
getDistributedCommandRunner() {
+    return this.distributedCommandRunner;
+  }
+
+  /** Waits for pending tasks to complete. Should be called before {@link 
#close()}. */
+  public void waitForPendingTasksToComplete() {
+    if (distributedCommandRunner.isPresent()) {
+      // Local (i.e. distributed) Collection API processing
+      distributedCommandRunner.get().stopAndWaitForPendingTasksToComplete();
+    } else {
+      // Overseer based processing
+      getOverseerCollectionQueue().allowOverseerPendingTasksToComplete();
+    }
+  }
+
   private ContextKey closeExistingElectionContext(CoreDescriptor cd, boolean 
sessionExpired) {
     // look for old context - if we find it, cancel it
     String collection = cd.getCloudDescriptor().getCollectionName();
@@ -779,6 +803,7 @@ public class ZkController implements Closeable {
     } finally {
 
       sysPropsCacher.close();
+
       customThreadPool.execute(() -> IOUtils.closeQuietly(cloudManager));
       customThreadPool.execute(() -> IOUtils.closeQuietly(cloudSolrClient));
 
@@ -998,7 +1023,9 @@ public class ZkController implements Closeable {
       checkForExistingEphemeralNode();
       registerLiveNodesListener();
 
-      // start the overseer first as following code may need it's processing
+      // Start the overseer now since the following code may need it's 
processing.
+      // Note: even when using distributed processing, we still create an 
Overseer anyway since
+      //    cluster singleton processing is linked to the elected Overseer.
       if (!zkRunOnly) {
         overseerElector = new LeaderElector(zkClient);
         this.overseer =
diff --git 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionCommandContext.java
 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionCommandContext.java
index 800c4bde1b1..1e440a534fc 100644
--- 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionCommandContext.java
+++ 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionCommandContext.java
@@ -27,23 +27,17 @@ import org.apache.solr.handler.component.ShardHandler;
 
 public class DistributedCollectionCommandContext implements 
CollectionCommandContext {
   private final CoreContainer coreContainer;
-  private final DistributedClusterStateUpdater 
getDistributedClusterStateUpdater;
+  private final DistributedClusterStateUpdater distributedClusterStateUpdater;
   private final ExecutorService executorService;
 
-  private final SolrCloudManager solrCloudManager;
-  private final ZkStateReader zkStateReader;
-
   public DistributedCollectionCommandContext(
       CoreContainer coreContainer, ExecutorService executorService) {
+    // note: coreContainer.getZkController() is not yet instantiated; don't 
call it right now
     this.coreContainer = coreContainer;
-    this.getDistributedClusterStateUpdater =
+    this.distributedClusterStateUpdater =
         new DistributedClusterStateUpdater(
             
coreContainer.getConfig().getCloudConfig().getDistributedClusterStateUpdates());
-    ;
     this.executorService = executorService;
-
-    solrCloudManager = 
this.coreContainer.getZkController().getSolrCloudManager();
-    zkStateReader = this.coreContainer.getZkController().getZkStateReader();
   }
 
   @Override
@@ -60,7 +54,7 @@ public class DistributedCollectionCommandContext implements 
CollectionCommandCon
 
   @Override
   public SolrCloudManager getSolrCloudManager() {
-    return solrCloudManager;
+    return this.coreContainer.getZkController().getSolrCloudManager();
   }
 
   @Override
@@ -70,12 +64,12 @@ public class DistributedCollectionCommandContext implements 
CollectionCommandCon
 
   @Override
   public ZkStateReader getZkStateReader() {
-    return zkStateReader;
+    return this.coreContainer.getZkController().getZkStateReader();
   }
 
   @Override
   public DistributedClusterStateUpdater getDistributedClusterStateUpdater() {
-    return this.getDistributedClusterStateUpdater;
+    return this.distributedClusterStateUpdater;
   }
 
   @Override
diff --git 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionConfigSetCommandRunner.java
 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionConfigSetCommandRunner.java
index bd7c8404886..5778198db0f 100644
--- 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionConfigSetCommandRunner.java
+++ 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionConfigSetCommandRunner.java
@@ -44,6 +44,7 @@ import org.apache.solr.cloud.OverseerSolrResponse;
 import org.apache.solr.cloud.ZkDistributedCollectionLockFactory;
 import org.apache.solr.cloud.ZkDistributedConfigSetLockFactory;
 import org.apache.solr.common.SolrException;
+import org.apache.solr.common.cloud.SolrZkClient;
 import org.apache.solr.common.cloud.ZkNodeProps;
 import org.apache.solr.common.params.CollectionParams;
 import org.apache.solr.common.params.ConfigSetParams;
@@ -106,7 +107,9 @@ public class DistributedCollectionConfigSetCommandRunner {
 
   private volatile boolean shuttingDown = false;
 
-  public DistributedCollectionConfigSetCommandRunner(CoreContainer 
coreContainer) {
+  public DistributedCollectionConfigSetCommandRunner(
+      CoreContainer coreContainer, SolrZkClient zkClient) {
+    // note: coreContainer.getZkController() is not yet instantiated; don't 
call it right now
     this.coreContainer = coreContainer;
 
     if (log.isInfoEnabled()) {
@@ -144,8 +147,7 @@ public class DistributedCollectionConfigSetCommandRunner {
         new DistributedCollectionCommandContext(
             this.coreContainer, this.distributedCollectionApiExecutorService);
     commandMapper = new CollApiCmds.CommandMap(ccc);
-    asyncTaskTracker =
-        new DistributedApiAsyncTracker(ccc.getZkStateReader().getZkClient(), 
ZK_ASYNC_ROOT);
+    asyncTaskTracker = new DistributedApiAsyncTracker(zkClient, ZK_ASYNC_ROOT);
   }
 
   /** See {@link DistributedApiAsyncTracker#getAsyncTaskRequestStatus(String)} 
*/
diff --git 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerStatusCmd.java
 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerStatusCmd.java
index 0b841fe8790..0edfb81ad98 100644
--- 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerStatusCmd.java
+++ 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerStatusCmd.java
@@ -165,7 +165,7 @@ public class OverseerStatusCmd implements 
CollApiCmds.CollectionApiCommand {
       throws Exception {
     // If Collection API execution is distributed, we're not running on the 
Overseer node so can't
     // return any Overseer stats.
-    if 
(ccc.getCoreContainer().getDistributedCollectionCommandRunner().isPresent()) {
+    if 
(ccc.getCoreContainer().getZkController().getDistributedCommandRunner().isPresent())
 {
       // TODO: introduce a per node status command allowing insight into how 
Cluster state updates,
       // Collection API and config set API execution went on that node...
       return;
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java 
b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index 5a3eda738b6..83f7657bc51 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -45,7 +45,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Properties;
 import java.util.Set;
 import java.util.UUID;
@@ -77,9 +76,7 @@ import org.apache.solr.client.solrj.io.SolrClientCache;
 import org.apache.solr.client.solrj.util.SolrIdentifierValidator;
 import org.apache.solr.cloud.CloudDescriptor;
 import org.apache.solr.cloud.ClusterSingleton;
-import org.apache.solr.cloud.OverseerTaskQueue;
 import org.apache.solr.cloud.ZkController;
-import 
org.apache.solr.cloud.api.collections.DistributedCollectionConfigSetCommandRunner;
 import org.apache.solr.cluster.events.ClusterEventProducer;
 import org.apache.solr.cluster.events.impl.ClusterEventProducerFactory;
 import org.apache.solr.cluster.placement.PlacementPluginConfig;
@@ -326,14 +323,6 @@ public class CoreContainer {
   private ExecutorService coreContainerAsyncTaskExecutor =
       ExecutorUtil.newMDCAwareCachedThreadPool("Core Container Async Task");
 
-  /**
-   * Non-empty if the Collection API is executed in a distributed way and not 
on Overseer, once the
-   * CoreContainer has been initialized properly, i.e. method {@link #load()} 
called. Until then it
-   * is null, and it is not expected to be read.
-   */
-  private volatile Optional<DistributedCollectionConfigSetCommandRunner>
-      distributedCollectionCommandRunner;
-
   private enum CoreInitFailedAction {
     fromleader,
     none
@@ -686,7 +675,6 @@ public class CoreContainer {
     cfg = null;
     containerProperties = null;
     replayUpdatesExecutor = null;
-    distributedCollectionCommandRunner = Optional.empty();
     allowPaths = null;
     allowListUrlChecker = null;
     indexSearcherExecutor = null;
@@ -900,19 +888,6 @@ public class CoreContainer {
     createHandler(
         ZK_STATUS_PATH, ZookeeperStatusHandler.class.getName(), 
ZookeeperStatusHandler.class);
 
-    // CoreContainer is initialized enough at this stage so we can set
-    // distributedCollectionCommandRunner (the construction of
-    // DistributedCollectionConfigSetCommandRunner uses Zookeeper so can't be 
done from the
-    // CoreContainer constructor because there Zookeeper is not yet ready). 
Given this is used in
-    // the CollectionsHandler created next line, this is the latest point where
-    // distributedCollectionCommandRunner can be initialized without 
refactoring this method...
-    // TODO: manage to completely build CoreContainer in the constructor and 
not in the load()
-    // method... Requires some test refactoring.
-    this.distributedCollectionCommandRunner =
-        isZooKeeperAware() && 
cfg.getCloudConfig().getDistributedCollectionConfigSetExecution()
-            ? Optional.of(new 
DistributedCollectionConfigSetCommandRunner(this))
-            : Optional.empty();
-
     collectionsHandler =
         createHandler(
             COLLECTIONS_HANDLER_PATH, cfg.getCollectionsHandlerClass(), 
CollectionsHandler.class);
@@ -1246,9 +1221,7 @@ public class CoreContainer {
           throw new SolrException(ErrorCode.SERVER_ERROR, e);
         }
       }
-      if (!distributedCollectionCommandRunner.isPresent()) {
-        zkSys.getZkController().checkOverseerDesignate();
-      }
+      zkSys.getZkController().checkOverseerDesignate();
     }
 
     // This is a bit redundant but these are two distinct concepts for all 
they're accomplished at
@@ -1318,14 +1291,7 @@ public class CoreContainer {
 
     ZkController zkController = getZkController();
     if (zkController != null) {
-      if (distributedCollectionCommandRunner.isPresent()) {
-        // Local (i.e. distributed) Collection API processing
-        
distributedCollectionCommandRunner.get().stopAndWaitForPendingTasksToComplete();
-      } else {
-        // Overseer based processing
-        OverseerTaskQueue overseerCollectionQueue = 
zkController.getOverseerCollectionQueue();
-        overseerCollectionQueue.allowOverseerPendingTasksToComplete();
-      }
+      zkController.waitForPendingTasksToComplete();
     }
     if (log.isInfoEnabled()) {
       log.info("Shutting down CoreContainer instance={}", 
System.identityHashCode(this));
@@ -2595,11 +2561,6 @@ public class CoreContainer {
     return placementPluginFactory;
   }
 
-  public Optional<DistributedCollectionConfigSetCommandRunner>
-      getDistributedCollectionCommandRunner() {
-    return this.distributedCollectionCommandRunner;
-  }
-
   /**
    * A general-purpose HTTP/2 Solr client.
    *
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java 
b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
index 85cc036d9ed..c2cd0148938 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
@@ -221,8 +221,6 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   protected final CoreContainer coreContainer;
-  private final Optional<DistributedCollectionConfigSetCommandRunner>
-      distributedCollectionConfigSetCommandRunner;
 
   public CollectionsHandler() {
     // Unlike most request handlers, CoreContainer initialization
@@ -237,10 +235,6 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
    */
   public CollectionsHandler(final CoreContainer coreContainer) {
     this.coreContainer = coreContainer;
-    distributedCollectionConfigSetCommandRunner =
-        coreContainer != null
-            ? coreContainer.getDistributedCollectionCommandRunner()
-            : Optional.empty();
   }
 
   @Override
@@ -350,12 +344,7 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
   }
 
   public static SolrResponse submitCollectionApiCommand(
-      CoreContainer coreContainer,
-      Optional<DistributedCollectionConfigSetCommandRunner>
-          distributedCollectionConfigSetCommandRunner,
-      ZkNodeProps m,
-      CollectionAction action,
-      long timeout)
+      ZkController zkController, ZkNodeProps m, CollectionAction action, long 
timeout)
       throws KeeperException, InterruptedException {
     // Collection API messages are either sent to Overseer and processed 
there, or processed
     // locally. Distributing Collection API implies we're also distributing 
Cluster State Updates.
@@ -367,10 +356,10 @@ public class CollectionsHandler extends 
RequestHandlerBase implements Permission
     // same JVM as the Overseer based cluster state update... The 
configuration handling includes
     // these checks to not allow distributing collection API without 
distributing cluster state
     // updates (but the other way around is ok). See constructor of 
CloudConfig.
-    if (distributedCollectionConfigSetCommandRunner.isPresent()) {
-      return distributedCollectionConfigSetCommandRunner
-          .get()
-          .runCollectionCommand(m, action, timeout);
+    Optional<DistributedCollectionConfigSetCommandRunner> distribCommandRunner 
=
+        zkController.getDistributedCommandRunner();
+    if (distribCommandRunner.isPresent()) {
+      return distribCommandRunner.get().runCollectionCommand(m, action, 
timeout);
     } else { // Sending the Collection API message to Overseer via a Zookeeper 
queue
       String operation = m.getStr(QUEUE_OPERATION);
       if (operation == null) {
@@ -380,15 +369,15 @@ public class CollectionsHandler extends 
RequestHandlerBase implements Permission
         String asyncId = m.getStr(ASYNC);
         NamedList<Object> r = new NamedList<>();
 
-        if (coreContainer.getZkController().claimAsyncId(asyncId)) {
+        if (zkController.claimAsyncId(asyncId)) {
           boolean success = false;
           try {
-            
coreContainer.getZkController().getOverseerCollectionQueue().offer(m);
+            zkController.getOverseerCollectionQueue().offer(m);
             success = true;
           } finally {
             if (!success) {
               try {
-                coreContainer.getZkController().clearAsyncId(asyncId);
+                zkController.clearAsyncId(asyncId);
               } catch (Exception e) {
                 // let the original exception bubble up
                 log.error("Unable to release async ID={}", asyncId, e);
@@ -406,11 +395,7 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
       }
 
       long time = System.nanoTime();
-      QueueEvent event =
-          coreContainer
-              .getZkController()
-              .getOverseerCollectionQueue()
-              .offer(Utils.toJSON(m), timeout);
+      QueueEvent event = 
zkController.getOverseerCollectionQueue().offer(Utils.toJSON(m), timeout);
       if (event.getBytes() != null) {
         return OverseerSolrResponseSerializer.deserialize(event.getBytes());
       } else {
@@ -441,8 +426,7 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
   public SolrResponse submitCollectionApiCommand(
       ZkNodeProps m, CollectionAction action, long timeout)
       throws KeeperException, InterruptedException {
-    return submitCollectionApiCommand(
-        coreContainer, distributedCollectionConfigSetCommandRunner, m, action, 
timeout);
+    return submitCollectionApiCommand(coreContainer.getZkController(), m, 
action, timeout);
   }
 
   private boolean overseerCollectionQueueContains(String asyncId)
@@ -804,7 +788,7 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
           final ZkController zkController = coreContainer.getZkController();
 
           final NamedList<Object> status = new NamedList<>();
-          if (coreContainer.getDistributedCollectionCommandRunner().isEmpty()) 
{
+          if (zkController.getDistributedCommandRunner().isEmpty()) {
             if (zkController.getOverseerRunningMap().contains(requestId)) {
               addStatusToResponse(status, RUNNING, "found [" + requestId + "] 
in running tasks");
             } else if 
(zkController.getOverseerCompletedMap().contains(requestId)) {
@@ -827,8 +811,8 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
             }
           } else {
             Pair<RequestStatusState, OverseerSolrResponse> sr =
-                coreContainer
-                    .getDistributedCollectionCommandRunner()
+                zkController
+                    .getDistributedCommandRunner()
                     .get()
                     .getAsyncTaskRequestStatus(requestId);
             final String message;
@@ -878,7 +862,7 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
                   "Both requestid and flush parameters can not be specified 
together.");
             }
 
-            if 
(coreContainer.getDistributedCollectionCommandRunner().isEmpty()) {
+            if (zkController.getDistributedCommandRunner().isEmpty()) {
               if (flush) {
                 Collection<String> completed = 
zkController.getOverseerCompletedMap().keys();
                 Collection<String> failed = 
zkController.getOverseerFailureMap().keys();
@@ -913,12 +897,12 @@ public class CollectionsHandler extends 
RequestHandlerBase implements Permission
               }
             } else {
               if (flush) {
-                
coreContainer.getDistributedCollectionCommandRunner().get().deleteAllAsyncIds();
+                
zkController.getDistributedCommandRunner().get().deleteAllAsyncIds();
                 rsp.getValues()
                     .add("status", "successfully cleared stored collection api 
responses");
               } else {
-                if (coreContainer
-                    .getDistributedCollectionCommandRunner()
+                if (zkController
+                    .getDistributedCommandRunner()
                     .get()
                     .deleteSingleAsyncId(requestId)) {
                   rsp.getValues()
@@ -953,7 +937,7 @@ public class CollectionsHandler extends RequestHandlerBase 
implements Permission
         (req, rsp, h) -> {
           NamedList<Object> results = new NamedList<>();
           boolean isDistributedApi =
-              
h.coreContainer.getDistributedCollectionCommandRunner().isPresent();
+              
h.coreContainer.getZkController().getDistributedCommandRunner().isPresent();
           results.add("isDistributedApi", isDistributedApi);
           rsp.getValues().addAll(results);
           return null;
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/AddReplicaProperty.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/AddReplicaProperty.java
index 37f79fb9f87..81b2dbce137 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/admin/api/AddReplicaProperty.java
+++ 
b/solr/core/src/java/org/apache/solr/handler/admin/api/AddReplicaProperty.java
@@ -80,8 +80,7 @@ public class AddReplicaProperty extends AdminAPIBase 
implements AddReplicaProper
         createRemoteMessage(collName, shardName, replicaName, propertyName, 
requestBody);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.ADDREPLICAPROP,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/AdminAPIBase.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/AdminAPIBase.java
index cfb7df038e3..c4e001a3ad3 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/AdminAPIBase.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/AdminAPIBase.java
@@ -132,11 +132,7 @@ public abstract class AdminAPIBase extends JerseyResource {
       throws Exception {
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
-            remoteMessage,
-            action,
-            DEFAULT_COLLECTION_OP_TIMEOUT);
+            coreContainer.getZkController(), remoteMessage, action, 
DEFAULT_COLLECTION_OP_TIMEOUT);
     if (remoteResponse.getException() != null) {
       throw remoteResponse.getException();
     }
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/AliasProperty.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/AliasProperty.java
index 9534bacd595..56af9177a96 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/AliasProperty.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/AliasProperty.java
@@ -163,8 +163,7 @@ public class AliasProperty extends AdminAPIBase implements 
AliasPropertyApis {
     final ZkNodeProps remoteMessage = createRemoteMessage(alias, properties, 
async);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.ALIASPROP,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/BalanceReplicas.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/BalanceReplicas.java
index 6063074a235..b582e39464b 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/BalanceReplicas.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/BalanceReplicas.java
@@ -59,8 +59,7 @@ public class BalanceReplicas extends AdminAPIBase implements 
BalanceReplicasApi
     final ZkNodeProps remoteMessage = createRemoteMessage(requestBody);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionAction.BALANCE_REPLICAS,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateAlias.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateAlias.java
index c0497891b78..d11116f0e69 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateAlias.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateAlias.java
@@ -111,8 +111,7 @@ public class CreateAlias extends AdminAPIBase implements 
CreateAliasApi {
 
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.CREATEALIAS,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollection.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollection.java
index 2a37aa99df6..fef25cfdb52 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollection.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollection.java
@@ -112,8 +112,7 @@ public class CreateCollection extends AdminAPIBase 
implements CreateCollectionAp
     final ZkNodeProps remoteMessage = createRemoteMessage(requestBody);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.CREATE,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionBackup.java
 
b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionBackup.java
index 94c1c7768fa..602bda58e7e 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionBackup.java
+++ 
b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionBackup.java
@@ -112,8 +112,7 @@ public class CreateCollectionBackup extends BackupAPIBase 
implements CollectionB
     final ZkNodeProps remoteMessage = createRemoteMessage(collectionName, 
backupName, requestBody);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.BACKUP,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionSnapshot.java
 
b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionSnapshot.java
index 9d0a6d295af..96cf56ff3f6 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionSnapshot.java
+++ 
b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateCollectionSnapshot.java
@@ -82,8 +82,7 @@ public class CreateCollectionSnapshot extends AdminAPIBase
         createRemoteMessage(collName, requestBody.followAliases, snapshotName, 
requestBody.async);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.CREATESNAPSHOT,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteAlias.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteAlias.java
index de625ef830f..aac01f61c84 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteAlias.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteAlias.java
@@ -56,8 +56,7 @@ public class DeleteAlias extends AdminAPIBase implements 
DeleteAliasApi {
     final ZkNodeProps remoteMessage = createRemoteMessage(aliasName, asyncId);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.DELETEALIAS,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteCollection.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteCollection.java
index 04b16435aeb..3b6d87ac33c 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteCollection.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteCollection.java
@@ -65,8 +65,7 @@ public class DeleteCollection extends AdminAPIBase implements 
DeleteCollectionAp
     final ZkNodeProps remoteMessage = createRemoteMessage(collectionName, 
followAliases, asyncId);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.DELETE,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteCollectionSnapshot.java
 
b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteCollectionSnapshot.java
index abe76571dde..dec728026a3 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteCollectionSnapshot.java
+++ 
b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteCollectionSnapshot.java
@@ -65,8 +65,7 @@ public class DeleteCollectionSnapshot extends AdminAPIBase
         createRemoteMessage(collectionName, followAliases, snapshotName, 
asyncId);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.DELETESNAPSHOT,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteNode.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteNode.java
index a2459a7b68f..78af96b4ec2 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteNode.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteNode.java
@@ -64,8 +64,7 @@ public class DeleteNode extends AdminAPIBase implements 
DeleteNodeApi {
     final ZkNodeProps remoteMessage = createRemoteMessage(nodeName, 
requestBody);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.DELETENODE,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteReplicaProperty.java
 
b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteReplicaProperty.java
index 1630bf575e6..9c2486fdc39 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteReplicaProperty.java
+++ 
b/solr/core/src/java/org/apache/solr/handler/admin/api/DeleteReplicaProperty.java
@@ -68,8 +68,7 @@ public class DeleteReplicaProperty extends AdminAPIBase 
implements DeleteReplica
         createRemoteMessage(collName, shardName, replicaName, propertyName);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.DELETEREPLICAPROP,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardData.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardData.java
index ac13ef75908..98623f7d209 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardData.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/InstallShardData.java
@@ -90,8 +90,7 @@ public class InstallShardData extends AdminAPIBase implements 
InstallShardDataAp
     final ZkNodeProps remoteMessage = createRemoteMessage(collName, shardName, 
requestBody);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.INSTALLSHARDDATA,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicas.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicas.java
index 7b4e0a934b4..670540b4f74 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicas.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/MigrateReplicas.java
@@ -61,8 +61,7 @@ public class MigrateReplicas extends AdminAPIBase implements 
MigrateReplicasApi
     final ZkNodeProps remoteMessage = createRemoteMessage(requestBody);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionAction.MIGRATE_REPLICAS,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/ReplaceNode.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/ReplaceNode.java
index 0e4b98138e7..bb36f2036df 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/ReplaceNode.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/ReplaceNode.java
@@ -65,8 +65,7 @@ public class ReplaceNode extends AdminAPIBase implements 
ReplaceNodeApi {
     final ZkNodeProps remoteMessage = createRemoteMessage(sourceNodeName, 
requestBody);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.REPLACENODE,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollection.java 
b/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollection.java
index 7ee3bb994cc..fb39fafa02e 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollection.java
+++ 
b/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollection.java
@@ -125,8 +125,7 @@ public class RestoreCollection extends BackupAPIBase 
implements CollectionBackup
     final ZkNodeProps remoteMessage = createRemoteMessage(backupName, 
requestBody);
     final SolrResponse remoteResponse =
         CollectionsHandler.submitCollectionApiCommand(
-            coreContainer,
-            coreContainer.getDistributedCollectionCommandRunner(),
+            coreContainer.getZkController(),
             remoteMessage,
             CollectionParams.CollectionAction.RESTORE,
             DEFAULT_COLLECTION_OP_TIMEOUT);
diff --git 
a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java 
b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java
index 3b829f6faef..0e780cf343d 100644
--- 
a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java
+++ 
b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java
@@ -58,24 +58,18 @@ public class ConfigSetAPIBase extends JerseyResource {
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   protected final CoreContainer coreContainer;
-
+  protected final ConfigSetService configSetService;
   protected final SolrQueryRequest solrQueryRequest;
   protected final SolrQueryResponse solrQueryResponse;
-  protected final Optional<DistributedCollectionConfigSetCommandRunner>
-      distributedCollectionConfigSetCommandRunner;
-  protected final ConfigSetService configSetService;
 
   public ConfigSetAPIBase(
       CoreContainer coreContainer,
       SolrQueryRequest solrQueryRequest,
       SolrQueryResponse solrQueryResponse) {
     this.coreContainer = coreContainer;
+    this.configSetService = coreContainer.getConfigSetService();
     this.solrQueryRequest = solrQueryRequest;
     this.solrQueryResponse = solrQueryResponse;
-
-    this.distributedCollectionConfigSetCommandRunner =
-        coreContainer.getDistributedCollectionCommandRunner();
-    this.configSetService = coreContainer.getConfigSetService();
   }
 
   protected void runConfigSetCommand(
@@ -87,8 +81,10 @@ public class ConfigSetAPIBase extends JerseyResource {
       log.info("Invoked ConfigSet Action :{} with params {} ", 
action.toLower(), messageToSend);
     }
 
-    if (distributedCollectionConfigSetCommandRunner.isPresent()) {
-      distributedCollectionConfigSetCommandRunner
+    Optional<DistributedCollectionConfigSetCommandRunner> distribCommandRunner 
=
+        coreContainer.getZkController().getDistributedCommandRunner();
+    if (distribCommandRunner.isPresent()) {
+      distribCommandRunner
           .get()
           .runConfigSetCommand(rsp, action, messageToSend, CONFIG_SET_TIMEOUT);
     } else {
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/api/AddReplicaPropertyAPITest.java
 
b/solr/core/src/test/org/apache/solr/handler/admin/api/AddReplicaPropertyAPITest.java
index 6de9c823536..07874f26486 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/api/AddReplicaPropertyAPITest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/api/AddReplicaPropertyAPITest.java
@@ -30,6 +30,7 @@ import java.util.Optional;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.api.model.AddReplicaPropertyRequestBody;
 import org.apache.solr.cloud.OverseerSolrResponse;
+import org.apache.solr.cloud.ZkController;
 import 
org.apache.solr.cloud.api.collections.DistributedCollectionConfigSetCommandRunner;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.ZkNodeProps;
@@ -49,6 +50,7 @@ public class AddReplicaPropertyAPITest extends SolrTestCaseJ4 
{
       new AddReplicaPropertyRequestBody("anyValue");
 
   private CoreContainer mockCoreContainer;
+  private ZkController mockZkController;
   private DistributedCollectionConfigSetCommandRunner mockCommandRunner;
   private SolrQueryRequest mockQueryRequest;
   private SolrQueryResponse queryResponse;
@@ -67,9 +69,10 @@ public class AddReplicaPropertyAPITest extends 
SolrTestCaseJ4 {
     super.setUp();
 
     mockCoreContainer = mock(CoreContainer.class);
+    mockZkController = mock(ZkController.class);
     mockCommandRunner = 
mock(DistributedCollectionConfigSetCommandRunner.class);
-    when(mockCoreContainer.getDistributedCollectionCommandRunner())
-        .thenReturn(Optional.of(mockCommandRunner));
+    when(mockCoreContainer.getZkController()).thenReturn(mockZkController);
+    
when(mockZkController.getDistributedCommandRunner()).thenReturn(Optional.of(mockCommandRunner));
     when(mockCommandRunner.runCollectionCommand(any(), any(), anyLong()))
         .thenReturn(new OverseerSolrResponse(new NamedList<>()));
     mockQueryRequest = mock(SolrQueryRequest.class);
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/api/MigrateReplicasAPITest.java
 
b/solr/core/src/test/org/apache/solr/handler/admin/api/MigrateReplicasAPITest.java
index f6124c99703..85f0ffc1d3c 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/api/MigrateReplicasAPITest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/api/MigrateReplicasAPITest.java
@@ -29,6 +29,7 @@ import java.util.Set;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.api.model.MigrateReplicasRequestBody;
 import org.apache.solr.cloud.OverseerSolrResponse;
+import org.apache.solr.cloud.ZkController;
 import 
org.apache.solr.cloud.api.collections.DistributedCollectionConfigSetCommandRunner;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.ZkNodeProps;
@@ -45,6 +46,7 @@ import org.mockito.ArgumentCaptor;
 public class MigrateReplicasAPITest extends SolrTestCaseJ4 {
 
   private CoreContainer mockCoreContainer;
+  private ZkController mockZkController;
   private SolrQueryRequest mockQueryRequest;
   private SolrQueryResponse queryResponse;
   private MigrateReplicas migrateReplicasAPI;
@@ -62,9 +64,10 @@ public class MigrateReplicasAPITest extends SolrTestCaseJ4 {
     super.setUp();
 
     mockCoreContainer = mock(CoreContainer.class);
+    mockZkController = mock(ZkController.class);
     mockCommandRunner = 
mock(DistributedCollectionConfigSetCommandRunner.class);
-    when(mockCoreContainer.getDistributedCollectionCommandRunner())
-        .thenReturn(Optional.of(mockCommandRunner));
+    when(mockCoreContainer.getZkController()).thenReturn(mockZkController);
+    
when(mockZkController.getDistributedCommandRunner()).thenReturn(Optional.of(mockCommandRunner));
     when(mockCommandRunner.runCollectionCommand(any(), any(), anyLong()))
         .thenReturn(new OverseerSolrResponse(new NamedList<>()));
     mockQueryRequest = mock(SolrQueryRequest.class);
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/api/ReplaceNodeAPITest.java 
b/solr/core/src/test/org/apache/solr/handler/admin/api/ReplaceNodeAPITest.java
index e49eb56de0d..a325da55e7d 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/api/ReplaceNodeAPITest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/api/ReplaceNodeAPITest.java
@@ -27,6 +27,7 @@ import java.util.Optional;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.api.model.ReplaceNodeRequestBody;
 import org.apache.solr.cloud.OverseerSolrResponse;
+import org.apache.solr.cloud.ZkController;
 import 
org.apache.solr.cloud.api.collections.DistributedCollectionConfigSetCommandRunner;
 import org.apache.solr.common.cloud.ZkNodeProps;
 import org.apache.solr.common.util.NamedList;
@@ -42,6 +43,7 @@ import org.mockito.ArgumentCaptor;
 public class ReplaceNodeAPITest extends SolrTestCaseJ4 {
 
   private CoreContainer mockCoreContainer;
+  private ZkController mockZkController;
   private SolrQueryRequest mockQueryRequest;
   private SolrQueryResponse queryResponse;
   private ReplaceNode replaceNodeApi;
@@ -59,9 +61,10 @@ public class ReplaceNodeAPITest extends SolrTestCaseJ4 {
     super.setUp();
 
     mockCoreContainer = mock(CoreContainer.class);
+    mockZkController = mock(ZkController.class);
     mockCommandRunner = 
mock(DistributedCollectionConfigSetCommandRunner.class);
-    when(mockCoreContainer.getDistributedCollectionCommandRunner())
-        .thenReturn(Optional.of(mockCommandRunner));
+    when(mockCoreContainer.getZkController()).thenReturn(mockZkController);
+    
when(mockZkController.getDistributedCommandRunner()).thenReturn(Optional.of(mockCommandRunner));
     when(mockCommandRunner.runCollectionCommand(any(), any(), anyLong()))
         .thenReturn(new OverseerSolrResponse(new NamedList<>()));
     mockQueryRequest = mock(SolrQueryRequest.class);


Reply via email to