You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2019/12/05 01:04:46 UTC

[lucene-solr] 01/01: added A ReplicaStateProvider interface and changed CollectionStatePredicate to use it

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

noble pushed a commit to branch jira/solr14003
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git

commit f51d7f3c87c46868c5139443d2e718b86ce90ceb
Author: noble <no...@apache.org>
AuthorDate: Thu Dec 5 12:04:20 2019 +1100

    added A ReplicaStateProvider interface and changed CollectionStatePredicate to use it
---
 .../src/java/org/apache/solr/cloud/CloudUtil.java  |  8 ++--
 .../autoscaling/sim/SimClusterStateProvider.java   |  2 +-
 .../solr/handler/admin/CollectionsHandler.java     |  4 +-
 .../apache/solr/handler/admin/PrepRecoveryOp.java  |  2 +-
 .../client/solrj/impl/ConnectionReuseTest.java     |  4 +-
 .../apache/solr/cloud/BasicDistributedZkTest.java  |  4 +-
 .../org/apache/solr/cloud/CleanupOldIndexTest.java |  2 +-
 .../cloud/CloudExitableDirectoryReaderTest.java    |  2 +-
 .../solr/cloud/CollectionStateFormat2Test.java     |  4 +-
 .../apache/solr/cloud/CollectionsAPISolrJTest.java | 43 +++++++++---------
 .../solr/cloud/DeleteInactiveReplicaTest.java      |  6 +--
 .../cloud/DeleteLastCustomShardedReplicaTest.java  |  2 +-
 .../org/apache/solr/cloud/DeleteReplicaTest.java   | 10 ++--
 .../org/apache/solr/cloud/DeleteShardTest.java     | 10 ++--
 .../DistribDocExpirationUpdateProcessorTest.java   |  2 +-
 .../solr/cloud/DistributedVersionInfoTest.java     |  2 +-
 .../solr/cloud/LeaderElectionIntegrationTest.java  |  2 +-
 .../apache/solr/cloud/LeaderTragicEventTest.java   | 11 +++--
 .../solr/cloud/LeaderVoteWaitTimeoutTest.java      |  6 +--
 .../org/apache/solr/cloud/MigrateRouteKeyTest.java |  2 +-
 .../test/org/apache/solr/cloud/OverseerTest.java   | 38 ++++++++--------
 .../apache/solr/cloud/ReindexCollectionTest.java   | 16 +++----
 .../apache/solr/cloud/TestCloudConsistency.java    | 10 ++--
 .../org/apache/solr/cloud/TestCloudRecovery2.java  |  8 ++--
 .../solr/cloud/TestCloudSearcherWarming.java       |  2 +-
 .../cloud/TestDeleteCollectionOnDownNodes.java     |  2 +-
 .../cloud/TestLeaderElectionWithEmptyReplica.java  |  2 +-
 .../org/apache/solr/cloud/TestPullReplica.java     |  7 ++-
 .../solr/cloud/TestPullReplicaErrorHandling.java   |  2 +-
 .../apache/solr/cloud/TestRebalanceLeaders.java    | 16 +++++--
 .../solr/cloud/TestSkipOverseerOperations.java     |  4 +-
 .../org/apache/solr/cloud/TestTlogReplica.java     |  6 +--
 .../cloud/TestWaitForStateWithJettyShutdowns.java  | 14 +++---
 .../api/collections/CollectionReloadTest.java      |  2 +-
 .../collections/CollectionTooManyReplicasTest.java |  4 +-
 .../CollectionsAPIAsyncDistributedZkTest.java      |  2 +-
 .../CollectionsAPIDistributedZkTest.java           | 18 ++++----
 .../api/collections/CustomCollectionTest.java      |  4 +-
 .../AutoAddReplicasIntegrationTest.java            | 15 +++---
 .../cloud/autoscaling/ComputePlanActionTest.java   |  8 ++--
 .../ScheduledMaintenanceTriggerTest.java           |  4 +-
 .../solr/cloud/autoscaling/TestPolicyCloud.java    |  6 +--
 .../autoscaling/sim/TestSimComputePlanAction.java  |  2 +-
 .../cloud/autoscaling/sim/TestSimPolicyCloud.java  |  4 +-
 .../solr/cloud/hdfs/HDFSCollectionsAPITest.java    |  2 +-
 .../solr/cloud/overseer/ZkStateReaderTest.java     |  8 ++--
 .../test/org/apache/solr/cloud/rule/RulesTest.java |  8 ++--
 .../DistributedQueryComponentOptimizationTest.java |  2 +-
 .../schema/ManagedSchemaRoundRobinCloudTest.java   |  2 +-
 .../PreAnalyzedFieldManagedSchemaCloudTest.java    |  2 +-
 .../apache/solr/schema/SchemaApiFailureTest.java   |  2 +-
 .../processor/RoutedAliasUpdateProcessorTest.java  |  2 +-
 .../client/solrj/cloud/DirectReplicaState.java     | 53 ++++++++++++++++++++++
 .../client/solrj/cloud/ReplicaStateProvider.java   | 33 ++++++++++++++
 .../client/solrj/impl/ClusterStateProvider.java    |  6 +++
 .../solrj/impl/ZkClientClusterStateProvider.java   |  6 +++
 .../common/cloud/CollectionStatePredicate.java     |  5 +-
 .../apache/solr/common/cloud/ZkStateReader.java    | 11 ++++-
 .../cloud/TestCloudCollectionsListeners.java       |  2 +-
 .../common/cloud/TestCollectionStateWatchers.java  | 26 +++++------
 .../common/cloud/TestDocCollectionWatcher.java     | 16 +++----
 .../solr/cloud/AbstractDistribZkTestBase.java      |  4 +-
 .../solr/cloud/AbstractFullDistribZkTestBase.java  |  8 ++--
 .../apache/solr/cloud/MiniSolrCloudCluster.java    | 14 +++---
 .../org/apache/solr/cloud/SolrCloudTestCase.java   |  8 ++--
 65 files changed, 326 insertions(+), 218 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/cloud/CloudUtil.java b/solr/core/src/java/org/apache/solr/cloud/CloudUtil.java
index 1558389..7008bf9 100644
--- a/solr/core/src/java/org/apache/solr/cloud/CloudUtil.java
+++ b/solr/core/src/java/org/apache/solr/cloud/CloudUtil.java
@@ -169,10 +169,10 @@ public class CloudUtil {
     AtomicReference<DocCollection> state = new AtomicReference<>();
     AtomicReference<Set<String>> liveNodesLastSeen = new AtomicReference<>();
     try {
-      return waitForState(cloudManager, collection, DEFAULT_TIMEOUT, TimeUnit.SECONDS, (n, c) -> {
+      return waitForState(cloudManager, collection, DEFAULT_TIMEOUT, TimeUnit.SECONDS, (n, c, rsp) -> {
         state.set(c);
         liveNodesLastSeen.set(n);
-        return predicate.matches(n, c);
+        return predicate.matches(n, c, rsp);
       });
     } catch (Exception e) {
       throw new AssertionError(message + "\n" + "Live Nodes: " + liveNodesLastSeen.get() + "\nLast available state: " + state.get(), e);
@@ -208,7 +208,7 @@ public class CloudUtil {
         timeout.sleep(100);
         continue;
       }
-      if (predicate.matches(state.getLiveNodes(), coll)) {
+      if (predicate.matches(state.getLiveNodes(), coll, cloudManager.getClusterStateProvider().getReplicaStateProvider(collection))) {
         log.trace("-- predicate matched with state {}", state);
         return timeout.timeElapsed(TimeUnit.MILLISECONDS);
       }
@@ -242,7 +242,7 @@ public class CloudUtil {
    */
   public static CollectionStatePredicate clusterShape(int expectedShards, int expectedReplicas, boolean withInactive,
                                                       boolean requireLeaders) {
-    return (liveNodes, collectionState) -> {
+    return (liveNodes, collectionState, rsp) -> {
       if (collectionState == null) {
         log.debug("-- null collection");
         return false;
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/sim/SimClusterStateProvider.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/sim/SimClusterStateProvider.java
index bb8c654..99dbfc0 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/sim/SimClusterStateProvider.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/sim/SimClusterStateProvider.java
@@ -1377,7 +1377,7 @@ public class SimClusterStateProvider implements ClusterStateProvider {
 
     boolean success = false;
     try {
-      CloudUtil.waitForState(cloudManager, collectionName, 30, TimeUnit.SECONDS, (liveNodes, state) -> {
+      CloudUtil.waitForState(cloudManager, collectionName, 30, TimeUnit.SECONDS, (liveNodes, state, rsp) -> {
         for (String subSlice : subSlices) {
           Slice s = state.getSlice(subSlice);
           if (s.getLeader() == null) {
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 5843a94..cda1353 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
@@ -149,10 +149,10 @@ import static org.apache.solr.common.params.CollectionParams.CollectionAction.*;
 import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
 import static org.apache.solr.common.params.CommonAdminParams.IN_PLACE_MOVE;
 import static org.apache.solr.common.params.CommonAdminParams.NUM_SUB_SHARDS;
+import static org.apache.solr.common.params.CommonAdminParams.SPLIT_BY_PREFIX;
 import static org.apache.solr.common.params.CommonAdminParams.SPLIT_FUZZ;
 import static org.apache.solr.common.params.CommonAdminParams.SPLIT_METHOD;
 import static org.apache.solr.common.params.CommonAdminParams.WAIT_FOR_FINAL_STATE;
-import static org.apache.solr.common.params.CommonAdminParams.SPLIT_BY_PREFIX;
 import static org.apache.solr.common.params.CommonParams.NAME;
 import static org.apache.solr.common.params.CommonParams.TIMING;
 import static org.apache.solr.common.params.CommonParams.VALUE_LONG;
@@ -1412,7 +1412,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
         + (checkLeaderOnly ? "leaders" : "replicas"));
 
     try {
-      cc.getZkController().getZkStateReader().waitForState(collectionName, seconds, TimeUnit.SECONDS, (n, c) -> {
+      cc.getZkController().getZkStateReader().waitForState(collectionName, seconds, TimeUnit.SECONDS, (n, c, rsp) -> {
 
         if (c == null) {
           // the collection was not created, don't wait
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/PrepRecoveryOp.java b/solr/core/src/java/org/apache/solr/handler/admin/PrepRecoveryOp.java
index 7109944..60f7d09 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/PrepRecoveryOp.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/PrepRecoveryOp.java
@@ -75,7 +75,7 @@ class PrepRecoveryOp implements CoreAdminHandler.CoreAdminOp {
     }
     AtomicReference<String> errorMessage = new AtomicReference<>();
     try {
-      coreContainer.getZkController().getZkStateReader().waitForState(collectionName, conflictWaitMs, TimeUnit.MILLISECONDS, (n, c) -> {
+      coreContainer.getZkController().getZkStateReader().waitForState(collectionName, conflictWaitMs, TimeUnit.MILLISECONDS, (n, c, rsp) -> {
         if (c == null)
           return false;
 
diff --git a/solr/core/src/test/org/apache/solr/client/solrj/impl/ConnectionReuseTest.java b/solr/core/src/test/org/apache/solr/client/solrj/impl/ConnectionReuseTest.java
index f0ae126..61a3d98 100644
--- a/solr/core/src/test/org/apache/solr/client/solrj/impl/ConnectionReuseTest.java
+++ b/solr/core/src/test/org/apache/solr/client/solrj/impl/ConnectionReuseTest.java
@@ -36,10 +36,10 @@ import org.apache.http.conn.routing.HttpRoute;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 import org.apache.http.message.BasicHttpRequest;
+import org.apache.solr.SolrTestCaseJ4.SuppressSSL;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.cloud.SolrCloudTestCase;
-import org.apache.solr.SolrTestCaseJ4.SuppressSSL;
 import org.apache.solr.common.cloud.DocCollection;
 import org.apache.solr.update.AddUpdateCommand;
 import org.apache.solr.util.TestInjection;
@@ -65,7 +65,7 @@ public class ConnectionReuseTest extends SolrCloudTestCase {
         .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT);
 
     cluster.getSolrClient().waitForState(COLLECTION, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
   }
 
   private SolrClient buildClient(CloseableHttpClient httpClient, URL url) {
diff --git a/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java b/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java
index 98b5a8a..11187e6 100644
--- a/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java
@@ -548,7 +548,7 @@ public class BasicDistributedZkTest extends AbstractFullDistribZkTestBase {
       throws Exception {
     AtomicLong total = new AtomicLong(-1);
     try {
-      getCommonCloudSolrClient().getZkStateReader().waitForState(DEFAULT_COLLECTION, waitMillis, TimeUnit.MILLISECONDS, (n, c) -> {
+      getCommonCloudSolrClient().getZkStateReader().waitForState(DEFAULT_COLLECTION, waitMillis, TimeUnit.MILLISECONDS, (n, c, rsp) -> {
         long docTotal;
         try {
           docTotal = checkSlicesSameCounts(c);
@@ -1044,7 +1044,7 @@ public class BasicDistributedZkTest extends AbstractFullDistribZkTestBase {
       });
 
       try {
-        getCommonCloudSolrClient().getZkStateReader().waitForState(oneInstanceCollection2, 20000, TimeUnit.MILLISECONDS, (n, c) -> {
+        getCommonCloudSolrClient().getZkStateReader().waitForState(oneInstanceCollection2, 20000, TimeUnit.MILLISECONDS, (n, c, rsp) -> {
           
  
           try {
diff --git a/solr/core/src/test/org/apache/solr/cloud/CleanupOldIndexTest.java b/solr/core/src/test/org/apache/solr/cloud/CleanupOldIndexTest.java
index ff1660f..0906049 100644
--- a/solr/core/src/test/org/apache/solr/cloud/CleanupOldIndexTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/CleanupOldIndexTest.java
@@ -112,7 +112,7 @@ public class CleanupOldIndexTest extends SolrCloudTestCase {
     indexThread.join();
 
     cluster.getSolrClient().waitForState(COLLECTION, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, 1, 2));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 2));
 
     assertTrue(!oldIndexDir1.isDirectory());
     assertTrue(!oldIndexDir2.isDirectory());
diff --git a/solr/core/src/test/org/apache/solr/cloud/CloudExitableDirectoryReaderTest.java b/solr/core/src/test/org/apache/solr/cloud/CloudExitableDirectoryReaderTest.java
index 3c757ad..985303c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/CloudExitableDirectoryReaderTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/CloudExitableDirectoryReaderTest.java
@@ -93,7 +93,7 @@ public class CloudExitableDirectoryReaderTest extends SolrCloudTestCase {
     CollectionAdminRequest.createCollection(COLLECTION, "conf", 2, 1)
         .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT);
     cluster.getSolrClient().waitForState(COLLECTION, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, 2, 1));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 2, 1));
 
     fiveHundredsByNode = new LinkedHashMap<>();
     int httpOk = 0;
diff --git a/solr/core/src/test/org/apache/solr/cloud/CollectionStateFormat2Test.java b/solr/core/src/test/org/apache/solr/cloud/CollectionStateFormat2Test.java
index 04da1f5..8fe3df1 100644
--- a/solr/core/src/test/org/apache/solr/cloud/CollectionStateFormat2Test.java
+++ b/solr/core/src/test/org/apache/solr/cloud/CollectionStateFormat2Test.java
@@ -47,7 +47,7 @@ public class CollectionStateFormat2Test extends SolrCloudTestCase {
 
     cluster.waitForActiveCollection(collectionName, 2, 4);
     
-    waitForState("Collection not created", collectionName, (n, c) -> DocCollection.isFullyActive(n, c, 2, 2));
+    waitForState("Collection not created", collectionName, (n, c, rsp) -> DocCollection.isFullyActive(n, c, 2, 2));
     assertTrue("State Format 2 collection path does not exist",
         zkClient().exists(ZkStateReader.getCollectionPath(collectionName), true));
 
@@ -61,7 +61,7 @@ public class CollectionStateFormat2Test extends SolrCloudTestCase {
 
     // remove collection
     CollectionAdminRequest.deleteCollection(collectionName).process(cluster.getSolrClient());
-    waitForState("Collection not deleted", collectionName, (n, coll) -> coll == null);
+    waitForState("Collection not deleted", collectionName, (n, coll, rsp) -> coll == null);
 
     assertFalse("collection state should not exist externally",
         zkClient().exists(ZkStateReader.getCollectionPath(collectionName), true));
diff --git a/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java b/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
index 4db1152..4d069b1 100644
--- a/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
@@ -16,14 +16,6 @@
  */
 package org.apache.solr.cloud;
 
-import static java.util.Arrays.asList;
-import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_DEF;
-import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS;
-import static org.apache.solr.common.cloud.ZkStateReader.NUM_SHARDS_PROP;
-import static org.apache.solr.common.cloud.ZkStateReader.SOLR_AUTOSCALING_CONF_PATH;
-import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
-import static org.apache.solr.common.params.CollectionAdminParams.DEFAULTS;
-
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.nio.file.Path;
@@ -38,6 +30,7 @@ import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
+import com.google.common.collect.ImmutableList;
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.TestUtil;
 import org.apache.solr.client.solrj.SolrClient;
@@ -75,11 +68,17 @@ import org.apache.zookeeper.KeeperException;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Arrays.asList;
+import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_DEF;
+import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS;
+import static org.apache.solr.common.cloud.ZkStateReader.NUM_SHARDS_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.SOLR_AUTOSCALING_CONF_PATH;
+import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
+import static org.apache.solr.common.params.CollectionAdminParams.DEFAULTS;
+
 @LuceneTestCase.Slow
 public class CollectionsAPISolrJTest extends SolrCloudTestCase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -134,7 +133,7 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
     Map<String,NamedList<Integer>> nodesStatus = response.getCollectionNodesStatus();
     assertEquals(4, nodesStatus.size());
 
-    waitForState("Expected " + collectionName + " to disappear from cluster state", collectionName, (n, c) -> c == null);
+    waitForState("Expected " + collectionName + " to disappear from cluster state", collectionName, (n, c, rsp) -> c == null);
   }
 
   @Test
@@ -326,7 +325,7 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
     Map<String,NamedList<Integer>> nodesStatus = response.getCollectionNodesStatus();
     assertEquals(4, nodesStatus.size());
 
-    waitForState("Expected " + collectionName + " to disappear from cluster state", collectionName, (n, c) -> c == null);
+    waitForState("Expected " + collectionName + " to disappear from cluster state", collectionName, (n, c, rsp) -> c == null);
 
     // Test Creating a collection with new stateformat.
     collectionName = "solrj_newstateformat";
@@ -337,7 +336,7 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
     assertEquals(0, response.getStatus());
     assertTrue(response.isSuccess());
 
-    waitForState("Expected " + collectionName + " to appear in cluster state", collectionName, (n, c) -> c != null);
+    waitForState("Expected " + collectionName + " to appear in cluster state", collectionName, (n, c, rsp) -> c != null);
 
   }
 
@@ -388,7 +387,7 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
     assertEquals(0, response.getStatus());
     assertTrue(response.isSuccess());
     
-    cluster.getSolrClient().waitForState(collectionName, 30, TimeUnit.SECONDS, (l,c) -> c != null && c.getSlice("shardC") != null); 
+    cluster.getSolrClient().waitForState(collectionName, 30, TimeUnit.SECONDS, (l,c, rsp) -> c != null && c.getSlice("shardC") != null);
     
     coresStatus = response.getCollectionCoresStatus();
     assertEquals(3, coresStatus.size());
@@ -453,7 +452,7 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
     assertEquals(1, shard10);
     assertEquals(1, shard11);
 
-    waitForState("Expected all shards to be active and parent shard to be removed", collectionName, (n, c) -> {
+    waitForState("Expected all shards to be active and parent shard to be removed", collectionName, (n, c, rsp) -> {
       if (c.getSlice("shard1").getState() == Slice.State.ACTIVE)
         return false;
       for (Replica r : c.getReplicas()) {
@@ -471,7 +470,7 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
     assertEquals(0, response.getStatus());
     assertTrue(response.isSuccess());
 
-    waitForState("Expected 5 slices to be active", collectionName, (n, c) -> c.getActiveSlices().size() == 5);
+    waitForState("Expected 5 slices to be active", collectionName, (n, c, rsp) -> c.getActiveSlices().size() == 5);
 
   }
 
@@ -537,7 +536,7 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
     assertEquals(0, response.getStatus());
 
     waitForState("Expected replica " + newReplica.getName() + " to vanish from cluster state", collectionName,
-        (n, c) -> c.getSlice("shard1").getReplica(newReplica.getName()) == null);
+        (n, c, rsp) -> c.getSlice("shard1").getReplica(newReplica.getName()) == null);
 
   }
 
@@ -1021,14 +1020,14 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
     assertEquals(0, response.getStatus());
 
     waitForState("Expecting property 'preferredleader' to appear on replica " + replica.getName(), collection,
-        (n, c) -> "true".equals(c.getReplica(replica.getName()).getProperty("preferredleader")));
+        (n, c, rsp) -> "true".equals(c.getReplica(replica.getName()).getProperty("preferredleader")));
 
     response = CollectionAdminRequest.deleteReplicaProperty(collection, "shard1", replica.getName(), "property.preferredleader")
         .process(cluster.getSolrClient());
     assertEquals(0, response.getStatus());
 
     waitForState("Expecting property 'preferredleader' to be removed from replica " + replica.getName(), collection,
-        (n, c) -> c.getReplica(replica.getName()).getProperty("preferredleader") == null);
+        (n, c, rsp) -> c.getReplica(replica.getName()).getProperty("preferredleader") == null);
 
   }
 
@@ -1046,7 +1045,7 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
         .process(cluster.getSolrClient());
     assertEquals(0, response.getStatus());
 
-    waitForState("Expecting 'preferredleader' property to be balanced across all shards", collection, (n, c) -> {
+    waitForState("Expecting 'preferredleader' property to be balanced across all shards", collection, (n, c, rsp) -> {
       for (Slice slice : c) {
         int count = 0;
         for (Replica replica : slice) {
@@ -1074,14 +1073,14 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
         .process(cluster.getSolrClient());
 
     waitForState("Expecting attribute 'replicationFactor' to be 25", collection,
-        (n, c) -> 25 == c.getReplicationFactor());
+        (n, c, rsp) -> 25 == c.getReplicationFactor());
 
     CollectionAdminRequest.modifyCollection(collection, null)
         .unsetAttribute("maxShardsPerNode")
         .process(cluster.getSolrClient());
 
     waitForState("Expecting attribute 'maxShardsPerNode' to be deleted", collection,
-        (n, c) -> null == c.get("maxShardsPerNode"));
+        (n, c, rsp) -> null == c.get("maxShardsPerNode"));
 
     expectThrows(IllegalArgumentException.class,
         "An attempt to set unknown collection attribute should have failed",
diff --git a/solr/core/src/test/org/apache/solr/cloud/DeleteInactiveReplicaTest.java b/solr/core/src/test/org/apache/solr/cloud/DeleteInactiveReplicaTest.java
index 33a1a55..3e91d3f 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DeleteInactiveReplicaTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DeleteInactiveReplicaTest.java
@@ -61,7 +61,7 @@ public class DeleteInactiveReplicaTest extends SolrCloudTestCase {
     CollectionAdminRequest.createCollection(collectionName, "conf", numShards, replicationFactor)
         .setMaxShardsPerNode(maxShardsPerNode)
         .process(cluster.getSolrClient());
-    waitForState("Expected a cluster of 2 shards and 2 replicas", collectionName, (n, c) -> {
+    waitForState("Expected a cluster of 2 shards and 2 replicas", collectionName, (n, c, rsp) -> {
       return DocCollection.isFullyActive(n, c, numShards, replicationFactor);
     });
 
@@ -76,7 +76,7 @@ public class DeleteInactiveReplicaTest extends SolrCloudTestCase {
     }
     cluster.stopJettySolrRunner(jetty);
 
-    waitForState("Expected replica " + replica.getName() + " on down node to be removed from cluster state", collectionName, (n, c) -> {
+    waitForState("Expected replica " + replica.getName() + " on down node to be removed from cluster state", collectionName, (n, c, rsp) -> {
       Replica r = c.getReplica(replica.getCoreName());
       return r == null || r.getState() != Replica.State.ACTIVE;
     });
@@ -84,7 +84,7 @@ public class DeleteInactiveReplicaTest extends SolrCloudTestCase {
     log.info("Removing replica {}/{} ", shard.getName(), replica.getName());
     CollectionAdminRequest.deleteReplica(collectionName, shard.getName(), replica.getName())
         .process(cluster.getSolrClient());
-    waitForState("Expected deleted replica " + replica.getName() + " to be removed from cluster state", collectionName, (n, c) -> {
+    waitForState("Expected deleted replica " + replica.getName() + " to be removed from cluster state", collectionName, (n, c, rsp) -> {
       return c.getReplica(replica.getCoreName()) == null;
     });
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/DeleteLastCustomShardedReplicaTest.java b/solr/core/src/test/org/apache/solr/cloud/DeleteLastCustomShardedReplicaTest.java
index c46362e..c9e4c23 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DeleteLastCustomShardedReplicaTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DeleteLastCustomShardedReplicaTest.java
@@ -46,7 +46,7 @@ public class DeleteLastCustomShardedReplicaTest extends SolrCloudTestCase {
     CollectionAdminRequest.deleteReplica(collectionName, "a", replica.getName())
         .process(cluster.getSolrClient());
 
-    waitForState("Expected shard 'a' to have no replicas", collectionName, (n, c) -> {
+    waitForState("Expected shard 'a' to have no replicas", collectionName, (n, c, rsp) -> {
       return c.getSlice("a") == null || c.getSlice("a").getReplicas().size() == 0;
     });
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java b/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java
index 253f2ba..160a572 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java
@@ -125,7 +125,7 @@ public class DeleteReplicaTest extends SolrCloudTestCase {
     
     CollectionAdminRequest.deleteReplica(collectionName, shard.getName(), replica.getName())
         .process(cluster.getSolrClient());
-    waitForState("Expected replica " + replica.getName() + " to have been removed", collectionName, (n, c) -> {
+    waitForState("Expected replica " + replica.getName() + " to have been removed", collectionName, (n, c, rsp) -> {
       Slice testShard = c.getSlice(shard.getName());
       return testShard.getReplica(replica.getName()) == null;
     });
@@ -259,7 +259,7 @@ public class DeleteReplicaTest extends SolrCloudTestCase {
     cluster.getOpenOverseer().getStateUpdateQueue().offer(Utils.toJSON(m));
 
     waitForState("Timeout waiting for replica get deleted", collectionName,
-        (liveNodes, collectionState) -> collectionState.getSlice("shard1").getReplicas().size() == 2);
+        (liveNodes, collectionState, rsp) -> collectionState.getSlice("shard1").getReplicas().size() == 2);
 
     TimeOut timeOut = new TimeOut(60, TimeUnit.SECONDS, TimeSource.NANO_TIME);
     timeOut.waitFor("Waiting for replica get unloaded", () ->
@@ -375,7 +375,7 @@ public class DeleteReplicaTest extends SolrCloudTestCase {
     try {
       replica1Jetty.stop();
       waitForNodeLeave(replica1JettyNodeName);
-      waitForState("Expected replica:"+replica1+" get down", collectionName, (liveNodes, collectionState)
+      waitForState("Expected replica:"+replica1+" get down", collectionName, (liveNodes, collectionState, rsp)
           -> collectionState.getSlice("shard1").getReplica(replica1.getName()).getState() == DOWN);
       replica1Jetty.start();
       waitingForReplicaGetDeleted.acquire();
@@ -403,7 +403,7 @@ public class DeleteReplicaTest extends SolrCloudTestCase {
     leaderJetty.stop();
     waitForNodeLeave(leaderJettyNodeName);
 
-    waitForState("Expected new active leader", collectionName, (liveNodes, collectionState) -> {
+    waitForState("Expected new active leader", collectionName, (liveNodes, collectionState, rsp) -> {
       Slice shard = collectionState.getSlice("shard1");
       Replica newLeader = shard.getLeader();
       return newLeader != null && newLeader.getState() == Replica.State.ACTIVE && !newLeader.getName().equals(latestLeader.getName());
@@ -465,7 +465,7 @@ public class DeleteReplicaTest extends SolrCloudTestCase {
     }
 
     try {
-      cluster.getSolrClient().waitForState(collectionName, 20, TimeUnit.SECONDS, (liveNodes, collectionState) -> collectionState.getReplicas().size() == 1);
+      cluster.getSolrClient().waitForState(collectionName, 20, TimeUnit.SECONDS, (liveNodes, collectionState, rsp) -> collectionState.getReplicas().size() == 1);
     } catch (TimeoutException e) {
       log.info("Timeout wait for state {}", getCollectionState(collectionName));
       throw e;
diff --git a/solr/core/src/test/org/apache/solr/cloud/DeleteShardTest.java b/solr/core/src/test/org/apache/solr/cloud/DeleteShardTest.java
index 6f384fb..da99e6e 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DeleteShardTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DeleteShardTest.java
@@ -76,14 +76,14 @@ public class DeleteShardTest extends SolrCloudTestCase {
 
     // Can delete an INATIVE shard
     CollectionAdminRequest.deleteShard(collection, "shard1").process(cluster.getSolrClient());
-    waitForState("Expected 'shard1' to be removed", collection, (n, c) -> {
+    waitForState("Expected 'shard1' to be removed", collection, (n, c, rsp) -> {
       return c.getSlice("shard1") == null;
     });
 
     // Can delete a shard under construction
     setSliceState(collection, "shard2", Slice.State.CONSTRUCTION);
     CollectionAdminRequest.deleteShard(collection, "shard2").process(cluster.getSolrClient());
-    waitForState("Expected 'shard2' to be removed", collection, (n, c) -> {
+    waitForState("Expected 'shard2' to be removed", collection, (n, c, rsp) -> {
       return c.getSlice("shard2") == null;
     });
 
@@ -102,7 +102,7 @@ public class DeleteShardTest extends SolrCloudTestCase {
     ZkNodeProps m = new ZkNodeProps(propMap);
     inQueue.offer(Utils.toJSON(m));
 
-    waitForState("Expected shard " + slice + " to be in state " + state.toString(), collection, (n, c) -> {
+    waitForState("Expected shard " + slice + " to be in state " + state.toString(), collection, (n, c, rsp) -> {
       return c.getSlice(slice).getState() == state;
     });
 
@@ -131,7 +131,7 @@ public class DeleteShardTest extends SolrCloudTestCase {
     // Delete shard 'a'
     CollectionAdminRequest.deleteShard(collection, "a").process(cluster.getSolrClient());
     
-    waitForState("Expected 'a' to be removed", collection, (n, c) -> {
+    waitForState("Expected 'a' to be removed", collection, (n, c, rsp) -> {
       return c.getSlice("a") == null;
     });
 
@@ -148,7 +148,7 @@ public class DeleteShardTest extends SolrCloudTestCase {
         .setDeleteInstanceDir(false)
         .process(cluster.getSolrClient());
 
-    waitForState("Expected 'b' to be removed", collection, (n, c) -> {
+    waitForState("Expected 'b' to be removed", collection, (n, c, rsp) -> {
       return c.getSlice("b") == null;
     });
     
diff --git a/solr/core/src/test/org/apache/solr/cloud/DistribDocExpirationUpdateProcessorTest.java b/solr/core/src/test/org/apache/solr/cloud/DistribDocExpirationUpdateProcessorTest.java
index 8847cec..4575500 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DistribDocExpirationUpdateProcessorTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DistribDocExpirationUpdateProcessorTest.java
@@ -61,7 +61,7 @@ public class DistribDocExpirationUpdateProcessorTest extends SolrCloudTestCase {
     CollectionAdminRequest.createCollection(COLLECTION, "conf", 2, 1)
         .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT);
     cluster.getSolrClient().waitForState(COLLECTION, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, 2, 1));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 2, 1));
   }
 
   @Test
diff --git a/solr/core/src/test/org/apache/solr/cloud/DistributedVersionInfoTest.java b/solr/core/src/test/org/apache/solr/cloud/DistributedVersionInfoTest.java
index 0394152..60f2b65 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DistributedVersionInfoTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DistributedVersionInfoTest.java
@@ -84,7 +84,7 @@ public class DistributedVersionInfoTest extends SolrCloudTestCase {
 
     final ZkStateReader stateReader = cluster.getSolrClient().getZkStateReader();
     stateReader.waitForState(COLLECTION, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, 1, 3));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 3));
 
     final Replica leader = stateReader.getLeaderRetry(COLLECTION, shardId);
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/LeaderElectionIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/LeaderElectionIntegrationTest.java
index c20a450..7863ff3 100644
--- a/solr/core/src/test/org/apache/solr/cloud/LeaderElectionIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/LeaderElectionIntegrationTest.java
@@ -108,7 +108,7 @@ public class LeaderElectionIntegrationTest extends SolrCloudTestCase {
       runner.start();
     }
     waitForState("Expected to see nodes come back " + collection, collection,
-        (n, c) -> {
+        (n, c, rsp) -> {
           return n.size() == 6;
         });
     CollectionAdminRequest.deleteCollection(collection).process(cluster.getSolrClient());
diff --git a/solr/core/src/test/org/apache/solr/cloud/LeaderTragicEventTest.java b/solr/core/src/test/org/apache/solr/cloud/LeaderTragicEventTest.java
index 2cdd6eb..3504cc6 100644
--- a/solr/core/src/test/org/apache/solr/cloud/LeaderTragicEventTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/LeaderTragicEventTest.java
@@ -17,9 +17,6 @@
 
 package org.apache.solr.cloud;
 
-import static org.hamcrest.CoreMatchers.anyOf;
-import static org.hamcrest.CoreMatchers.is;
-
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
@@ -27,6 +24,7 @@ import java.nio.file.NoSuchFileException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+
 import org.apache.lucene.store.AlreadyClosedException;
 import org.apache.lucene.store.MockDirectoryWrapper;
 import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
@@ -50,6 +48,9 @@ import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+
 @AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/SOLR-13237")
 public class LeaderTragicEventTest extends SolrCloudTestCase {
 
@@ -84,7 +85,7 @@ public class LeaderTragicEventTest extends SolrCloudTestCase {
       List<String> addedIds = new ArrayList<>();
       Replica oldLeader = corruptLeader(collection, addedIds);
 
-      waitForState("Timeout waiting for new replica become leader", collection, (liveNodes, collectionState) -> {
+      waitForState("Timeout waiting for new replica become leader", collection, (liveNodes, collectionState, rsp) -> {
         Slice slice = collectionState.getSlice("shard1");
 
         if (slice.getReplicas().size() != 2) return false;
@@ -180,7 +181,7 @@ public class LeaderTragicEventTest extends SolrCloudTestCase {
         log.info("Stop jetty node : {} state:{}", otherReplicaJetty.getBaseUrl(), getCollectionState(collection));
         otherReplicaJetty.stop();
         cluster.waitForJettyToStop(otherReplicaJetty);
-        waitForState("Timeout waiting for replica get down", collection, (liveNodes, collectionState) -> getNonLeader(collectionState.getSlice("shard1")).getState() != Replica.State.ACTIVE);
+        waitForState("Timeout waiting for replica get down", collection, (liveNodes, collectionState, rsp) -> getNonLeader(collectionState.getSlice("shard1")).getState() != Replica.State.ACTIVE);
       }
 
       Replica oldLeader = corruptLeader(collection, new ArrayList<>());
diff --git a/solr/core/src/test/org/apache/solr/cloud/LeaderVoteWaitTimeoutTest.java b/solr/core/src/test/org/apache/solr/cloud/LeaderVoteWaitTimeoutTest.java
index 3dfb521..295889c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/LeaderVoteWaitTimeoutTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/LeaderVoteWaitTimeoutTest.java
@@ -131,13 +131,13 @@ public class LeaderVoteWaitTimeoutTest extends SolrCloudTestCase {
     j.stop();
     cluster.waitForJettyToStop(j);
     
-    cluster.getSolrClient().getZkStateReader().waitForState(collectionName, 10, TimeUnit.SECONDS, (liveNodes, collectionState) -> !liveNodes.contains(nodeName));
+    cluster.getSolrClient().getZkStateReader().waitForState(collectionName, 10, TimeUnit.SECONDS, (liveNodes, collectionState, rsp) -> !liveNodes.contains(nodeName));
 
     CollectionAdminRequest.addReplicaToShard(collectionName, "shard1")
         .setNode(cluster.getJettySolrRunner(1).getNodeName())
         .process(cluster.getSolrClient());
 
-    waitForState("Timeout waiting for replica win the election", collectionName, (liveNodes, collectionState) -> {
+    waitForState("Timeout waiting for replica win the election", collectionName, (liveNodes, collectionState, rsp) -> {
       Replica newLeader = collectionState.getSlice("shard1").getLeader();
       if (newLeader == null) {
         return false;
@@ -232,7 +232,7 @@ public class LeaderVoteWaitTimeoutTest extends SolrCloudTestCase {
 
     try {
       // even replica2 joined election at the end of the queue, but it is the one with highest term
-      waitForState("Timeout waiting for new leader", collectionName, (liveNodes, collectionState) -> {
+      waitForState("Timeout waiting for new leader", collectionName, (liveNodes, collectionState, rsp) -> {
         Replica newLeader = collectionState.getSlice("shard1").getLeader();
         if (newLeader == null) {
           return false;
diff --git a/solr/core/src/test/org/apache/solr/cloud/MigrateRouteKeyTest.java b/solr/core/src/test/org/apache/solr/cloud/MigrateRouteKeyTest.java
index 5e17706..e069175 100644
--- a/solr/core/src/test/org/apache/solr/cloud/MigrateRouteKeyTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/MigrateRouteKeyTest.java
@@ -162,7 +162,7 @@ public class MigrateRouteKeyTest extends SolrCloudTestCase {
       log.info("Response from target collection: " + response);
       assertEquals("DocCount on target collection does not match", splitKeyCount[0], response.getResults().getNumFound());
 
-      waitForState("Expected to find routing rule for split key " + splitKey, "sourceCollection", (n, c) -> {
+      waitForState("Expected to find routing rule for split key " + splitKey, "sourceCollection", (n, c, rsp) -> {
         if (c == null)
           return false;
         Slice shard = c.getSlice("shard2");
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 175806d..c35b52e 100644
--- a/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
@@ -16,15 +16,7 @@
  */
 package org.apache.solr.cloud;
 
-import static org.apache.solr.cloud.AbstractDistribZkTestBase.verifyReplicaStatus;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
+import javax.xml.parsers.ParserConfigurationException;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.nio.file.Path;
@@ -44,8 +36,8 @@ import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import javax.xml.parsers.ParserConfigurationException;
-
+import com.codahale.metrics.Snapshot;
+import com.codahale.metrics.Timer;
 import org.apache.lucene.util.LuceneTestCase.Slow;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
@@ -103,8 +95,14 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.SAXException;
 
-import com.codahale.metrics.Snapshot;
-import com.codahale.metrics.Timer;
+import static org.apache.solr.cloud.AbstractDistribZkTestBase.verifyReplicaStatus;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 @Slow
 public class OverseerTest extends SolrTestCaseJ4 {
@@ -223,7 +221,7 @@ public class OverseerTest extends SolrTestCaseJ4 {
 
       if (startElection && collection.length() > 0) {
         zkStateReader.waitForState(collection, 45000, TimeUnit.MILLISECONDS,
-            (liveNodes, collectionState) -> getShardId(collectionState, coreNodeName) != null);
+            (liveNodes, collectionState, rsp) -> getShardId(collectionState, coreNodeName) != null);
         String shardId = getShardId(collection, coreNodeName);
         if (shardId != null) {
           ElectionContext prevContext = electionContext.get(coreName);
@@ -547,7 +545,7 @@ public class OverseerTest extends SolrTestCaseJ4 {
       Set<String> availableCollections = state.getCollectionsMap().keySet();
       int availableCount = 0;
       for(String requiredCollection: collections) {
-        stateReader.waitForState(requiredCollection, 30000, TimeUnit.MILLISECONDS, (liveNodes, collectionState) ->  collectionState != null);
+        stateReader.waitForState(requiredCollection, 30000, TimeUnit.MILLISECONDS, (liveNodes, collectionState, rsp) ->  collectionState != null);
         if(availableCollections.contains(requiredCollection)) {
           availableCount++;
         }
@@ -623,7 +621,7 @@ public class OverseerTest extends SolrTestCaseJ4 {
       throws InterruptedException, KeeperException, TimeoutException {
 
     reader.waitForState(collection, 15000, TimeUnit.MILLISECONDS,
-        (liveNodes, collectionState) -> collectionState != null
+        (liveNodes, collectionState, rsp) -> collectionState != null
             && expectedCore.equals((collectionState.getLeader(shard) != null)
                 ? collectionState.getLeader(shard).getStr(ZkStateReader.CORE_NAME_PROP) : null));
 
@@ -691,7 +689,7 @@ public class OverseerTest extends SolrTestCaseJ4 {
       mockController.publishState(COLLECTION, core, core_node, "shard1", null, numShards, true, overseers.get(1));
 
       reader.waitForState(COLLECTION, 5000,
-            TimeUnit.MILLISECONDS, (liveNodes, collectionState) -> collectionState != null && collectionState.getReplica(core_node) == null);
+            TimeUnit.MILLISECONDS, (liveNodes, collectionState, rsp) -> collectionState != null && collectionState.getReplica(core_node) == null);
 
       reader.forceUpdateCollection(COLLECTION);
       // as of SOLR-5209 core removal does not cascade to remove the slice and collection
@@ -1140,7 +1138,7 @@ public class OverseerTest extends SolrTestCaseJ4 {
         overseerClient = electNewOverseer(server.getZkAddress());
         assertTrue(overseers.size() > 0);
 
-        reader.waitForState("perf_sentinel", 15000, TimeUnit.MILLISECONDS, (liveNodes, collectionState) -> collectionState != null);
+        reader.waitForState("perf_sentinel", 15000, TimeUnit.MILLISECONDS, (liveNodes, collectionState, rsp) -> collectionState != null);
 
       } finally {
         context.stop();
@@ -1250,7 +1248,7 @@ public class OverseerTest extends SolrTestCaseJ4 {
       queue.offer(Utils.toJSON(m));
 
       reader.waitForState(COLLECTION, 1000, TimeUnit.MILLISECONDS,
-          (liveNodes, collectionState) -> collectionState != null && collectionState.getSlice("shard1") != null
+          (liveNodes, collectionState, rsp) -> collectionState != null && collectionState.getSlice("shard1") != null
               && collectionState.getSlice("shard1").getReplicas().size() == 3);
 
       assertNotNull(reader.getClusterState().getCollection(COLLECTION).getSlice("shard1"));
@@ -1544,7 +1542,7 @@ public class OverseerTest extends SolrTestCaseJ4 {
 
           {
             String shard = "shard"+ss;
-            zkStateReader.waitForState(COLLECTION, 15000, TimeUnit.MILLISECONDS, (liveNodes, collectionState) -> collectionState != null && (collectionState.getSlice(shard) == null || collectionState.getSlice(shard).getReplicasMap().get("core_node"+N) == null));
+            zkStateReader.waitForState(COLLECTION, 15000, TimeUnit.MILLISECONDS, (liveNodes, collectionState, rsp) -> collectionState != null && (collectionState.getSlice(shard) == null || collectionState.getSlice(shard).getReplicasMap().get("core_node"+N) == null));
           }
 
           final DocCollection docCollection = zkStateReader.getClusterState().getCollection(COLLECTION);
diff --git a/solr/core/src/test/org/apache/solr/cloud/ReindexCollectionTest.java b/solr/core/src/test/org/apache/solr/cloud/ReindexCollectionTest.java
index f03e4f8..db3354c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ReindexCollectionTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ReindexCollectionTest.java
@@ -137,7 +137,7 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
     assertEquals(status.toString(), (long)NUM_DOCS, ((Number)status.get("inputDocs")).longValue());
     assertEquals(status.toString(), (long)NUM_DOCS, ((Number)status.get("processedDocs")).longValue());
 
-    CloudUtil.waitForState(cloudManager, "did not finish copying in time", targetCollection, (liveNodes, coll) -> {
+    CloudUtil.waitForState(cloudManager, "did not finish copying in time", targetCollection, (liveNodes, coll, replicaStateProvider) -> {
       ReindexCollectionCmd.State state = ReindexCollectionCmd.State.get(coll.getStr(ReindexCollectionCmd.REINDEXING_STATE));
       return ReindexCollectionCmd.State.FINISHED == state;
     });
@@ -189,7 +189,7 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
     }
     assertNotNull("target collection not present after 30s", realTargetCollection);
 
-    CloudUtil.waitForState(cloudManager, "did not finish copying in time", realTargetCollection, (liveNodes, coll) -> {
+    CloudUtil.waitForState(cloudManager, "did not finish copying in time", realTargetCollection, (liveNodes, coll, rsp) -> {
       ReindexCollectionCmd.State state = ReindexCollectionCmd.State.get(coll.getStr(ReindexCollectionCmd.REINDEXING_STATE));
       return ReindexCollectionCmd.State.FINISHED == state;
     });
@@ -224,7 +224,7 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
         .setConfigName("conf3");
     req.process(solrClient);
 
-    CloudUtil.waitForState(cloudManager, "did not finish copying in time", targetCollection, (liveNodes, coll) -> {
+    CloudUtil.waitForState(cloudManager, "did not finish copying in time", targetCollection, (liveNodes, coll, rsp) -> {
       ReindexCollectionCmd.State state = ReindexCollectionCmd.State.get(coll.getStr(ReindexCollectionCmd.REINDEXING_STATE));
       return ReindexCollectionCmd.State.FINISHED == state;
     });
@@ -261,7 +261,7 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
         .setCollectionParam("q", "id:10*");
     req.process(solrClient);
 
-    CloudUtil.waitForState(cloudManager, "did not finish copying in time", targetCollection, (liveNodes, coll) -> {
+    CloudUtil.waitForState(cloudManager, "did not finish copying in time", targetCollection, (liveNodes, coll, rsp) -> {
       ReindexCollectionCmd.State state = ReindexCollectionCmd.State.get(coll.getStr(ReindexCollectionCmd.REINDEXING_STATE));
       return ReindexCollectionCmd.State.FINISHED == state;
     });
@@ -338,7 +338,7 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
     });
     // verify that the source collection is read-write and has no reindexing flags
     CloudUtil.waitForState(cloudManager, "collection state is incorrect", sourceCollection,
-        ((liveNodes, collectionState) ->
+        ((liveNodes, collectionState, replicaStateProvider) ->
             !collectionState.isReadOnly() &&
             collectionState.getStr(ReindexCollectionCmd.REINDEXING_STATE) == null &&
             getState(sourceCollection) == null));
@@ -356,7 +356,7 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
     String asyncId = req.processAsync(solrClient);
     // wait for the source collection to be put in readOnly mode
     CloudUtil.waitForState(cloudManager, "source collection didn't become readOnly",
-        sourceCollection, (liveNodes, coll) -> coll.isReadOnly());
+        sourceCollection, (liveNodes, coll, rsp) -> coll.isReadOnly());
 
     req = CollectionAdminRequest.reindexCollection(sourceCollection);
     req.setCommand("abort");
@@ -366,7 +366,7 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
     assertEquals(status.toString(), "aborting", status.get("state"));
 
     CloudUtil.waitForState(cloudManager, "incorrect collection state", sourceCollection,
-        ((liveNodes, collectionState) ->
+        ((liveNodes, collectionState, replicaStateProvider) ->
             collectionState.isReadOnly() &&
             getState(sourceCollection) == ReindexCollectionCmd.State.ABORTED));
 
@@ -379,7 +379,7 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
     // let the process continue
     TestInjection.reindexLatch.countDown();
     CloudUtil.waitForState(cloudManager, "source collection is in wrong state",
-        sourceCollection, (liveNodes, docCollection) -> !docCollection.isReadOnly() && getState(sourceCollection) == null);
+        sourceCollection, (liveNodes, docCollection, replicaStateProvider) -> !docCollection.isReadOnly() && getState(sourceCollection) == null);
     // verify the response
     rsp = CollectionAdminRequest.requestStatus(asyncId).process(solrClient);
     status = (Map<String, Object>)rsp.getResponse().get(ReindexCollectionCmd.REINDEX_STATUS);
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestCloudConsistency.java b/solr/core/src/test/org/apache/solr/cloud/TestCloudConsistency.java
index f3224ff..c27fb59 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestCloudConsistency.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestCloudConsistency.java
@@ -156,7 +156,7 @@ public class TestCloudConsistency extends SolrCloudTestCase {
     cluster.waitForJettyToStop(j1);
     cluster.waitForJettyToStop(j2);
     
-    waitForState("", collection, (liveNodes, collectionState) ->
+    waitForState("", collection, (liveNodes, collectionState, rsp) ->
       collectionState.getSlice("shard1").getReplicas().stream()
           .filter(replica -> replica.getState() == Replica.State.DOWN).count() == 2);
 
@@ -164,7 +164,7 @@ public class TestCloudConsistency extends SolrCloudTestCase {
     JettySolrRunner j3 = cluster.getJettySolrRunner(0);
     j3.stop();
     cluster.waitForJettyToStop(j3);
-    waitForState("", collection, (liveNodes, collectionState) -> collectionState.getReplica(leader.getName()).getState() == Replica.State.DOWN);
+    waitForState("", collection, (liveNodes, collectionState, rsp) -> collectionState.getReplica(leader.getName()).getState() == Replica.State.DOWN);
 
     cluster.getJettySolrRunner(1).start();
     cluster.getJettySolrRunner(2).start();
@@ -187,7 +187,7 @@ public class TestCloudConsistency extends SolrCloudTestCase {
     // waitForNode not solid yet?
     cluster.waitForAllNodes(30);
     
-    waitForState("Timeout waiting for leader", collection, (liveNodes, collectionState) -> {
+    waitForState("Timeout waiting for leader", collection, (liveNodes, collectionState, rsp) -> {
       Replica newLeader = collectionState.getLeader("shard1");
       return newLeader != null && newLeader.getName().equals(leader.getName());
     });
@@ -211,7 +211,7 @@ public class TestCloudConsistency extends SolrCloudTestCase {
     for (int i = 1; i < 3; i++) {
       proxies.get(cluster.getJettySolrRunner(i)).reopen();
     }
-    waitForState("Timeout waiting for leader goes DOWN", collection, (liveNodes, collectionState)
+    waitForState("Timeout waiting for leader goes DOWN", collection, (liveNodes, collectionState, rsp)
         -> collectionState.getReplica(leader.getName()).getState() == Replica.State.DOWN);
 
     TimeOut timeOut = new TimeOut(10, TimeUnit.SECONDS, TimeSource.NANO_TIME);
@@ -225,7 +225,7 @@ public class TestCloudConsistency extends SolrCloudTestCase {
     proxies.get(cluster.getJettySolrRunner(0)).reopen();
     cluster.getJettySolrRunner(0).start();
     cluster.waitForAllNodes(30);;
-    waitForState("Timeout waiting for leader", collection, (liveNodes, collectionState) -> {
+    waitForState("Timeout waiting for leader", collection, (liveNodes, collectionState, rsp) -> {
       Replica newLeader = collectionState.getLeader("shard1");
       return newLeader != null && newLeader.getName().equals(leader.getName());
     });
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestCloudRecovery2.java b/solr/core/src/test/org/apache/solr/cloud/TestCloudRecovery2.java
index ae5e769..ff04dd1 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestCloudRecovery2.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestCloudRecovery2.java
@@ -58,7 +58,7 @@ public class TestCloudRecovery2 extends SolrCloudTestCase {
     try (HttpSolrClient client1 = getHttpSolrClient(node1.getBaseUrl().toString())) {
 
       node2.stop();
-      waitForState("", COLLECTION, (liveNodes, collectionState) -> liveNodes.size() == 1);
+      waitForState("", COLLECTION, (liveNodes, collectionState, rsp) -> liveNodes.size() == 1);
 
       UpdateRequest req = new UpdateRequest();
       for (int i = 0; i < 100; i++) {
@@ -88,7 +88,7 @@ public class TestCloudRecovery2 extends SolrCloudTestCase {
 
       //
       node2.stop();
-      waitForState("", COLLECTION, (liveNodes, collectionState) -> liveNodes.size() == 1);
+      waitForState("", COLLECTION, (liveNodes, collectionState, rsp) -> liveNodes.size() == 1);
 
       new UpdateRequest().add("id", "1", "num", "20")
           .commit(client1, COLLECTION);
@@ -103,7 +103,7 @@ public class TestCloudRecovery2 extends SolrCloudTestCase {
       }
 
       node2.stop();
-      waitForState("", COLLECTION, (liveNodes, collectionState) -> liveNodes.size() == 1);
+      waitForState("", COLLECTION, (liveNodes, collectionState, rsp) -> liveNodes.size() == 1);
 
       new UpdateRequest().add("id", "1", "num", "30")
           .commit(client1, COLLECTION);
@@ -122,7 +122,7 @@ public class TestCloudRecovery2 extends SolrCloudTestCase {
     }
 
     node1.stop();
-    waitForState("", COLLECTION, (liveNodes, collectionState) -> {
+    waitForState("", COLLECTION, (liveNodes, collectionState, rsp) -> {
       Replica leader = collectionState.getLeader("shard1");
       return leader != null && leader.getNodeName().equals(node2.getNodeName());
     });
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java b/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java
index 70680c2..4f5a2c7 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java
@@ -193,7 +193,7 @@ public class TestCloudSearcherWarming extends SolrCloudTestCase {
     waitForState("The collection should have 1 shard and 1 replica", collectionName, clusterShape(1, 1));
     // the above call is not enough because we want to assert that the down'ed replica is not active
     // but clusterShape will also return true if replica is not live -- which we don't want
-    CollectionStatePredicate collectionStatePredicate = (liveNodes, collectionState) -> {
+    CollectionStatePredicate collectionStatePredicate = (liveNodes, collectionState, rsp) -> {
       for (Replica r : collectionState.getReplicas()) {
         if (r.getNodeName().equals(oldNodeName.get())) {
           return r.getState() == Replica.State.DOWN;
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestDeleteCollectionOnDownNodes.java b/solr/core/src/test/org/apache/solr/cloud/TestDeleteCollectionOnDownNodes.java
index e6836a3..3d583cf 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestDeleteCollectionOnDownNodes.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestDeleteCollectionOnDownNodes.java
@@ -58,7 +58,7 @@ public class TestDeleteCollectionOnDownNodes extends SolrCloudTestCase {
 
     // delete the collection
     CollectionAdminRequest.deleteCollection("halfdeletedcollection2").process(cluster.getSolrClient());
-    waitForState("Timed out waiting for collection to be deleted", "halfdeletedcollection2", (n, c) -> c == null);
+    waitForState("Timed out waiting for collection to be deleted", "halfdeletedcollection2", (n, c, rsp) -> c == null);
 
     assertFalse("Still found collection that should be gone",
         cluster.getSolrClient().getZkStateReader().getClusterState().hasCollection("halfdeletedcollection2"));
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestLeaderElectionWithEmptyReplica.java b/solr/core/src/test/org/apache/solr/cloud/TestLeaderElectionWithEmptyReplica.java
index f0bb15a..90154ee 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestLeaderElectionWithEmptyReplica.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestLeaderElectionWithEmptyReplica.java
@@ -94,7 +94,7 @@ public class TestLeaderElectionWithEmptyReplica extends SolrCloudTestCase {
 
     // wait until everyone is active
     solrClient.waitForState(COLLECTION_NAME, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, 1, 2));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 2));
 
     // now query each replica and check for consistency
     assertConsistentReplicas(solrClient, solrClient.getZkStateReader().getClusterState().getCollection(COLLECTION_NAME).getSlice("shard1"));
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestPullReplica.java b/solr/core/src/test/org/apache/solr/cloud/TestPullReplica.java
index 50404be..6541177 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestPullReplica.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestPullReplica.java
@@ -28,6 +28,7 @@ import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+import com.carrotsearch.randomizedtesting.annotations.Repeat;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.HttpClient;
@@ -65,8 +66,6 @@ import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.carrotsearch.randomizedtesting.annotations.Repeat;
-
 @Slow
 @AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/SOLR-12028")
 public class TestPullReplica extends SolrCloudTestCase {
@@ -598,7 +597,7 @@ public class TestPullReplica extends SolrCloudTestCase {
    * passes only if all replicas are active or down, and the "liveNodes" reflect the same status
    */
   private CollectionStatePredicate clusterStateReflectsActiveAndDownReplicas() {
-    return (liveNodes, collectionState) -> {
+    return (liveNodes, collectionState, rsp) -> {
       for (Replica r:collectionState.getReplicas()) {
         if (r.getState() != Replica.State.DOWN && r.getState() != Replica.State.ACTIVE) {
           return false;
@@ -616,7 +615,7 @@ public class TestPullReplica extends SolrCloudTestCase {
 
 
   private CollectionStatePredicate activeReplicaCount(int numNrtReplicas, int numTlogReplicas, int numPullReplicas) {
-    return (liveNodes, collectionState) -> {
+    return (liveNodes, collectionState, rsp) -> {
       int nrtFound = 0, tlogFound = 0, pullFound = 0;
       if (collectionState == null)
         return false;
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestPullReplicaErrorHandling.java b/solr/core/src/test/org/apache/solr/cloud/TestPullReplicaErrorHandling.java
index a449589..e33c533 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestPullReplicaErrorHandling.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestPullReplicaErrorHandling.java
@@ -325,7 +325,7 @@ public void testCantConnectToPullReplica() throws Exception {
   }
   
   private CollectionStatePredicate activeReplicaCount(int numWriter, int numActive, int numPassive) {
-    return (liveNodes, collectionState) -> {
+    return (liveNodes, collectionState, rsp) -> {
       int writersFound = 0, activesFound = 0, passivesFound = 0;
       if (collectionState == null)
         return false;
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestRebalanceLeaders.java b/solr/core/src/test/org/apache/solr/cloud/TestRebalanceLeaders.java
index b207fa3..f63ebce 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestRebalanceLeaders.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestRebalanceLeaders.java
@@ -18,7 +18,15 @@ package org.apache.solr.cloud;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.lucene.util.LuceneTestCase;
@@ -452,7 +460,7 @@ public class TestRebalanceLeaders extends SolrCloudTestCase {
     cluster.getSolrClient().request(request);
     String propLC = prop.toLowerCase(Locale.ROOT);
     waitForState("Expecting property '" + prop + "'to appear on replica " + rep.getName(), COLLECTION_NAME,
-        (n, c) -> "true".equals(c.getReplica(rep.getName()).getProperty(propLC)));
+        (n, c, rsp) -> "true".equals(c.getReplica(rep.getName()).getProperty(propLC)));
 
   }
 
@@ -467,7 +475,7 @@ public class TestRebalanceLeaders extends SolrCloudTestCase {
     assertEquals(0, resp.getStatus());
     String propLC = prop.toLowerCase(Locale.ROOT);
     waitForState("Expecting property '" + prop + "'to appear on replica " + rep.getName(), COLLECTION_NAME,
-        (n, c) -> "true".equals(c.getReplica(rep.getName()).getProperty(propLC)));
+        (n, c, rsp) -> "true".equals(c.getReplica(rep.getName()).getProperty(propLC)));
 
   }
 
@@ -477,7 +485,7 @@ public class TestRebalanceLeaders extends SolrCloudTestCase {
         .process(cluster.getSolrClient());
     assertEquals("Admin request failed; ", 0, resp.getStatus());
     waitForState("Expecting property '" + prop + "' to be removed from replica " + rep.getName(), COLLECTION_NAME,
-        (n, c) -> c.getReplica(rep.getName()).getProperty(prop) == null);
+        (n, c, rsp) -> c.getReplica(rep.getName()).getProperty(prop) == null);
   }
 
   // Intentionally un-balance the property to insure that BALANCESHARDUNIQUE does its job. There was an odd case
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestSkipOverseerOperations.java b/solr/core/src/test/org/apache/solr/cloud/TestSkipOverseerOperations.java
index 73bf698..9f86de7 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestSkipOverseerOperations.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestSkipOverseerOperations.java
@@ -109,7 +109,7 @@ public class TestSkipOverseerOperations extends SolrCloudTestCase {
     });
     
     waitForState("Expected single liveNode", collection,
-        (liveNodes, collectionState) -> liveNodes.size() == 1);
+        (liveNodes, collectionState, rsp) -> liveNodes.size() == 1);
 
     CollectionAdminResponse resp = CollectionAdminRequest.getOverseerStatus().process(cluster.getSolrClient());
     for (JettySolrRunner solrRunner : notOverseerNodes) {
@@ -177,7 +177,7 @@ public class TestSkipOverseerOperations extends SolrCloudTestCase {
     });
     
     waitForState("Expected single liveNode", collection,
-        (liveNodes, collectionState) -> liveNodes.size() == 1);
+        (liveNodes, collectionState, rsp) -> liveNodes.size() == 1);
 
     CollectionAdminResponse resp = CollectionAdminRequest.getOverseerStatus().process(cluster.getSolrClient());
     for (JettySolrRunner solrRunner : notOverseerNodes) {
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java b/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java
index 58f3b7a..f40e788 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java
@@ -678,7 +678,7 @@ public class TestTlogReplica extends SolrCloudTestCase {
 
   private void waitForLeaderChange(JettySolrRunner oldLeaderJetty, String shardName) {
     waitForState("Expect new leader", collectionName,
-        (liveNodes, collectionState) -> {
+        (liveNodes, collectionState, rsp) -> {
           Replica leader = collectionState.getLeader(shardName);
           if (leader == null || !leader.isActive(cluster.getSolrClient().getZkStateReader().getClusterState().getLiveNodes())) {
             return false;
@@ -831,7 +831,7 @@ public class TestTlogReplica extends SolrCloudTestCase {
    * passes only if all replicas are active or down, and the "liveNodes" reflect the same status
    */
   private CollectionStatePredicate clusterStateReflectsActiveAndDownReplicas() {
-    return (liveNodes, collectionState) -> {
+    return (liveNodes, collectionState, rsp) -> {
       for (Replica r:collectionState.getReplicas()) {
         if (r.getState() != Replica.State.DOWN && r.getState() != Replica.State.ACTIVE) {
           return false;
@@ -849,7 +849,7 @@ public class TestTlogReplica extends SolrCloudTestCase {
 
 
   private CollectionStatePredicate activeReplicaCount(int numNrtReplicas, int numTlogReplicas, int numPullReplicas) {
-    return (liveNodes, collectionState) -> {
+    return (liveNodes, collectionState, rsp) -> {
       int nrtFound = 0, tlogFound = 0, pullFound = 0;
       if (collectionState == null)
         return false;
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestWaitForStateWithJettyShutdowns.java b/solr/core/src/test/org/apache/solr/cloud/TestWaitForStateWithJettyShutdowns.java
index f2d16a8..b25dd6d 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestWaitForStateWithJettyShutdowns.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestWaitForStateWithJettyShutdowns.java
@@ -18,9 +18,7 @@
 package org.apache.solr.cloud;
 
 import java.lang.invoke.MethodHandles;
-
 import java.util.Set;
-
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -28,18 +26,18 @@ import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.cloud.ReplicaStateProvider;
 import org.apache.solr.client.solrj.embedded.JettySolrRunner;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.common.cloud.CollectionStatePredicate;
 import org.apache.solr.common.cloud.DocCollection;
 import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.util.DefaultSolrThreadFactory;
-
-import static org.apache.solr.cloud.SolrCloudTestCase.clusterShape;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.solr.cloud.SolrCloudTestCase.clusterShape;
+
 public class TestWaitForStateWithJettyShutdowns extends SolrTestCaseJ4 {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
@@ -143,8 +141,10 @@ public class TestWaitForStateWithJettyShutdowns extends SolrTestCaseJ4 {
       this.latch = latch;
       this.inner = inner;
     }
-    public boolean matches(Set<String> liveNodes, DocCollection collectionState) {
-      final boolean result = inner.matches(liveNodes, collectionState);
+
+    @Override
+    public boolean matches(Set<String> liveNodes, DocCollection collectionState, ReplicaStateProvider rsp) {
+      final boolean result = inner.matches(liveNodes, collectionState, rsp);
       log.info("Predicate called: result={}, (pre)latch={}, liveNodes={}, state={}",
                result, latch.getCount(), liveNodes, collectionState);
       latch.countDown();
diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionReloadTest.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionReloadTest.java
index c084412..8480237 100644
--- a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionReloadTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionReloadTest.java
@@ -74,7 +74,7 @@ public class CollectionReloadTest extends SolrCloudTestCase {
 
     cluster.expireZkSession(cluster.getReplicaJetty(leader));
 
-    waitForState("Timed out waiting for core to re-register as ACTIVE after session expiry", testCollectionName, (n, c) -> {
+    waitForState("Timed out waiting for core to re-register as ACTIVE after session expiry", testCollectionName, (n, c, rsp) -> {
       log.info("Collection state: {}", c.toString());
       Replica expiredReplica = c.getReplica(leader.getName());
       return expiredReplica.getState() == Replica.State.ACTIVE && c.getZNodeVersion() > initialStateVersion;
diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionTooManyReplicasTest.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionTooManyReplicasTest.java
index 25aaf4e..fbbdfb0 100644
--- a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionTooManyReplicasTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionTooManyReplicasTest.java
@@ -108,7 +108,7 @@ public class CollectionTooManyReplicasTest extends SolrCloudTestCase {
         e2.getMessage().contains("given the current number of eligible live nodes"));
 
     // wait for recoveries to finish, for a clean shutdown - see SOLR-9645
-    waitForState("Expected to see all replicas active", collectionName, (n, c) -> {
+    waitForState("Expected to see all replicas active", collectionName, (n, c, rsp) -> {
       for (Replica r : c.getReplicas()) {
         if (r.getState() != Replica.State.ACTIVE)
           return false;
@@ -159,7 +159,7 @@ public class CollectionTooManyReplicasTest extends SolrCloudTestCase {
 
     // And finally, ensure that there are all the replicas we expect. We should have shards 1, 2 and 4 and each
     // should have exactly two replicas
-    waitForState("Expected shards shardstart, 1, 2 and 4, each with two active replicas", collectionName, (n, c) -> {
+    waitForState("Expected shards shardstart, 1, 2 and 4, each with two active replicas", collectionName, (n, c, rsp) -> {
       return DocCollection.isFullyActive(n, c, 4, 2);
     });
     Map<String, Slice> slices = getCollectionState(collectionName).getSlicesMap();
diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionsAPIAsyncDistributedZkTest.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionsAPIAsyncDistributedZkTest.java
index 953fad0..905793f 100644
--- a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionsAPIAsyncDistributedZkTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionsAPIAsyncDistributedZkTest.java
@@ -162,7 +162,7 @@ public class CollectionsAPIAsyncDistributedZkTest extends SolrCloudTestCase {
     assertSame("AddReplica did not complete", RequestStatusState.COMPLETED, state);
 
     //cloudClient watch might take a couple of seconds to reflect it
-    client.getZkStateReader().waitForState(collection, 20, TimeUnit.SECONDS, (n, c) -> {
+    client.getZkStateReader().waitForState(collection, 20, TimeUnit.SECONDS, (n, c, rsp) -> {
       if (c == null)
         return false;
       Slice slice = c.getSlice("shard1");
diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionsAPIDistributedZkTest.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionsAPIDistributedZkTest.java
index 1899d8d..5b22d62 100644
--- a/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionsAPIDistributedZkTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionsAPIDistributedZkTest.java
@@ -16,9 +16,9 @@
  */
 package org.apache.solr.cloud.api.collections;
 
-import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP;
-import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR;
-
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.lang.management.ManagementFactory;
@@ -38,10 +38,7 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import javax.management.MBeanServer;
-import javax.management.MBeanServerFactory;
-import javax.management.ObjectName;
-
+import com.google.common.collect.ImmutableList;
 import org.apache.lucene.util.LuceneTestCase.Slow;
 import org.apache.lucene.util.TestUtil;
 import org.apache.solr.client.solrj.SolrQuery;
@@ -81,7 +78,8 @@ import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableList;
+import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR;
 
 /**
  * Tests the Cloud Collections API.
@@ -335,7 +333,7 @@ public class CollectionsAPIDistributedZkTest extends SolrCloudTestCase {
     CollectionAdminRequest.createCollection("acollectionafterbaddelete", "conf", 1, 2)
         .process(cluster.getSolrClient());
     waitForState("Collection creation after a bad delete failed", "acollectionafterbaddelete",
-        (n, c) -> DocCollection.isFullyActive(n, c, 1, 2));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 2));
   }
 
   @Test
@@ -468,7 +466,7 @@ public class CollectionsAPIDistributedZkTest extends SolrCloudTestCase {
       String collectionName = "awhollynewcollection_" + i;
       final int j = i;
       waitForState("Expected to see collection " + collectionName, collectionName,
-          (n, c) -> {
+          (n, c, rsp) -> {
             CollectionAdminRequest.Create req = createRequests[j];
             return DocCollection.isFullyActive(n, c, req.getNumShards(), req.getReplicationFactor());
           });
diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/CustomCollectionTest.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/CustomCollectionTest.java
index d556271..224148c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/api/collections/CustomCollectionTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/CustomCollectionTest.java
@@ -103,7 +103,7 @@ public class CustomCollectionTest extends SolrCloudTestCase {
     //Testing CREATESHARD
     CollectionAdminRequest.createShard(collection, "x")
         .process(cluster.getSolrClient());
-    waitForState("Expected shard 'x' to be active", collection, (n, c) -> {
+    waitForState("Expected shard 'x' to be active", collection, (n, c, rsp) -> {
       if (c.getSlice("x") == null)
         return false;
       for (Replica r : c.getSlice("x")) {
@@ -194,7 +194,7 @@ public class CustomCollectionTest extends SolrCloudTestCase {
     CollectionAdminRequest.createShard(collectionName, "x")
         .process(cluster.getSolrClient());
 
-    waitForState("Not enough active replicas in shard 'x'", collectionName, (n, c) -> {
+    waitForState("Not enough active replicas in shard 'x'", collectionName, (n, c, rsp) -> {
       return c.getSlice("x").getReplicas().size() == 1;
     });
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasIntegrationTest.java
index cd92ce6..b4f82ae 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasIntegrationTest.java
@@ -16,10 +16,8 @@
  */
 package org.apache.solr.cloud.autoscaling;
 
-import static org.apache.solr.common.util.Utils.makeMap;
-
-import java.lang.invoke.MethodHandles;
 import java.io.IOException;
+import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -34,8 +32,8 @@ import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.V2Request;
 import org.apache.solr.cloud.MiniSolrCloudCluster;
 import org.apache.solr.cloud.SolrCloudTestCase;
-import org.apache.solr.common.cloud.CollectionStatePredicate;
 import org.apache.solr.common.cloud.ClusterStateUtil;
+import org.apache.solr.common.cloud.CollectionStatePredicate;
 import org.apache.solr.common.cloud.DocCollection;
 import org.apache.solr.common.cloud.Replica;
 import org.apache.solr.common.cloud.ZkStateReader;
@@ -48,10 +46,11 @@ import org.apache.solr.util.TimeOut;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.solr.common.util.Utils.makeMap;
+
 @org.apache.solr.util.LogLevel("org.apache.solr.cloud.autoscaling=DEBUG;org.apache.solr.cloud.autoscaling.NodeLostTrigger=TRACE;org.apache.solr.cloud.Overseer=DEBUG;org.apache.solr.cloud.overseer=DEBUG")
 public class AutoAddReplicasIntegrationTest extends SolrCloudTestCase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -394,7 +393,7 @@ public class AutoAddReplicasIntegrationTest extends SolrCloudTestCase {
 
   /** 
    * {@link MiniSolrCloudCluster#waitForNode} Doesn't check isRunning first, and we don't want to 
-   * use {@link MiniSolrCloudCluster#waitForAllNodes} because we don't want to waste cycles checking 
+   * use {@link MiniSolrCloudCluster#waitForAllNodes(int)} because we don't want to waste cycles checking
    * nodes we aren't messing with  
    */
   private void waitForNodeLive(final JettySolrRunner jetty)
@@ -427,8 +426,8 @@ public class AutoAddReplicasIntegrationTest extends SolrCloudTestCase {
   
   private static CollectionStatePredicate clusterShapeNoDownReplicas(final int expectedShards,
                                                                      final int expectedReplicas) {
-    return (liveNodes, collectionState)
-      -> (clusterShape(expectedShards, expectedReplicas).matches(liveNodes, collectionState)
+    return (liveNodes, collectionState, rsp)
+      -> (clusterShape(expectedShards, expectedReplicas).matches(liveNodes, collectionState, rsp)
           && collectionState.getReplicas().size() == expectedReplicas);
   }
   
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ComputePlanActionTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ComputePlanActionTest.java
index 9b579ac..962c943 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ComputePlanActionTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ComputePlanActionTest.java
@@ -361,7 +361,7 @@ public class ComputePlanActionTest extends SolrCloudTestCase {
     create.process(solrClient);
 
     waitForState("Timed out waiting for replicas of new collection to be active",
-        "testNodeAdded", (liveNodes, collectionState) -> collectionState.getReplicas().stream().allMatch(replica -> replica.isActive(liveNodes)));
+        "testNodeAdded", (liveNodes, collectionState, rsp) -> collectionState.getReplicas().stream().allMatch(replica -> replica.isActive(liveNodes)));
 
     // reset to the original policy which has only 1 replica per shard per node
     setClusterPolicyCommand = "{" +
@@ -608,7 +608,7 @@ public class ComputePlanActionTest extends SolrCloudTestCase {
     create.process(solrClient);
 
     waitForState("Timed out waiting for replicas of new collection to be active",
-        collectionNamePrefix + "_0", (liveNodes, collectionState) ->
+        collectionNamePrefix + "_0", (liveNodes, collectionState, rsp) ->
             collectionState.getReplicas().stream().allMatch(replica -> replica.isActive(liveNodes)));
 
     JettySolrRunner newNode = cluster.startJettySolrRunner();
@@ -635,7 +635,7 @@ public class ComputePlanActionTest extends SolrCloudTestCase {
       create.process(solrClient);
 
       waitForState("Timed out waiting for replicas of new collection to be active",
-          collectionNamePrefix + "_" + i, (liveNodes, collectionState) ->
+          collectionNamePrefix + "_" + i, (liveNodes, collectionState,rsp) ->
               collectionState.getReplicas().stream().allMatch(replica -> replica.isActive(liveNodes)));
     }
 
@@ -706,7 +706,7 @@ public class ComputePlanActionTest extends SolrCloudTestCase {
     create.process(solrClient);
 
     waitForState("Timed out waiting for replicas of new collection to be active",
-        collectionNamePrefix + "_0", (liveNodes, collectionState) ->
+        collectionNamePrefix + "_0", (liveNodes, collectionState,rsp) ->
             collectionState.getReplicas().stream().allMatch(replica -> replica.isActive(liveNodes)));
 
     cluster.stopJettySolrRunner(newNode);
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledMaintenanceTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledMaintenanceTriggerTest.java
index 2fb82cd..3c1be6b 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledMaintenanceTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledMaintenanceTriggerTest.java
@@ -29,8 +29,8 @@ import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.cloud.DistribStateManager;
-import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
 import org.apache.solr.client.solrj.cloud.SolrCloudManager;
+import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
 import org.apache.solr.client.solrj.cloud.autoscaling.TriggerEventProcessorStage;
 import org.apache.solr.client.solrj.embedded.JettySolrRunner;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
@@ -309,7 +309,7 @@ public class ScheduledMaintenanceTriggerTest extends SolrCloudTestCase {
 
     ClusterState state = cloudManager.getClusterStateProvider().getClusterState();
 
-    CloudUtil.clusterShape(2, 1).matches(state.getLiveNodes(), state.getCollection(collection1));
+    CloudUtil.clusterShape(2, 1).matches(state.getLiveNodes(), state.getCollection(collection1), cloudManager.getClusterStateProvider().getReplicaStateProvider(collection1));
   }
 
   public static CountDownLatch getTriggerFired() {
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
index 5d7981f..f04a777 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
@@ -180,7 +180,7 @@ public class TestPolicyCloud extends SolrCloudTestCase {
      final int expectedSliceCount,
      final int expectedReplicaCount) {
 
-    return (liveNodes, collection) -> {
+    return (liveNodes, collection, rsp) -> {
       if (null == collection || expectedSliceCount != collection.getSlices().size()) {
         return false;
       }
@@ -263,7 +263,7 @@ public class TestPolicyCloud extends SolrCloudTestCase {
                    
     waitForState("Should have found exactly 1 slice w/2 live Replicas, one on each expected jetty: " +
                  firstNodeName + "/" + firstNodePort + " & " +  secondNodeName + "/" + secondNodePort,
-                 collectionName, (liveNodes, collection) -> {
+                 collectionName, (liveNodes, collection, rsp) -> {
                    // short circut if collection is deleted
                    // or we some how have the wrong number of slices
                    if (null == collection || 1 != collection.getSlices().size()) {
@@ -300,7 +300,7 @@ public class TestPolicyCloud extends SolrCloudTestCase {
     waitForState("Should have found exactly 3 shards (1 inactive) each w/two live Replicas, " +
                  "one on each expected jetty: " +
                  firstNodeName + "/" + firstNodePort + " & " +  secondNodeName + "/" + secondNodePort,
-                 collectionName, (liveNodes, collection) -> {
+                 collectionName, (liveNodes, collection, rsp) -> {
                    // short circut if collection is deleted
                    // or we some how have the wrong number of (active) slices
                    if (null == collection
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimComputePlanAction.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimComputePlanAction.java
index 4b7f4d3..9602ab9 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimComputePlanAction.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimComputePlanAction.java
@@ -302,7 +302,7 @@ public class TestSimComputePlanAction extends SimSolrCloudTestCase {
     create.process(solrClient);
 
     CloudUtil.waitForState(cluster, "Timed out waiting for replicas of new collection to be active",
-        "testNodeAdded", (liveNodes, collectionState) -> collectionState.getReplicas().stream().allMatch(replica -> replica.isActive(liveNodes)));
+        "testNodeAdded", (liveNodes, collectionState, rsp) -> collectionState.getReplicas().stream().allMatch(replica -> replica.isActive(liveNodes)));
 
     // reset to the original policy which has only 1 replica per shard per node
     setClusterPolicyCommand = "{" +
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimPolicyCloud.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimPolicyCloud.java
index 36c94a0..d8e6cce 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimPolicyCloud.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimPolicyCloud.java
@@ -134,7 +134,7 @@ public class TestSimPolicyCloud extends SimSolrCloudTestCase {
     CollectionAdminRequest.addReplicaToShard(collectionName, "shard1").process(solrClient);
     CloudUtil.waitForState(cluster,
         collectionName, 120l, TimeUnit.SECONDS,
-        (liveNodes, collectionState) -> collectionState.getReplicas().size() == 2);
+        (liveNodes, collectionState, rsp) -> collectionState.getReplicas().size() == 2);
 
     getCollectionState(collectionName).forEachReplica((s, replica) -> assertEquals(nodeId, replica.getNodeName()));
   }
@@ -175,7 +175,7 @@ public class TestSimPolicyCloud extends SimSolrCloudTestCase {
     CollectionAdminRequest.splitShard(collectionName).setShardName("shard1").process(solrClient);
 
     CloudUtil.waitForState(cluster, "Timed out waiting to see 6 replicas for collection: " + collectionName,
-        collectionName, (liveNodes, collectionState) -> collectionState.getReplicas().size() == 6);
+        collectionName, (liveNodes, collectionState, rsp) -> collectionState.getReplicas().size() == 6);
 
     docCollection = getCollectionState(collectionName);
     list = docCollection.getReplicas(firstNode);
diff --git a/solr/core/src/test/org/apache/solr/cloud/hdfs/HDFSCollectionsAPITest.java b/solr/core/src/test/org/apache/solr/cloud/hdfs/HDFSCollectionsAPITest.java
index 6a93042..a0f8a5d 100644
--- a/solr/core/src/test/org/apache/solr/cloud/hdfs/HDFSCollectionsAPITest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/hdfs/HDFSCollectionsAPITest.java
@@ -77,7 +77,7 @@ public class HDFSCollectionsAPITest extends SolrCloudTestCase {
     cluster.getSolrClient().add(new SolrInputDocument("id", "3"));
 
     jettySolrRunner.stop();
-    waitForState("", collection, (liveNodes, collectionState) -> {
+    waitForState("", collection, (liveNodes, collectionState, rsp) -> {
       Replica replica = collectionState.getSlice("shard1").getReplicas().iterator().next();
       return replica.getState() == Replica.State.DOWN;
     });
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 f4c5bb2..39f9d20 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
@@ -100,7 +100,7 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
         if (explicitRefresh) {
           reader.forceUpdateCollection("c1");
         } else {
-          reader.waitForState("c1", TIMEOUT, TimeUnit.SECONDS, (n, c) -> c != null);
+          reader.waitForState("c1", TIMEOUT, TimeUnit.SECONDS, (n, c, rsp) -> c != null);
         }
 
         DocCollection collection = reader.getClusterState().getCollection("c1");
@@ -124,7 +124,7 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
           reader.forceUpdateCollection("c1");
         } else {
           reader.waitForState("c1", TIMEOUT, TimeUnit.SECONDS,
-              (n, c) -> c != null && c.getStateFormat() == 2);
+              (n, c, rsp) -> c != null && c.getStateFormat() == 2);
         }
 
         DocCollection collection = reader.getClusterState().getCollection("c1");
@@ -200,7 +200,7 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
       writer.enqueueUpdate(reader.getClusterState(), Collections.singletonList(wc), null);
       writer.writePendingUpdates();
       assertTrue(zkClient.exists(ZkStateReader.COLLECTIONS_ZKNODE + "/c1/state.json", true));
-      reader.waitForState("c1", 1, TimeUnit.SECONDS, (liveNodes, collectionState) -> collectionState != null);
+      reader.waitForState("c1", 1, TimeUnit.SECONDS, (liveNodes, collectionState, rsp) -> collectionState != null);
 
       state = new DocCollection("c1", new HashMap<>(), Collections.singletonMap("x", "y"), DocRouter.DEFAULT, 0, ZkStateReader.CLUSTER_STATE + "/c1/state.json");
       wc = new ZkWriteCommand("c1", state);
@@ -262,7 +262,7 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
       assertTrue(zkClient.exists(ZkStateReader.COLLECTIONS_ZKNODE + "/c1/state.json", true));
 
       //reader.forceUpdateCollection("c1");
-      reader.waitForState("c1", TIMEOUT, TimeUnit.SECONDS, (n, c) -> c != null);
+      reader.waitForState("c1", TIMEOUT, TimeUnit.SECONDS, (n, c, rsp) -> c != null);
       ClusterState.CollectionRef ref = reader.getClusterState().getCollectionRef("c1");
       assertNotNull(ref);
       assertFalse(ref.isLazilyLoaded());
diff --git a/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java b/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
index afe6acd..f95cb00 100644
--- a/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
@@ -108,7 +108,7 @@ public class RulesTest extends SolrCloudTestCase {
     CollectionAdminRequest.addReplicaToShard(rulesColl, "shard2").process(cluster.getSolrClient());
 
     waitForState("Should have found shard1 w/2active replicas + shard2 w/1active replica",
-                 rulesColl, (liveNodes, collection) -> {
+                 rulesColl, (liveNodes, collection, rsp) -> {
                    // short circut if collection is deleted
                    // or we don't yet have the correct number of slices
                    if (null == collection || 2 != collection.getSlices().size()) {
@@ -179,7 +179,7 @@ public class RulesTest extends SolrCloudTestCase {
         .process(cluster.getSolrClient());
     
     waitForState("Collection should have followed port rule w/ImplicitSnitch, not cluster policy",
-                 rulesColl, (liveNodes, rulesCollection) -> {
+                 rulesColl, (liveNodes, rulesCollection, rsp) -> {
                    // first sanity check that the collection exists & the rules/snitch are listed
                    if (null == rulesCollection) {
                      return false;
@@ -223,7 +223,7 @@ public class RulesTest extends SolrCloudTestCase {
         .process(cluster.getSolrClient());
 
     waitForState("Collection should have followed port rule w/ImplicitSnitch, not cluster policy",
-                 rulesColl, (liveNodes, rulesCollection) -> {
+                 rulesColl, (liveNodes, rulesCollection, rsp) -> {
                    // first sanity check that the collection exists & the rules/snitch are listed
                    if (null == rulesCollection) {
                      return false;
@@ -351,7 +351,7 @@ public class RulesTest extends SolrCloudTestCase {
     cluster.getSolrClient().request(new GenericSolrRequest(POST, COLLECTIONS_HANDLER_PATH, p));
 
     waitForState("Should have found updated rules in DocCollection",
-                 rulesColl, (liveNodes, rulesCollection) -> {
+                 rulesColl, (liveNodes, rulesCollection, rsp) -> {
                    if (null == rulesCollection) {
                      return false;
                    } 
diff --git a/solr/core/src/test/org/apache/solr/handler/component/DistributedQueryComponentOptimizationTest.java b/solr/core/src/test/org/apache/solr/handler/component/DistributedQueryComponentOptimizationTest.java
index 6818676..6cfaedf 100644
--- a/solr/core/src/test/org/apache/solr/handler/component/DistributedQueryComponentOptimizationTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/component/DistributedQueryComponentOptimizationTest.java
@@ -65,7 +65,7 @@ public class DistributedQueryComponentOptimizationTest extends SolrCloudTestCase
         .setMaxShardsPerNode(1)
         .processAndWait(cluster.getSolrClient(), DEFAULT_TIMEOUT);
     cluster.getSolrClient().waitForState(COLLECTION, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, sliceCount, 1));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, sliceCount, 1));
 
     new UpdateRequest()
         .add(sdoc(id, "1", "text", "a", "test_sS", "21", "payload", ByteBuffer.wrap(new byte[]{0x12, 0x62, 0x15})))
diff --git a/solr/core/src/test/org/apache/solr/schema/ManagedSchemaRoundRobinCloudTest.java b/solr/core/src/test/org/apache/solr/schema/ManagedSchemaRoundRobinCloudTest.java
index 883ebfd..46b85c8 100644
--- a/solr/core/src/test/org/apache/solr/schema/ManagedSchemaRoundRobinCloudTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/ManagedSchemaRoundRobinCloudTest.java
@@ -49,7 +49,7 @@ public class ManagedSchemaRoundRobinCloudTest extends SolrCloudTestCase {
         .setMaxShardsPerNode(1)
         .process(cluster.getSolrClient());
     cluster.getSolrClient().waitForState(COLLECTION, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, NUM_SHARDS, 1));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, NUM_SHARDS, 1));
   }
 
   @AfterClass
diff --git a/solr/core/src/test/org/apache/solr/schema/PreAnalyzedFieldManagedSchemaCloudTest.java b/solr/core/src/test/org/apache/solr/schema/PreAnalyzedFieldManagedSchemaCloudTest.java
index 04e1be0..25f066b 100644
--- a/solr/core/src/test/org/apache/solr/schema/PreAnalyzedFieldManagedSchemaCloudTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/PreAnalyzedFieldManagedSchemaCloudTest.java
@@ -43,7 +43,7 @@ public class PreAnalyzedFieldManagedSchemaCloudTest extends SolrCloudTestCase {
         .setMaxShardsPerNode(1)
         .process(cluster.getSolrClient());
     cluster.getSolrClient().waitForState(COLLECTION, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, 2, 1));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 2, 1));
   }
 
   @Test
diff --git a/solr/core/src/test/org/apache/solr/schema/SchemaApiFailureTest.java b/solr/core/src/test/org/apache/solr/schema/SchemaApiFailureTest.java
index 95cd2a7..b395476 100644
--- a/solr/core/src/test/org/apache/solr/schema/SchemaApiFailureTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/SchemaApiFailureTest.java
@@ -41,7 +41,7 @@ public class SchemaApiFailureTest extends SolrCloudTestCase {
         .setMaxShardsPerNode(2)
         .process(cluster.getSolrClient());
     cluster.getSolrClient().waitForState(COLLECTION, DEFAULT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, 2, 1));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 2, 1));
   }
 
   @Test
diff --git a/solr/core/src/test/org/apache/solr/update/processor/RoutedAliasUpdateProcessorTest.java b/solr/core/src/test/org/apache/solr/update/processor/RoutedAliasUpdateProcessorTest.java
index f751066..23e3d13 100644
--- a/solr/core/src/test/org/apache/solr/update/processor/RoutedAliasUpdateProcessorTest.java
+++ b/solr/core/src/test/org/apache/solr/update/processor/RoutedAliasUpdateProcessorTest.java
@@ -239,7 +239,7 @@ public abstract class RoutedAliasUpdateProcessorTest extends SolrCloudTestCase {
   @SuppressWarnings("WeakerAccess")
   void waitCol(int slices, String collection) {
     waitForState("waiting for collections to be created", collection,
-        (liveNodes, collectionState) -> {
+        (liveNodes, collectionState, rsp) -> {
           if (collectionState == null) {
             // per predicate javadoc, this is what we get if the collection doesn't exist at all.
             return false;
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DirectReplicaState.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DirectReplicaState.java
new file mode 100644
index 0000000..cccd785
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DirectReplicaState.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.client.solrj.cloud;
+
+import java.util.function.Predicate;
+
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+
+/**Reads the state from state.json
+ *
+ */
+public class DirectReplicaState implements ReplicaStateProvider {
+
+  private final Predicate<String> isNodeLive;
+
+  public DirectReplicaState(Predicate<String> isNodeLive) {
+    this.isNodeLive = isNodeLive;
+  }
+
+  @Override
+  public Replica.State getState(Replica replica) {
+    return replica.getState();
+  }
+
+  @Override
+  public Replica getLeader(Slice slice) {
+    return slice.getLeader();
+  }
+
+  @Override
+  public boolean isActive(Replica replica) {
+    return  replica.getNodeName() != null &&
+        replica.getState() == Replica.State.ACTIVE &&
+        isNodeLive.test(replica.getNodeName());
+  }
+
+}
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/ReplicaStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/ReplicaStateProvider.java
new file mode 100644
index 0000000..0a5e58e
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/ReplicaStateProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.client.solrj.cloud;
+
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+/**An implementation that fetches the state of each replica in a collection
+ * and it also provides the leader of shards
+ *
+ */
+public interface ReplicaStateProvider {
+
+  Replica.State getState(Replica replica);
+
+  Replica getLeader(Slice slice);
+
+  boolean isActive(Replica replica);
+}
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClusterStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClusterStateProvider.java
index a7ce278..6746268 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClusterStateProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClusterStateProvider.java
@@ -21,6 +21,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.solr.client.solrj.cloud.DirectReplicaState;
+import org.apache.solr.client.solrj.cloud.ReplicaStateProvider;
 import org.apache.solr.common.SolrCloseable;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.DocCollection;
@@ -107,5 +109,9 @@ public interface ClusterStateProvider extends SolrCloseable {
    */
   String getPolicyNameByCollection(String coll);
 
+  default ReplicaStateProvider getReplicaStateProvider(String coll) {
+    return new DirectReplicaState(s -> getLiveNodes().contains(s)) ;
+  }
+
   void connect();
 }
\ No newline at end of file
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
index cc4b045..82cd8ed 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.solr.client.solrj.cloud.ReplicaStateProvider;
 import org.apache.solr.common.AlreadyClosedException;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.ClusterState;
@@ -243,6 +244,11 @@ public class ZkClientClusterStateProvider implements ClusterStateProvider {
   }
 
   @Override
+  public ReplicaStateProvider getReplicaStateProvider(String coll) {
+    return zkStateReader.getReplicaStateProvider(coll);
+  }
+
+  @Override
   public String toString() {
     return zkHost;
   }
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/CollectionStatePredicate.java b/solr/solrj/src/java/org/apache/solr/common/cloud/CollectionStatePredicate.java
index a91a499..307efd3 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/CollectionStatePredicate.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/CollectionStatePredicate.java
@@ -21,6 +21,8 @@ import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 
+import org.apache.solr.client.solrj.cloud.ReplicaStateProvider;
+
 /**
  * Interface to determine if a set of liveNodes and a collection's state matches some expecatations.
  *
@@ -41,6 +43,7 @@ public interface CollectionStatePredicate {
    *                        does not exist
    * @return true if the input matches the requirements of this predicate
    */
-  boolean matches(Set<String> liveNodes, DocCollection collectionState);
+  boolean matches(Set<String> liveNodes, DocCollection collectionState, ReplicaStateProvider rsp);
+
 
 }
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
index a7d7e37..4cd3cfa 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
@@ -45,6 +45,8 @@ import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
 import java.util.stream.Collectors;
 
+import org.apache.solr.client.solrj.cloud.DirectReplicaState;
+import org.apache.solr.client.solrj.cloud.ReplicaStateProvider;
 import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
 import org.apache.solr.common.AlreadyClosedException;
 import org.apache.solr.common.Callable;
@@ -217,6 +219,8 @@ public class ZkStateReader implements SolrCloseable {
 
   private Set<ClusterPropertiesListener> clusterPropertiesListeners = ConcurrentHashMap.newKeySet();
 
+  private final ReplicaStateProvider directReplicaState = new DirectReplicaState(s -> liveNodes.contains(s));
+
   /**
    * Used to submit notifications to Collection Properties watchers in order
    **/
@@ -963,7 +967,7 @@ public class ZkStateReader implements SolrCloseable {
 
     AtomicReference<Replica> leader = new AtomicReference<>();
     try {
-      waitForState(collection, timeout, TimeUnit.MILLISECONDS, (n, c) -> {
+      waitForState(collection, timeout, TimeUnit.MILLISECONDS, (n, c, rsp) -> {
         if (c == null)
           return false;
         Replica l = getLeader(n, c, shard);
@@ -1743,7 +1747,7 @@ public class ZkStateReader implements SolrCloseable {
     AtomicReference<DocCollection> docCollection = new AtomicReference<>();
     CollectionStateWatcher watcher = (n, c) -> {
       docCollection.set(c);
-      boolean matches = predicate.matches(n, c);
+      boolean matches = predicate.matches(n, c, getReplicaStateProvider(collection));
       if (matches)
         latch.countDown();
 
@@ -2330,4 +2334,7 @@ public class ZkStateReader implements SolrCloseable {
       return result;
     }
   }
+  public ReplicaStateProvider getReplicaStateProvider(String coll){
+    return directReplicaState;
+  }
 }
diff --git a/solr/solrj/src/test/org/apache/solr/common/cloud/TestCloudCollectionsListeners.java b/solr/solrj/src/test/org/apache/solr/common/cloud/TestCloudCollectionsListeners.java
index 1ef806e..b2ac8a3 100644
--- a/solr/solrj/src/test/org/apache/solr/common/cloud/TestCloudCollectionsListeners.java
+++ b/solr/solrj/src/test/org/apache/solr/common/cloud/TestCloudCollectionsListeners.java
@@ -99,7 +99,7 @@ public class TestCloudCollectionsListeners extends SolrCloudTestCase {
     CollectionAdminRequest.createCollection("testcollection1", "config", 4, 1)
         .processAndWait(client, MAX_WAIT_TIMEOUT);
     client.waitForState("testcollection1", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-        (n, c) -> DocCollection.isFullyActive(n, c, 4, 1));
+        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 4, 1));
 
     assertFalse("CloudCollectionsListener has new collection in old set of collections", oldResults.get(1).contains("testcollection1"));
     assertFalse("CloudCollectionsListener has new collection in old set of collections", oldResults.get(2).contains("testcollection1"));
diff --git a/solr/solrj/src/test/org/apache/solr/common/cloud/TestCollectionStateWatchers.java b/solr/solrj/src/test/org/apache/solr/common/cloud/TestCollectionStateWatchers.java
index ab3dc95..d0f837c 100644
--- a/solr/solrj/src/test/org/apache/solr/common/cloud/TestCollectionStateWatchers.java
+++ b/solr/solrj/src/test/org/apache/solr/common/cloud/TestCollectionStateWatchers.java
@@ -124,7 +124,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
       .processAndWait(client, MAX_WAIT_TIMEOUT);
 
     client.waitForState("testcollection", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                        (n, c) -> DocCollection.isFullyActive(n, c, CLUSTER_SIZE, 1));
+                        (n, c, rsp) -> DocCollection.isFullyActive(n, c, CLUSTER_SIZE, 1));
 
     final JettySolrRunner extraJetty = cluster.startJettySolrRunner();
     final JettySolrRunner jettyToShutdown
@@ -202,13 +202,13 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
       .processAndWait(client, MAX_WAIT_TIMEOUT);
 
     client.waitForState("waitforstate", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                        (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                        (n, c,rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
 
     // several goes, to check that we're not getting delayed state changes
     for (int i = 0; i < 10; i++) {
       try {
         client.waitForState("waitforstate", 1, TimeUnit.SECONDS,
-                            (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                            (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
       } catch (TimeoutException e) {
         fail("waitForState should return immediately if the predicate is already satisfied");
       }
@@ -220,7 +220,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
   public void testCanWaitForNonexistantCollection() throws Exception {
 
     Future<Boolean> future = waitInBackground("delayed", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                                              (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                                              (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
 
     CollectionAdminRequest.createCollection("delayed", "config", 1, 1)
       .processAndWait(cluster.getSolrClient(), MAX_WAIT_TIMEOUT);
@@ -234,7 +234,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
     CloudSolrClient client = cluster.getSolrClient();
     expectThrows(TimeoutException.class, () -> {
       client.waitForState("nosuchcollection", 1, TimeUnit.SECONDS,
-                          ((liveNodes, collectionState) -> false));
+                          ((liveNodes, collectionState, rsp) -> false));
     });
     waitFor("Watchers for collection should be removed after timeout",
             MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
@@ -250,7 +250,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
       .processAndWait(client, MAX_WAIT_TIMEOUT);
 
     client.waitForState("falsepredicate", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                        (n, c) -> DocCollection.isFullyActive(n, c, 4, 1));
+                        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 4, 1));
 
     final CountDownLatch firstCall = new CountDownLatch(1);
 
@@ -261,7 +261,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
     cluster.waitForJettyToStop(node1);
 
     Future<Boolean> future = waitInBackground("falsepredicate", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                                              (liveNodes, collectionState) -> {
+                                              (liveNodes, collectionState, rsp) -> {
           firstCall.countDown();
           return DocCollection.isFullyActive(liveNodes, collectionState, 4, 1);
         });
@@ -289,7 +289,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
 
     expectThrows(TimeoutException.class, () -> {
       client.waitForState("no-such-collection", 10, TimeUnit.MILLISECONDS,
-                          (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                          (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
     });
 
     waitFor("Watchers for collection should be removed after timeout",
@@ -304,7 +304,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
       .process(cluster.getSolrClient());
     
     Future<Boolean> future = waitInBackground("tobedeleted", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                                              (l, c) -> c == null);
+                                              (l, c, rsp) -> c == null);
 
     CollectionAdminRequest.deleteCollection("tobedeleted").process(cluster.getSolrClient());
 
@@ -318,7 +318,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
     CollectionAdminRequest.createCollection("test_collection", "config", 1, 1).process(client);
 
     Future<Boolean> future = waitInBackground("test_collection", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                                              (l, c) -> (l.size() == 1 + CLUSTER_SIZE));
+                                              (l, c, rsp) -> (l.size() == 1 + CLUSTER_SIZE));
     
     JettySolrRunner unusedJetty = cluster.startJettySolrRunner();
     assertTrue("CollectionStateWatcher not notified of new node", future.get());
@@ -327,7 +327,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
             () -> client.getZkStateReader().getStateWatchers("test_collection").size() == 0);
 
     future = waitInBackground("test_collection", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                              (l, c) -> (l.size() == CLUSTER_SIZE));
+                              (l, c, rsp) -> (l.size() == CLUSTER_SIZE));
 
     cluster.stopJettySolrRunner(unusedJetty);
     
@@ -344,7 +344,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
     final CloudSolrClient client = cluster.getSolrClient();
 
     Future<Boolean> future = waitInBackground("stateformat1", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                                              (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                                              (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
 
     CollectionAdminRequest.createCollection("stateformat1", "config", 1, 1).setStateFormat(1)
       .processAndWait(client, MAX_WAIT_TIMEOUT);
@@ -352,7 +352,7 @@ public class TestCollectionStateWatchers extends SolrCloudTestCase {
                future.get());
 
     Future<Boolean> migrated = waitInBackground("stateformat1", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                                                (n, c) -> c != null && c.getStateFormat() == 2);
+                                                (n, c, rsp) -> c != null && c.getStateFormat() == 2);
 
     CollectionAdminRequest.migrateCollectionFormat("stateformat1")
       .processAndWait(client, MAX_WAIT_TIMEOUT);
diff --git a/solr/solrj/src/test/org/apache/solr/common/cloud/TestDocCollectionWatcher.java b/solr/solrj/src/test/org/apache/solr/common/cloud/TestDocCollectionWatcher.java
index 22d687e..74000f1 100644
--- a/solr/solrj/src/test/org/apache/solr/common/cloud/TestDocCollectionWatcher.java
+++ b/solr/solrj/src/test/org/apache/solr/common/cloud/TestDocCollectionWatcher.java
@@ -18,7 +18,6 @@
 package org.apache.solr.common.cloud;
 
 import java.lang.invoke.MethodHandles;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -26,6 +25,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Predicate;
 
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
@@ -143,13 +143,13 @@ public class TestDocCollectionWatcher extends SolrCloudTestCase {
       .processAndWait(client, MAX_WAIT_TIMEOUT);
 
     client.waitForState("waitforstate", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                        (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
 
     // several goes, to check that we're not getting delayed state changes
     for (int i = 0; i < 10; i++) {
       try {
         client.waitForState("waitforstate", 1, TimeUnit.SECONDS,
-                            (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                            (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
       } catch (TimeoutException e) {
         fail("waitForState should return immediately if the predicate is already satisfied");
       }
@@ -175,7 +175,7 @@ public class TestDocCollectionWatcher extends SolrCloudTestCase {
     CloudSolrClient client = cluster.getSolrClient();
     expectThrows(TimeoutException.class, () -> {
       client.waitForState("nosuchcollection", 1, TimeUnit.SECONDS,
-                          ((liveNodes, collectionState) -> false));
+                          ((liveNodes, collectionState, rsp) -> false));
     });
     waitFor("Watchers for collection should be removed after timeout",
             MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
@@ -192,7 +192,7 @@ public class TestDocCollectionWatcher extends SolrCloudTestCase {
 
     // create collection with 1 shard 1 replica...
     client.waitForState("falsepredicate", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                        (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
 
     // set watcher waiting for at least 3 replicas (will fail initially)
     final AtomicInteger runCount = new AtomicInteger(0);
@@ -213,7 +213,7 @@ public class TestDocCollectionWatcher extends SolrCloudTestCase {
     CollectionAdminRequest.addReplicaToShard("falsepredicate", "shard1")
       .processAndWait(client, MAX_WAIT_TIMEOUT);
     client.waitForState("falsepredicate", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                        (n, c) -> DocCollection.isFullyActive(n, c, 1, 2));
+                        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 2));
 
     // confirm watcher has run at least once and has been retained...
     final int runCountSnapshot = runCount.get();
@@ -255,7 +255,7 @@ public class TestDocCollectionWatcher extends SolrCloudTestCase {
     CollectionAdminRequest.createCollection("tobedeleted", "config", 1, 1).process(client);
       
     client.waitForState("tobedeleted", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                        (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                        (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
    
     Future<Boolean> future = waitInBackground("tobedeleted", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
                                               (c) -> c == null);
@@ -276,7 +276,7 @@ public class TestDocCollectionWatcher extends SolrCloudTestCase {
     CollectionAdminRequest.createCollection("stateformat1", "config", 1, 1).setStateFormat(1)
       .processAndWait(client, MAX_WAIT_TIMEOUT);
     client.waitForState("stateformat1", MAX_WAIT_TIMEOUT, TimeUnit.SECONDS,
-                         (n, c) -> DocCollection.isFullyActive(n, c, 1, 1));
+                         (n, c, rsp) -> DocCollection.isFullyActive(n, c, 1, 1));
     
     assertTrue("DocCollectionWatcher not notified of stateformat=1 collection creation",
                future.get());
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 194d387..6c34b11 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
@@ -149,7 +149,7 @@ public abstract class AbstractDistribZkTestBase extends BaseDistributedSearchTes
       throws Exception {
     log.info("Wait for recoveries to finish - collection: " + collection + " failOnTimeout:" + failOnTimeout + " timeout (sec):" + timeoutSeconds);
     try {
-      zkStateReader.waitForState(collection, timeoutSeconds, TimeUnit.SECONDS, (liveNodes, docCollection) -> {
+      zkStateReader.waitForState(collection, timeoutSeconds, TimeUnit.SECONDS, (liveNodes, docCollection, rsp) -> {
         if (docCollection == null)
           return false;
         boolean sawLiveRecovering = false;
@@ -170,7 +170,7 @@ public abstract class AbstractDistribZkTestBase extends BaseDistributedSearchTes
                 + shard.getValue().getStr(ZkStateReader.STATE_PROP)
                 + " live:"
                 + liveNodes.contains(shard.getValue().getNodeName()));
-            final Replica.State state = shard.getValue().getState();
+            final Replica.State state = rsp.getState(shard.getValue());
             if ((state == Replica.State.RECOVERING || state == Replica.State.DOWN
                 || state == Replica.State.RECOVERY_FAILED)
                 && liveNodes.contains(shard.getValue().getStr(ZkStateReader.NODE_NAME_PROP))) {
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
index f2bd410..4423272 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
@@ -16,8 +16,6 @@
  */
 package org.apache.solr.cloud;
 
-import static org.apache.solr.common.util.Utils.makeMap;
-
 import java.io.File;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
@@ -108,6 +106,8 @@ import org.noggit.JSONWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.solr.common.util.Utils.makeMap;
+
 /**
  * TODO: we should still test this works as a custom update chain as well as
  * what we test now - the default update chain
@@ -596,14 +596,14 @@ public abstract class AbstractFullDistribZkTestBase extends AbstractDistribZkTes
     log.info("Waiting to see {} active replicas in collection: {}", expectedNumReplicas, collection);
     AtomicInteger nReplicas = new AtomicInteger();
     try {
-      client.getZkStateReader().waitForState(collection, 30, TimeUnit.SECONDS, (liveNodes, collectionState) -> {
+      client.getZkStateReader().waitForState(collection, 30, TimeUnit.SECONDS, (liveNodes, collectionState, rsp) -> {
           if (collectionState == null) {
             return false;
           }
           int activeReplicas = 0;
           for (Slice slice : collectionState) {
             for (Replica replica : slice) {
-              if (replica.isActive(liveNodes)) {
+              if (rsp.isActive(replica)) {
                 activeReplicas++;
               }
             }
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 fbb547c..9f80592 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
@@ -16,6 +16,7 @@
  */
 package org.apache.solr.cloud;
 
+import javax.servlet.Filter;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.nio.charset.Charset;
@@ -43,8 +44,7 @@ import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
-import javax.servlet.Filter;
-
+import com.codahale.metrics.MetricRegistry;
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.embedded.JettyConfig;
@@ -77,8 +77,6 @@ import org.eclipse.jetty.servlet.ServletHolder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.codahale.metrics.MetricRegistry;
-
 /**
  * "Mini" SolrCloud cluster to be used for testing
  */
@@ -750,11 +748,11 @@ public class MiniSolrCloudCluster {
     AtomicReference<DocCollection> state = new AtomicReference<>();
     AtomicReference<Set<String>> liveNodesLastSeen = new AtomicReference<>();
     try {
-      getSolrClient().waitForState(collection, wait, unit, (n, c) -> {
+      getSolrClient().waitForState(collection, wait, unit, (n, c, rsp) -> {
         state.set(c);
         liveNodesLastSeen.set(n);
 
-        return predicate.matches(n, c);
+        return predicate.matches(n, c, rsp);
       });
     } catch (TimeoutException | InterruptedException e) {
       throw new RuntimeException("Failed while waiting for active collection" + "\n" + e.getMessage() + "\nLive Nodes: " + Arrays.toString(liveNodesLastSeen.get().toArray())
@@ -768,7 +766,7 @@ public class MiniSolrCloudCluster {
   }
   
   public static CollectionStatePredicate expectedShardsAndActiveReplicas(int expectedShards, int expectedReplicas) {
-    return (liveNodes, collectionState) -> {
+    return (liveNodes, collectionState, rsp) -> {
       if (collectionState == null)
         return false;
       if (collectionState.getSlices().size() != expectedShards) {
@@ -778,7 +776,7 @@ public class MiniSolrCloudCluster {
       int activeReplicas = 0;
       for (Slice slice : collectionState) {
         for (Replica replica : slice) {
-          if (replica.isActive(liveNodes)) {
+          if (rsp.isActive(replica)) {
             activeReplicas++;
           }
         }
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java
index 4ce7a5e..3ddacd2 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java
@@ -312,10 +312,10 @@ public class SolrCloudTestCase extends SolrTestCaseJ4 {
     AtomicReference<DocCollection> state = new AtomicReference<>();
     AtomicReference<Set<String>> liveNodesLastSeen = new AtomicReference<>();
     try {
-      cluster.getSolrClient().waitForState(collection, timeout, timeUnit, (n, c) -> {
+      cluster.getSolrClient().waitForState(collection, timeout, timeUnit, (n, c, rsp) -> {
         state.set(c);
         liveNodesLastSeen.set(n);
-        return predicate.matches(n, c);
+        return predicate.matches(n, c, rsp);
       });
     } catch (Exception e) {
       fail(message + "\n" + e.getMessage() + "\nLive Nodes: " + Arrays.toString(liveNodesLastSeen.get().toArray()) + "\nLast available state: " + state.get());
@@ -327,7 +327,7 @@ public class SolrCloudTestCase extends SolrTestCaseJ4 {
    * number of shards and active replicas
    */
   public static CollectionStatePredicate clusterShape(int expectedShards, int expectedReplicas) {
-    return (liveNodes, collectionState) -> {
+    return (liveNodes, collectionState, rsp) -> {
       if (collectionState == null)
         return false;
       if (collectionState.getSlices().size() != expectedShards)
@@ -341,7 +341,7 @@ public class SolrCloudTestCase extends SolrTestCaseJ4 {
    * number of active shards and active replicas
    */
   public static CollectionStatePredicate activeClusterShape(int expectedShards, int expectedReplicas) {
-    return (liveNodes, collectionState) -> {
+    return (liveNodes, collectionState, rsp) -> {
       if (collectionState == null)
         return false;
       log.info("active slice count: " + collectionState.getActiveSlices().size() + " expected:" + expectedShards);