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

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

commit e5c90acdd21c893af7679091beb1c48262bcef45
Author: Serhiy Bzhezytskyy <[email protected]>
AuthorDate: Tue Aug 25 23:52:45 2026 +0300

    SOLR-18382: remove DocCollection.getReplicas(), migrate 60 call sites 
(#4760)
    
    (cherry picked from commit b0aa1ebbfad36a3776a7cdf4cb148b51c62a1b82)
---
 .../api/collections/ReindexCollectionCmd.java      |  14 +--
 .../test/org/apache/solr/cloud/AddReplicaTest.java |  14 ++-
 .../org/apache/solr/cloud/BalanceReplicasTest.java |   4 +-
 .../apache/solr/cloud/CollectionsAPISolrJTest.java |  19 ++-
 .../cloud/CreateCollectionWithPropertiesTest.java  |   7 +-
 .../solr/cloud/DeleteCoreRemnantsOnCreateTest.java |   4 +-
 .../org/apache/solr/cloud/DeleteReplicaTest.java   |   2 +-
 .../DistribDocExpirationUpdateProcessorTest.java   |  68 +++++------
 .../solr/cloud/DistributedVersionInfoTest.java     |   5 +-
 .../org/apache/solr/cloud/MigrateRouteKeyTest.java |   2 +-
 .../org/apache/solr/cloud/MoveReplicaTest.java     |   6 +-
 .../solr/cloud/NestedShardedAtomicUpdateTest.java  |   8 +-
 .../apache/solr/cloud/TestCloudConsistency.java    |   2 +-
 .../solr/cloud/TestCloudSearcherWarming.java       |  15 ++-
 .../solr/cloud/TestOnReconnectListenerSupport.java |   3 +-
 .../org/apache/solr/cloud/TestPullReplica.java     |  47 +++++---
 .../solr/cloud/TestQueryingOnDownCollection.java   |   8 +-
 .../org/apache/solr/cloud/TestTlogReplica.java     |  28 +++--
 .../org/apache/solr/cloud/ZkControllerTest.java    |   3 +-
 .../collections/CollectionTooManyReplicasTest.java |  15 +--
 .../solr/cloud/api/collections/ShardSplitTest.java |   2 +-
 .../cloud/api/collections/TestCollectionAPI.java   |   5 +-
 .../impl/PlacementPluginIntegrationTest.java       |   6 +-
 .../apache/solr/search/TestCoordinatorRole.java    |  13 ++-
 .../org/apache/solr/servlet/HttpSolrProxyTest.java | 128 +++++++++++++++++++++
 .../solr/update/DeleteByIdWithRouterFieldTest.java |  13 ++-
 .../solrj/io/stream/CloudAuthStreamTest.java       |   3 +-
 .../client/solrj/io/stream/MathExpressionTest.java |   2 +-
 .../solrj/io/stream/StreamDecoratorTest.java       |   2 +-
 .../solr/client/solrj/io/stream/StreamingTest.java |   4 +-
 .../apache/solr/common/cloud/DocCollection.java    |  11 +-
 .../client/solrj/impl/HttpClusterStateSSLTest.java |   2 +-
 .../impl/SendUpdatesToLeadersOverrideTest.java     |   7 +-
 .../cloud/PerReplicaStatesIntegrationTest.java     |   2 +-
 .../AbstractCloudBackupRestoreTestCase.java        |  14 ++-
 ...bstractCollectionsAPIDistributedZkTestBase.java |  23 ++--
 .../solr/webapp/AdminUiCollectionsScreenTest.java  |  13 ++-
 37 files changed, 344 insertions(+), 180 deletions(-)

diff --git 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/ReindexCollectionCmd.java
 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/ReindexCollectionCmd.java
index e72cfbe04a4..822031d4a39 100644
--- 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/ReindexCollectionCmd.java
+++ 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/ReindexCollectionCmd.java
@@ -672,7 +672,7 @@ public class ReindexCollectionCmd implements 
CollApiCmds.CollectionApiCommand {
   }
 
   // XXX see #waitForDaemon() for why we need this
-  private Replica getReplicaForDaemon(SolrResponse rsp, DocCollection coll) {
+  private Replica getReplicaForDaemon(SolrResponse rsp, DocCollection 
collectionState) {
     @SuppressWarnings({"unchecked"})
     Map<String, Object> rs = (Map<String, Object>) 
rsp.getResponse().get("result-set");
     if (rs == null || rs.isEmpty()) {
@@ -713,13 +713,13 @@ public class ReindexCollectionCmd implements 
CollApiCmds.CollectionApiCommand {
     if (replicaName == null) {
       return null;
     }
+    final String finalReplicaName = replicaName;
     // build a baseUrl of the replica
-    for (Replica r : coll.getReplicas()) {
-      if (replicaName.equals(r.getCoreName())) {
-        return r;
-      }
-    }
-    return null;
+    return collectionState
+        .replicaStream()
+        .filter(r -> finalReplicaName.equals(r.getCoreName()))
+        .findFirst()
+        .orElse(null);
   }
 
   // XXX currently this is complicated to due a bug in the way the daemon 
'list'
diff --git a/solr/core/src/test/org/apache/solr/cloud/AddReplicaTest.java 
b/solr/core/src/test/org/apache/solr/cloud/AddReplicaTest.java
index 7590311a01f..8c0750635ca 100644
--- a/solr/core/src/test/org/apache/solr/cloud/AddReplicaTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/AddReplicaTest.java
@@ -73,7 +73,7 @@ public class AddReplicaTest extends SolrCloudTestCase {
 
     DocCollection docCollection = 
cloudClient.getClusterState().getCollectionOrNull(collection);
     assertNotNull(docCollection);
-    assertEquals(4, docCollection.getReplicas().size());
+    assertEquals(4, docCollection.replicaStream().count());
     assertEquals(2, getReplicas(docCollection, 
EnumSet.of(Replica.Type.NRT)).size());
     assertEquals(1, getReplicas(docCollection, 
EnumSet.of(Replica.Type.TLOG)).size());
     assertEquals(1, getReplicas(docCollection, 
EnumSet.of(Replica.Type.PULL)).size());
@@ -81,7 +81,7 @@ public class AddReplicaTest extends SolrCloudTestCase {
     docCollection = 
cloudClient.getClusterState().getCollectionOrNull(collection);
     assertNotNull(docCollection);
     // sanity check that everything is as before
-    assertEquals(4, docCollection.getReplicas().size());
+    assertEquals(4, docCollection.replicaStream().count());
     assertEquals(2, getReplicas(docCollection, 
EnumSet.of(Replica.Type.NRT)).size());
     assertEquals(1, getReplicas(docCollection, 
EnumSet.of(Replica.Type.TLOG)).size());
     assertEquals(1, getReplicas(docCollection, 
EnumSet.of(Replica.Type.PULL)).size());
@@ -106,7 +106,7 @@ public class AddReplicaTest extends SolrCloudTestCase {
     docCollection = 
cloudClient.getClusterState().getCollectionOrNull(collection);
     assertNotNull(docCollection);
     // sanity check that everything is as before
-    assertEquals(9, docCollection.getReplicas().size());
+    assertEquals(9, docCollection.replicaStream().count());
     assertEquals(5, getReplicas(docCollection, 
EnumSet.of(Replica.Type.NRT)).size());
     assertEquals(2, getReplicas(docCollection, 
EnumSet.of(Replica.Type.TLOG)).size());
     assertEquals(2, getReplicas(docCollection, 
EnumSet.of(Replica.Type.PULL)).size());
@@ -211,7 +211,13 @@ public class AddReplicaTest extends SolrCloudTestCase {
     // Verify that the new core was created with user-defined properties 
coming from the request
     // and inherited from the collection (the former taking precedence over 
the latter).
     Replica replica =
-        
cloudClient.getClusterState().getCollection(collectionName).getReplicas().get(1);
+        cloudClient
+            .getClusterState()
+            .getCollection(collectionName)
+            .replicaStream()
+            .skip(1)
+            .findFirst()
+            .orElseThrow();
     CoreDescriptor coreDescriptor =
         cluster
             .getReplicaJetty(replica)
diff --git a/solr/core/src/test/org/apache/solr/cloud/BalanceReplicasTest.java 
b/solr/core/src/test/org/apache/solr/cloud/BalanceReplicasTest.java
index 7fb0d8ddb4f..79a8a80e0a4 100644
--- a/solr/core/src/test/org/apache/solr/cloud/BalanceReplicasTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/BalanceReplicasTest.java
@@ -97,7 +97,7 @@ public class BalanceReplicasTest extends SolrCloudTestCase {
     collection = cloudClient.getClusterState().getCollectionOrNull(coll, 
false);
     log.debug("### After balancing: {}", collection);
     Set<String> replicaNodes =
-        
collection.getReplicas().stream().map(Replica::getNodeName).collect(Collectors.toSet());
+        
collection.replicaStream().map(Replica::getNodeName).collect(Collectors.toSet());
     assertEquals("Incorrect nodes for replicas after balancing", liveNodes, 
replicaNodes);
   }
 
@@ -145,7 +145,7 @@ public class BalanceReplicasTest extends SolrCloudTestCase {
     collection = cloudClient.getClusterState().getCollectionOrNull(coll, 
false);
     log.debug("### After balancing: {}", collection);
     Set<String> replicaNodes =
-        
collection.getReplicas().stream().map(Replica::getNodeName).collect(Collectors.toSet());
+        
collection.replicaStream().map(Replica::getNodeName).collect(Collectors.toSet());
     assertEquals("Incorrect nodes for replicas after balancing", 4, 
replicaNodes.size());
     assertTrue(
         "A non-balanced node lost replicas during balancing", 
replicaNodes.contains(l.get(0)));
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 17cb7e51ebb..44c24de9b10 100644
--- a/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
@@ -409,13 +409,9 @@ public class CollectionsAPISolrJTest extends 
SolrCloudTestCase {
     waitForState(
         "Expected all shards to be active and parent shard to be removed",
         collectionName,
-        (n, c) -> {
-          if (c.getSlice("shard1").getState() == Slice.State.ACTIVE) return 
false;
-          for (Replica r : c.getReplicas()) {
-            if (r.isActive(n) == false) return false;
-          }
-          return true;
-        });
+        (n, collectionState) ->
+            collectionState.getSlice("shard1").getState() != Slice.State.ACTIVE
+                && collectionState.replicaStream().allMatch(r -> 
r.isActive(n)));
 
     // Test splitting using split.key
     response =
@@ -455,7 +451,7 @@ public class CollectionsAPISolrJTest extends 
SolrCloudTestCase {
 
     DocCollection testCollection = getCollectionState(collectionName);
 
-    Replica replica1 = testCollection.getReplicas().iterator().next();
+    Replica replica1 = 
testCollection.replicaStream().findFirst().orElseThrow();
     final var coreStatus = getCoreStatus(replica1);
 
     assertEquals(Path.of(coreStatus.dataDir).toString(), dataDir.toString());
@@ -501,7 +497,8 @@ public class CollectionsAPISolrJTest extends 
SolrCloudTestCase {
   private Replica grabNewReplica(CollectionAdminResponse response, 
DocCollection docCollection) {
     String replicaName = 
response.getCollectionCoresStatus().keySet().iterator().next();
     Optional<Replica> optional =
-        docCollection.getReplicas().stream()
+        docCollection
+            .replicaStream()
             .filter(replica -> replicaName.equals(replica.getCoreName()))
             .findAny();
     if (optional.isPresent()) {
@@ -1334,8 +1331,8 @@ public class CollectionsAPISolrJTest extends 
SolrCloudTestCase {
     waitForState(
         "Expecting 'preferredleader' property to be balanced across all 
shards",
         collection,
-        c -> {
-          for (Slice slice : c) {
+        collectionState -> {
+          for (Slice slice : collectionState) {
             int count = 0;
             for (Replica replica : slice) {
               if ("true".equals(replica.getProperty("preferredleader"))) count 
+= 1;
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/CreateCollectionWithPropertiesTest.java
 
b/solr/core/src/test/org/apache/solr/cloud/CreateCollectionWithPropertiesTest.java
index c90e9d21190..a21791e3079 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/CreateCollectionWithPropertiesTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/CreateCollectionWithPropertiesTest.java
@@ -57,7 +57,12 @@ public class CreateCollectionWithPropertiesTest extends 
SolrCloudTestCase {
 
     // Verify that the core was created with user-defined properties
     Replica replica =
-        
cloudClient.getClusterState().getCollection(collectionName).getReplicas().get(0);
+        cloudClient
+            .getClusterState()
+            .getCollection(collectionName)
+            .replicaStream()
+            .findFirst()
+            .orElseThrow();
     CoreDescriptor coreDescriptor =
         cluster
             .getReplicaJetty(replica)
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/DeleteCoreRemnantsOnCreateTest.java 
b/solr/core/src/test/org/apache/solr/cloud/DeleteCoreRemnantsOnCreateTest.java
index 57cea18d24e..bc2c4e0b1f1 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/DeleteCoreRemnantsOnCreateTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/DeleteCoreRemnantsOnCreateTest.java
@@ -211,7 +211,7 @@ public class DeleteCoreRemnantsOnCreateTest extends 
SolrCloudTestCase {
     // Verify collection was created successfully
     DocCollection collection = getCollectionState(collectionName);
     assertNotNull("Collection should exist", collection);
-    assertEquals("Should have 1 replica", 1, collection.getReplicas().size());
+    assertEquals("Should have 1 replica", 1, 
collection.replicaStream().count());
 
     // Verify replica on the node where we had the remnant is active
     Replica recreatedReplica =
@@ -271,7 +271,7 @@ public class DeleteCoreRemnantsOnCreateTest extends 
SolrCloudTestCase {
     // Verify collection now has 2 replicas
     DocCollection collection = getCollectionState(collectionName);
     assertNotNull("Collection should exist", collection);
-    assertEquals("Should have 2 replicas after adding", 2, 
collection.getReplicas().size());
+    assertEquals("Should have 2 replicas after adding", 2, 
collection.replicaStream().count());
 
     // Verify the replica was added on the single node and is active
     Replica addedReplica = getReplicaOnNode(collectionName, "shard1", 
primaryNode);
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 2932553c6d2..417285bd166 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DeleteReplicaTest.java
@@ -538,7 +538,7 @@ public class DeleteReplicaTest extends SolrCloudTestCase {
     waitForState(
         "Waiting for single replica in state",
         collectionName,
-        collectionState -> collectionState.getReplicas().size() == 1);
+        collectionState -> collectionState.replicaStream().count() == 1);
   }
 
   /**
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 45b83e7066d..821548e1238 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/DistribDocExpirationUpdateProcessorTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/DistribDocExpirationUpdateProcessorTest.java
@@ -285,39 +285,41 @@ public class DistribDocExpirationUpdateProcessorTest 
extends SolrCloudTestCase {
     DocCollection collectionState =
         cluster.getSolrClient().getClusterState().getCollection(COLLECTION);
 
-    for (Replica replica : collectionState.getReplicas()) {
-
-      String coreName = replica.getCoreName();
-      try (SolrClient client = getHttpSolrClient(replica)) {
-
-        ModifiableSolrParams params = new ModifiableSolrParams();
-        params.set("command", "indexversion");
-        params.set("_trace", "getIndexVersion");
-        QueryRequest req = setAuthIfNeeded(new 
QueryRequest(ReplicationHandler.PATH, params));
-
-        NamedList<Object> res = client.request(req);
-        assertNotNull("null response from server: " + coreName, res);
-
-        Object version = res.get("indexversion");
-        assertNotNull("null version from server: " + coreName, version);
-        assertTrue("version isn't a long: " + coreName, version instanceof 
Long);
-
-        long numDocs =
-            setAuthIfNeeded(
-                    new QueryRequest(
-                        params(
-                            "q", "*:*",
-                            "distrib", "false",
-                            "rows", "0",
-                            "_trace", "counting_docs")))
-                .process(client)
-                .getResults()
-                .getNumFound();
-
-        final ReplicaData data =
-            new ReplicaData(replica.getShard(), coreName, (Long) version, 
numDocs);
-        log.info("{}", data);
-        results.put(coreName, data);
+    for (Slice slice : collectionState) {
+      for (Replica replica : slice.getReplicas()) {
+
+        String coreName = replica.getCoreName();
+        try (SolrClient client = getHttpSolrClient(replica)) {
+
+          ModifiableSolrParams params = new ModifiableSolrParams();
+          params.set("command", "indexversion");
+          params.set("_trace", "getIndexVersion");
+          QueryRequest req = setAuthIfNeeded(new 
QueryRequest(ReplicationHandler.PATH, params));
+
+          NamedList<Object> res = client.request(req);
+          assertNotNull("null response from server: " + coreName, res);
+
+          Object version = res.get("indexversion");
+          assertNotNull("null version from server: " + coreName, version);
+          assertTrue("version isn't a long: " + coreName, version instanceof 
Long);
+
+          long numDocs =
+              setAuthIfNeeded(
+                      new QueryRequest(
+                          params(
+                              "q", "*:*",
+                              "distrib", "false",
+                              "rows", "0",
+                              "_trace", "counting_docs")))
+                  .process(client)
+                  .getResults()
+                  .getNumFound();
+
+          final ReplicaData data =
+              new ReplicaData(replica.getShard(), coreName, (Long) version, 
numDocs);
+          log.info("{}", data);
+          results.put(coreName, data);
+        }
       }
     }
 
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 b6b1f977240..a67f0c0495e 100644
--- a/solr/core/src/test/org/apache/solr/cloud/DistributedVersionInfoTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/DistributedVersionInfoTest.java
@@ -92,7 +92,10 @@ public class DistributedVersionInfoTest extends 
SolrCloudTestCase {
 
     // verify doc is on the leader and replica
     final List<Replica> notLeaders =
-        
stateReader.getClusterState().getCollection(COLLECTION).getReplicas().stream()
+        stateReader
+            .getClusterState()
+            .getCollection(COLLECTION)
+            .replicaStream()
             .filter(r -> r.getCoreName().equals(leader.getCoreName()) == false)
             .collect(Collectors.toList());
     assertDocsExistInAllReplicas(leader, notLeaders, COLLECTION, 1, 1, null);
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 c990f43423a..723f9d9ba6a 100644
--- a/solr/core/src/test/org/apache/solr/cloud/MigrateRouteKeyTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/MigrateRouteKeyTest.java
@@ -133,7 +133,7 @@ public class MigrateRouteKeyTest extends SolrCloudTestCase {
     indexer.start();
 
     DocCollection state = getCollectionState(targetCollection);
-    Replica replica = state.getReplicas().get(0);
+    Replica replica = state.replicaStream().findFirst().orElseThrow();
     try (SolrClient collectionClient = getHttpSolrClient(replica)) {
 
       SolrQuery solrQuery = new SolrQuery("*:*");
diff --git a/solr/core/src/test/org/apache/solr/cloud/MoveReplicaTest.java 
b/solr/core/src/test/org/apache/solr/cloud/MoveReplicaTest.java
index 76092a2c936..68524274e02 100644
--- a/solr/core/src/test/org/apache/solr/cloud/MoveReplicaTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/MoveReplicaTest.java
@@ -342,9 +342,9 @@ public class MoveReplicaTest extends SolrCloudTestCase {
   }
 
   private Replica getRandomReplica(String coll, CloudSolrClient cloudClient) 
throws IOException {
-    List<Replica> replicas = 
cloudClient.getClusterState().getCollection(coll).getReplicas();
-    Collections.shuffle(replicas, random());
-    return replicas.get(0);
+    List<Replica> replicas =
+        
cloudClient.getClusterState().getCollection(coll).replicaStream().toList();
+    return replicas.get(random().nextInt(replicas.size()));
   }
 
   private void checkNumOfCores(
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/NestedShardedAtomicUpdateTest.java 
b/solr/core/src/test/org/apache/solr/cloud/NestedShardedAtomicUpdateTest.java
index 455b202f1d3..c284d581cd8 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/NestedShardedAtomicUpdateTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/NestedShardedAtomicUpdateTest.java
@@ -33,7 +33,6 @@ import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.cloud.ClusterState;
-import org.apache.solr.common.cloud.Replica;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.util.RandomNoReverseMergePolicyFactory;
@@ -71,9 +70,10 @@ public class NestedShardedAtomicUpdateTest extends 
SolrCloudTestCase {
 
     clients = new ArrayList<>();
     ClusterState clusterState = cloudClient.getClusterState();
-    for (Replica replica : 
clusterState.getCollection(DEFAULT_COLLECTION).getReplicas()) {
-      clients.add(getHttpSolrClient(replica));
-    }
+    clusterState
+        .getCollection(DEFAULT_COLLECTION)
+        .replicaStream()
+        .forEach(replica -> clients.add(getHttpSolrClient(replica)));
   }
 
   @AfterClass
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 9041d42555d..b3c7c4142ae 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestCloudConsistency.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestCloudConsistency.java
@@ -133,7 +133,7 @@ public class TestCloudConsistency extends SolrCloudTestCase 
{
     }
 
     assertDocsExistInAllReplicas(
-        getCollectionState(collectionName).getReplicas(), collectionName, 1, 
4);
+        getCollectionState(collectionName).replicaStream().toList(), 
collectionName, 1, 4);
 
     
CollectionAdminRequest.deleteCollection(collectionName).process(cluster.getSolrClient());
   }
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 3e2089af36c..6c8764a761e 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestCloudSearcherWarming.java
@@ -213,14 +213,13 @@ public class TestCloudSearcherWarming extends 
SolrCloudTestCase {
     // the above call is not enough because we want to assert that the downed 
replica is not active
     // but clusterShape will also return true if replica is not live -- which 
we don't want
     Predicate<DocCollection> collectionStatePredicate =
-        collectionState -> {
-          for (Replica r : collectionState.getReplicas()) {
-            if (r.getNodeName().equals(oldNodeName.get())) {
-              return r.getState() == Replica.State.DOWN;
-            }
-          }
-          return false;
-        };
+        collectionState ->
+            collectionState
+                .replicaStream()
+                .filter(r -> r.getNodeName().equals(oldNodeName.get()))
+                .findFirst()
+                .map(r -> r.getState() == Replica.State.DOWN)
+                .orElse(false);
     waitForState("", collectionName, collectionStatePredicate);
     
assertNotNull(ZkStateReader.from(solrClient).getLeaderRetry(collectionName, 
"shard1"));
 
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/TestOnReconnectListenerSupport.java 
b/solr/core/src/test/org/apache/solr/cloud/TestOnReconnectListenerSupport.java
index 39dc89dd877..fa2f9264dee 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/TestOnReconnectListenerSupport.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/TestOnReconnectListenerSupport.java
@@ -47,7 +47,8 @@ public class TestOnReconnectListenerSupport extends 
SolrCloudTestCase {
         .process(cluster.getSolrClient());
     cluster.waitForActiveCollection(testCollectionName, 1, 1);
 
-    Replica leader = 
getCollectionState(testCollectionName).getReplicas().get(0);
+    Replica leader =
+        
getCollectionState(testCollectionName).replicaStream().findFirst().orElseThrow();
     CoreContainer cores = cluster.getJettySolrRunner(0).getCoreContainer();
     ZkController zkController = cores.getZkController();
     assertNotNull("ZkController is null", zkController);
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 1d8166e119b..c6f30385972 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestPullReplica.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestPullReplica.java
@@ -185,7 +185,7 @@ public class TestPullReplica extends SolrCloudTestCase {
       while (true) {
         DocCollection docCollection = getCollectionState(collectionName);
         assertNotNull(docCollection);
-        assertEquals("Expecting 4 replicas per shard", 8, 
docCollection.getReplicas().size());
+        assertEquals("Expecting 4 replicas per shard", 8, 
docCollection.replicaStream().count());
         assertEquals(
             "Expecting 6 pull replicas, 3 per shard",
             6,
@@ -773,7 +773,9 @@ public class TestPullReplica extends SolrCloudTestCase {
 
     // index a few docs and wait to ensure everything is in sync with our 
expectations
     addDocs(numDocsAdded);
-    waitForNumDocsInAllReplicas(numDocsAdded, 
getCollectionState(collectionName).getReplicas());
+    waitForNumDocsInAllReplicas(
+        numDocsAdded,
+        
getCollectionState(collectionName).replicaStream().collect(Collectors.toList()));
     waitForState(
         "Replica prop never added?",
         collectionName,
@@ -841,14 +843,16 @@ public class TestPullReplica extends SolrCloudTestCase {
     waitForState(
         "Special PULL should be ACTIVE, all others should be DOWN",
         collectionName,
-        (liveNodes, colState) -> {
-          for (Replica r : colState.getReplicas()) {
-            if (r.getName().equals(pullThatSkipsRecovery)) {
-              if (!r.getState().equals(Replica.State.ACTIVE)) {
+        (liveNodes, collectionState) -> {
+          for (Slice slice : collectionState) {
+            for (Replica r : slice.getReplicas()) {
+              if (r.getName().equals(pullThatSkipsRecovery)) {
+                if (!r.getState().equals(Replica.State.ACTIVE)) {
+                  return false;
+                }
+              } else if (!r.getState().equals(Replica.State.DOWN)) {
                 return false;
               }
-            } else if (!r.getState().equals(Replica.State.DOWN)) {
-              return false;
             }
           }
           return true;
@@ -858,7 +862,9 @@ public class TestPullReplica extends SolrCloudTestCase {
     tlogLeaderyJetty.start();
     waitForState(
         "Leader should be back, all replicas active", collectionName, 
activeReplicaCount(0, 1, 3));
-    waitForNumDocsInAllReplicas(numDocsAdded, 
getCollectionState(collectionName).getReplicas());
+    waitForNumDocsInAllReplicas(
+        numDocsAdded,
+        
getCollectionState(collectionName).replicaStream().collect(Collectors.toList()));
   }
 
   private void waitForNumDocsInAllActiveReplicas(int numDocs)
@@ -866,7 +872,8 @@ public class TestPullReplica extends SolrCloudTestCase {
     DocCollection docCollection = getCollectionState(collectionName);
     waitForNumDocsInAllReplicas(
         numDocs,
-        docCollection.getReplicas().stream()
+        docCollection
+            .replicaStream()
             .filter(r -> r.getState() == Replica.State.ACTIVE)
             .collect(Collectors.toList()));
   }
@@ -978,15 +985,17 @@ public class TestPullReplica extends SolrCloudTestCase {
    */
   private CollectionStatePredicate clusterStateReflectsActiveAndDownReplicas() 
{
     return (liveNodes, collectionState) -> {
-      for (Replica r : collectionState.getReplicas()) {
-        if (r.getState() != Replica.State.DOWN && r.getState() != 
Replica.State.ACTIVE) {
-          return false;
-        }
-        if (r.getState() == Replica.State.DOWN && 
liveNodes.contains(r.getNodeName())) {
-          return false;
-        }
-        if (r.getState() == Replica.State.ACTIVE && 
!liveNodes.contains(r.getNodeName())) {
-          return false;
+      for (Slice slice : collectionState) {
+        for (Replica r : slice.getReplicas()) {
+          if (r.getState() != Replica.State.DOWN && r.getState() != 
Replica.State.ACTIVE) {
+            return false;
+          }
+          if (r.getState() == Replica.State.DOWN && 
liveNodes.contains(r.getNodeName())) {
+            return false;
+          }
+          if (r.getState() == Replica.State.ACTIVE && 
!liveNodes.contains(r.getNodeName())) {
+            return false;
+          }
         }
       }
       return true;
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/TestQueryingOnDownCollection.java 
b/solr/core/src/test/org/apache/solr/cloud/TestQueryingOnDownCollection.java
index e95bf3493fb..2d9d9749221 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestQueryingOnDownCollection.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestQueryingOnDownCollection.java
@@ -16,7 +16,6 @@
  */
 package org.apache.solr.cloud;
 
-import java.util.List;
 import java.util.Map;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
@@ -72,10 +71,9 @@ public class TestQueryingOnDownCollection extends 
SolrCloudTestCase {
     downAllReplicas();
 
     // assert all replicas are in down state
-    List<Replica> replicas = getCollectionState(COLLECTION_NAME).getReplicas();
-    for (Replica replica : replicas) {
-      assertEquals(replica.getState(), Replica.State.DOWN);
-    }
+    getCollectionState(COLLECTION_NAME)
+        .replicaStream()
+        .forEach(replica -> assertEquals(replica.getState(), 
Replica.State.DOWN));
 
     // assert all nodes as active
     assertEquals(3, 
cluster.getSolrClient().getClusterStateProvider().getLiveNodes().size());
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 aec3a34ed36..ce0fb84f6d5 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java
@@ -197,7 +197,7 @@ public class TestTlogReplica extends SolrCloudTestCase {
       DocCollection docCollection = getCollectionState(collectionName);
       assertNotNull(docCollection);
       assertEquals("Expecting 2 shards", 2, docCollection.getSlices().size());
-      assertEquals("Expecting 4 replicas per shard", 8, 
docCollection.getReplicas().size());
+      assertEquals("Expecting 4 replicas per shard", 8, 
docCollection.replicaStream().count());
       assertEquals(
           "Expecting 8 tlog replicas, 4 per shard",
           8,
@@ -718,7 +718,8 @@ public class TestTlogReplica extends SolrCloudTestCase {
   }
 
   private List<Replica> getNonLeaderReplicas(String collectionName) {
-    return getCollectionState(collectionName).getReplicas().stream()
+    return getCollectionState(collectionName)
+        .replicaStream()
         .filter((r) -> !r.getBool("leader", false))
         .collect(Collectors.toList());
   }
@@ -976,7 +977,8 @@ public class TestTlogReplica extends SolrCloudTestCase {
     DocCollection docCollection = getCollectionState(collectionName);
     waitForNumDocsInAllReplicas(
         numDocs,
-        docCollection.getReplicas().stream()
+        docCollection
+            .replicaStream()
             .filter(r -> r.getState() == Replica.State.ACTIVE)
             .collect(Collectors.toList()),
         timeout);
@@ -1062,15 +1064,17 @@ public class TestTlogReplica extends SolrCloudTestCase {
    */
   private CollectionStatePredicate clusterStateReflectsActiveAndDownReplicas() 
{
     return (liveNodes, collectionState) -> {
-      for (Replica r : collectionState.getReplicas()) {
-        if (r.getState() != Replica.State.DOWN && r.getState() != 
Replica.State.ACTIVE) {
-          return false;
-        }
-        if (r.getState() == Replica.State.DOWN && 
liveNodes.contains(r.getNodeName())) {
-          return false;
-        }
-        if (r.getState() == Replica.State.ACTIVE && 
!liveNodes.contains(r.getNodeName())) {
-          return false;
+      for (Slice slice : collectionState) {
+        for (Replica r : slice.getReplicas()) {
+          if (r.getState() != Replica.State.DOWN && r.getState() != 
Replica.State.ACTIVE) {
+            return false;
+          }
+          if (r.getState() == Replica.State.DOWN && 
liveNodes.contains(r.getNodeName())) {
+            return false;
+          }
+          if (r.getState() == Replica.State.ACTIVE && 
!liveNodes.contains(r.getNodeName())) {
+            return false;
+          }
         }
       }
       return true;
diff --git a/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java 
b/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java
index 5c7cd7442d3..1d868787d48 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java
@@ -373,8 +373,7 @@ public class ZkControllerTest extends SolrCloudTestCase {
                   TimeUnit.SECONDS,
                   collectionState ->
                       Optional.ofNullable(collectionState)
-                              .map(DocCollection::getReplicas)
-                              .map(List::size)
+                              .map(c -> (int) c.replicaStream().count())
                               .orElse(0)
                           == 3);
         }
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 fae4f9ceaa4..375146bc8a5 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
@@ -18,7 +18,6 @@ package org.apache.solr.cloud.api.collections;
 
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.apache.solr.common.cloud.DocCollection;
@@ -112,12 +111,8 @@ public class CollectionTooManyReplicasTest extends 
SolrCloudTestCase {
     waitForState(
         "Expected to see all replicas active",
         collectionName,
-        c -> {
-          for (Replica r : c.getReplicas()) {
-            if (r.getState() != Replica.State.ACTIVE) return false;
-          }
-          return true;
-        });
+        docCollection ->
+            docCollection.replicaStream().allMatch(r -> r.getState() == 
Replica.State.ACTIVE));
   }
 
   @Test
@@ -258,10 +253,10 @@ public class CollectionTooManyReplicasTest extends 
SolrCloudTestCase {
   }
 
   private List<String> getAllNodeNames(String collectionName) {
-    DocCollection state = getCollectionState(collectionName);
-    return state.getReplicas().stream()
+    return getCollectionState(collectionName)
+        .replicaStream()
         .map(Replica::getNodeName)
         .distinct()
-        .collect(Collectors.toList());
+        .toList();
   }
 }
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/api/collections/ShardSplitTest.java 
b/solr/core/src/test/org/apache/solr/cloud/api/collections/ShardSplitTest.java
index c55843dab9d..088eb242124 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/api/collections/ShardSplitTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/api/collections/ShardSplitTest.java
@@ -137,7 +137,7 @@ public class ShardSplitTest extends BasicDistributedZkTest {
         cloudClient
             .getClusterState()
             .getCollection(AbstractFullDistribZkTestBase.DEFAULT_COLLECTION);
-    Replica replica = defCol.getReplicas().get(0);
+    Replica replica = defCol.replicaStream().findFirst().orElseThrow();
     String nodeName = replica.getNodeName();
 
     String collectionName = "testSplitStaticIndexReplication_" + 
splitMethod.toLower();
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java
 
b/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java
index b8c1a0e9a3e..c1fe64a73da 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java
@@ -381,7 +381,8 @@ public class TestCollectionAPI extends 
ReplicaPropertiesBase {
           TimeUnit.SECONDS,
           docCollection ->
               docCollection != null
-                  && docCollection.getReplicas().stream()
+                  && docCollection
+                      .replicaStream()
                       .anyMatch(r -> r.getState().equals(Replica.State.DOWN) 
&& !r.isLeader()));
       zkStateReader.waitForState(
           COLLECTION_NAME,
@@ -420,7 +421,7 @@ public class TestCollectionAPI extends 
ReplicaPropertiesBase {
                 TimeUnit.SECONDS,
                 (liveNodes, coll) ->
                     coll != null
-                        && coll.getReplicas().stream()
+                        && coll.replicaStream()
                             .allMatch(r -> 
r.getState().equals(Replica.State.ACTIVE)));
         rsp = request.process(newClient).getResponse();
         collection =
diff --git 
a/solr/core/src/test/org/apache/solr/cluster/placement/impl/PlacementPluginIntegrationTest.java
 
b/solr/core/src/test/org/apache/solr/cluster/placement/impl/PlacementPluginIntegrationTest.java
index 3ffb39bf373..2fb85423006 100644
--- 
a/solr/core/src/test/org/apache/solr/cluster/placement/impl/PlacementPluginIntegrationTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cluster/placement/impl/PlacementPluginIntegrationTest.java
@@ -341,7 +341,8 @@ public class PlacementPluginIntegrationTest extends 
SolrCloudTestCase {
 
     // try deleting secondary replica from node without the primary replica
     Optional<String> onlySecondaryReplica =
-        secondary.getReplicas().stream()
+        secondary
+            .replicaStream()
             .filter(replica -> !nodeSet.contains(replica.getNodeName()))
             .map(replica -> replica.getName())
             .findFirst();
@@ -355,7 +356,8 @@ public class PlacementPluginIntegrationTest extends 
SolrCloudTestCase {
 
     // try deleting secondary replica from node WITH the primary replica - 
should fail
     Optional<String> secondaryWithPrimaryReplica =
-        secondary.getReplicas().stream()
+        secondary
+            .replicaStream()
             .filter(replica -> nodeSet.contains(replica.getNodeName()))
             .map(replica -> replica.getName())
             .findFirst();
diff --git a/solr/core/src/test/org/apache/solr/search/TestCoordinatorRole.java 
b/solr/core/src/test/org/apache/solr/search/TestCoordinatorRole.java
index 3dbe061559f..053fb9e4d7c 100644
--- a/solr/core/src/test/org/apache/solr/search/TestCoordinatorRole.java
+++ b/solr/core/src/test/org/apache/solr/search/TestCoordinatorRole.java
@@ -798,8 +798,9 @@ public class TestCoordinatorRole extends SolrCloudTestCase {
           "Cannot find replica on first node yet",
           COLLECTION_NAME,
           collectionState -> {
-            if (collectionState.getReplicas().size() == 1) {
-              Replica replica = collectionState.getReplicas().get(0);
+            List<Replica> replicas = collectionState.replicaStream().toList();
+            if (replicas.size() == 1) {
+              Replica replica = replicas.get(0);
               return fromNode.equals(replica.getNodeName())
                   && replica.getState() == Replica.State.ACTIVE;
             }
@@ -832,15 +833,17 @@ public class TestCoordinatorRole extends 
SolrCloudTestCase {
       assertEquals(DOC_PER_COLLECTION_COUNT, 
response.getResults().getNumFound());
 
       // now move the shard/replica
-      String replicaName = 
getCollectionState(COLLECTION_NAME).getReplicas().get(0).getName();
+      String replicaName =
+          
getCollectionState(COLLECTION_NAME).replicaStream().findFirst().orElseThrow().getName();
       String toNodeName = dataNodes.get(1);
       CollectionAdminRequest.moveReplica(COLLECTION_NAME, replicaName, 
toNodeName).process(client);
       waitForState(
           "Cannot find replica on second node yet after repliac move",
           COLLECTION_NAME,
           collectionState -> {
-            if (collectionState.getReplicas().size() == 1) {
-              Replica replica = collectionState.getReplicas().get(0);
+            List<Replica> replicas = collectionState.replicaStream().toList();
+            if (replicas.size() == 1) {
+              Replica replica = replicas.get(0);
               return toNodeName.equals(replica.getNodeName())
                   && replica.getState() == Replica.State.ACTIVE;
             }
diff --git a/solr/core/src/test/org/apache/solr/servlet/HttpSolrProxyTest.java 
b/solr/core/src/test/org/apache/solr/servlet/HttpSolrProxyTest.java
new file mode 100644
index 00000000000..9f182dc416f
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/servlet/HttpSolrProxyTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.servlet;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.embedded.JettySolrRunner;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Tests {@link HttpSolrProxy}, the internode proxy used when a request lands 
on a node that does
+ * not host a replica of the target collection. Verifies that a request body 
with a known length is
+ * forwarded successfully and that the origin's {@code Content-Length} is 
preserved on the response.
+ */
[email protected]
+public class HttpSolrProxyTest extends SolrCloudTestCase {
+  private static final String COLLECTION = "proxycoll";
+
+  private static JettySolrRunner owningNode;
+  private static JettySolrRunner proxyingNode;
+
+  @BeforeClass
+  public static void setupCluster() throws Exception {
+    configureCluster(2).addConfig("conf", 
configset("cloud-minimal")).configure();
+
+    // Single shard + replica, so exactly one node hosts it; the other must 
proxy.
+    CollectionAdminRequest.createCollection(COLLECTION, "conf", 1, 1)
+        .process(cluster.getSolrClient());
+    cluster.waitForActiveCollection(COLLECTION, 1, 1);
+
+    Replica replica =
+        cluster
+            .getSolrClient()
+            .getClusterState()
+            .getCollection(COLLECTION)
+            .replicaStream()
+            .findFirst()
+            .orElseThrow();
+    for (JettySolrRunner runner : cluster.getJettySolrRunners()) {
+      if (runner.getNodeName().equals(replica.getNodeName())) {
+        owningNode = runner;
+      } else {
+        proxyingNode = runner;
+      }
+    }
+    assertNotNull(owningNode);
+    assertNotNull(proxyingNode);
+  }
+
+  /**
+   * A POST with a body proxied to a node that doesn't host the replica must 
succeed. Jetty 12.1
+   * rejects (400) a forwarded Content-Length that contradicts the client's 
own framing, so the
+   * proxy must convey the known length via the request body content provider 
instead.
+   */
+  @Test
+  public void testProxiedPostWithBodySucceeds() throws Exception {
+    byte[] body = "[{\"id\":\"1\"}]".getBytes(UTF_8);
+    HttpURLConnection conn = open(proxyingNode, "/" + COLLECTION + 
"/update/json?commit=true");
+    conn.setRequestMethod("POST");
+    conn.setDoOutput(true);
+    conn.setFixedLengthStreamingMode(body.length); // send a deterministic 
Content-Length
+    conn.setRequestProperty("Content-Type", "application/json");
+    try (var out = conn.getOutputStream()) {
+      out.write(body);
+    }
+    assertEquals(200, conn.getResponseCode());
+    conn.getInputStream().readAllBytes();
+    conn.disconnect();
+  }
+
+  /**
+   * The proxied response must preserve the origin's Content-Length (rather 
than being forced to
+   * chunked) and it must exactly match the bytes delivered — otherwise Jetty 
12.1 would abort the
+   * re-streamed response with "too much content written".
+   */
+  @Test
+  public void testProxiedResponsePreservesContentLength() throws Exception {
+    String path = "/" + COLLECTION + "/select?q=*:*&rows=0";
+
+    HttpURLConnection direct = open(owningNode, path);
+    assertEquals(200, direct.getResponseCode());
+    String originLen = direct.getHeaderField("Content-Length");
+    direct.getInputStream().readAllBytes();
+    direct.disconnect();
+    assertNotNull(
+        "Precondition: origin should frame this small response with a 
Content-Length", originLen);
+
+    HttpURLConnection proxied = open(proxyingNode, path);
+    assertEquals(200, proxied.getResponseCode());
+    String proxiedLen = proxied.getHeaderField("Content-Length");
+    int proxiedBodyLen = proxied.getInputStream().readAllBytes().length;
+    proxied.disconnect();
+
+    assertNotNull("Proxy dropped the origin's Content-Length (forced 
chunked)", proxiedLen);
+    assertEquals(
+        "Proxied Content-Length must match the bytes delivered",
+        proxiedBodyLen,
+        Integer.parseInt(proxiedLen));
+  }
+
+  private static HttpURLConnection open(JettySolrRunner node, String 
pathAndQuery)
+      throws Exception {
+    URI uri = URI.create(node.getBaseUrl().toString() + pathAndQuery);
+    return (HttpURLConnection) uri.toURL().openConnection();
+  }
+}
diff --git 
a/solr/core/src/test/org/apache/solr/update/DeleteByIdWithRouterFieldTest.java 
b/solr/core/src/test/org/apache/solr/update/DeleteByIdWithRouterFieldTest.java
index 4a92852cbdc..e879b3281bd 100644
--- 
a/solr/core/src/test/org/apache/solr/update/DeleteByIdWithRouterFieldTest.java
+++ 
b/solr/core/src/test/org/apache/solr/update/DeleteByIdWithRouterFieldTest.java
@@ -79,11 +79,14 @@ public class DeleteByIdWithRouterFieldTest extends 
SolrCloudTestCase {
     solrClient = cluster.getSolrClient(COLL);
 
     ClusterState clusterState = cluster.getSolrClient().getClusterState();
-    for (Replica replica : clusterState.getCollection(COLL).getReplicas()) {
-      clients.add(
-          new CollectionScopedSolrClient(
-              cluster.getReplicaJetty(replica).getSolrClient(), 
replica.getCoreName()));
-    }
+    clusterState
+        .getCollection(COLL)
+        .replicaStream()
+        .forEach(
+            replica ->
+                clients.add(
+                    new CollectionScopedSolrClient(
+                        cluster.getReplicaJetty(replica).getSolrClient(), 
replica.getCoreName())));
   }
 
   @AfterClass
diff --git 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/CloudAuthStreamTest.java
 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/CloudAuthStreamTest.java
index 386ff48af29..6bd61e22426 100644
--- 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/CloudAuthStreamTest.java
+++ 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/CloudAuthStreamTest.java
@@ -927,8 +927,7 @@ public class CloudAuthStreamTest extends SolrCloudTestCase {
             .getZkStateReader()
             .getClusterState()
             .getCollectionOrNull(collection)
-            .getReplicas()
-            .stream()
+            .replicaStream()
             .map(Replica::getCoreUrl)
             .collect(Collectors.toList());
     Collections.shuffle(replicaUrls, random());
diff --git 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/MathExpressionTest.java
 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/MathExpressionTest.java
index a1cbd9c0499..5f19648f936 100644
--- 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/MathExpressionTest.java
+++ 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/MathExpressionTest.java
@@ -4025,7 +4025,7 @@ public class MathExpressionTest extends SolrCloudTestCase 
{
     ClusterState clusterState = cluster.getSolrClient().getClusterState();
     String collection = useAlias ? COLLECTIONORALIAS + "_collection" : 
COLLECTIONORALIAS;
     DocCollection coll = clusterState.getCollection(collection);
-    String node = coll.getReplicas().iterator().next().getNodeName();
+    String node = coll.replicaStream().findFirst().orElseThrow().getNodeName();
     String url = null;
     for (JettySolrRunner jetty : cluster.getJettySolrRunners()) {
       if (jetty.getNodeName().equals(node)) {
diff --git 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamDecoratorTest.java
 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamDecoratorTest.java
index 33f4cb8796a..3359ef2754e 100644
--- 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamDecoratorTest.java
+++ 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamDecoratorTest.java
@@ -4417,7 +4417,7 @@ public class StreamDecoratorTest extends 
SolrCloudTestCase {
     // find a node with a replica
     ClusterState clusterState = cluster.getSolrClient().getClusterState();
     DocCollection coll = clusterState.getCollection(COLLECTIONORALIAS);
-    String node = coll.getReplicas().iterator().next().getNodeName();
+    String node = coll.replicaStream().findFirst().orElseThrow().getNodeName();
     String url = null;
     for (JettySolrRunner jetty : cluster.getJettySolrRunners()) {
       if (jetty.getNodeName().equals(node)) {
diff --git 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamingTest.java
 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamingTest.java
index 181f36787a3..54dd1bbf677 100644
--- 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamingTest.java
+++ 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamingTest.java
@@ -2730,7 +2730,7 @@ public class StreamingTest extends SolrCloudTestCase {
     String collName = strings.size() > 0 ? strings.get(0) : COLLECTIONORALIAS;
     zkStateReader.forceUpdateCollection(collName);
     DocCollection collection = 
zkStateReader.getClusterState().getCollectionOrNull(collName);
-    List<Replica> replicas = collection.getReplicas();
+    List<Replica> replicas = collection.replicaStream().toList();
     streamContext
         .getEntries()
         .put("core", 
replicas.get(random().nextInt(replicas.size())).getCoreName());
@@ -3216,7 +3216,7 @@ public class StreamingTest extends SolrCloudTestCase {
       streamContext.setLocal(true);
 
       for (String coll : resolved) {
-        Replica rr = zkStateReader.getCollection(coll).getReplicas().get(0);
+        Replica rr = 
zkStateReader.getCollection(coll).replicaStream().findFirst().orElseThrow();
         streamContext.put("core", rr.core);
         List<Replica> replicas =
             TupleStream.getReplicas(
diff --git 
a/solr/solrj/src/java/org/apache/solr/common/cloud/DocCollection.java 
b/solr/solrj/src/java/org/apache/solr/common/cloud/DocCollection.java
index 407efd7875b..faa6dc8e38b 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/DocCollection.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/DocCollection.java
@@ -31,6 +31,7 @@ import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiConsumer;
 import java.util.function.Supplier;
+import java.util.stream.Stream;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -400,13 +401,9 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
     return slices.values().iterator();
   }
 
-  @Deprecated // low usage and builds an ArrayList (surprising)
-  public List<Replica> getReplicas() {
-    List<Replica> replicas = new ArrayList<>();
-    for (Slice slice : this) {
-      replicas.addAll(slice.getReplicas());
-    }
-    return replicas;
+  /** Stream of all replicas across all slices. */
+  public Stream<Replica> replicaStream() {
+    return getSlices().stream().flatMap(slice -> slice.getReplicas().stream());
   }
 
   @Override
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpClusterStateSSLTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpClusterStateSSLTest.java
index 5ae877eda0a..8881b1ba140 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpClusterStateSSLTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpClusterStateSSLTest.java
@@ -103,7 +103,7 @@ public class HttpClusterStateSSLTest extends 
SolrCloudTestCase {
   private void verifyUrlSchemeInClusterState(
       final DocCollection collection, final int expectedReplicas) {
     assertNotNull(collection);
-    List<Replica> replicas = collection.getReplicas();
+    List<Replica> replicas = collection.replicaStream().toList();
     assertNotNull(replicas);
     assertEquals(expectedReplicas, replicas.size());
     for (Replica r : replicas) {
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/SendUpdatesToLeadersOverrideTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/SendUpdatesToLeadersOverrideTest.java
index 366a048a8a5..08f3367edcc 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/SendUpdatesToLeadersOverrideTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/SendUpdatesToLeadersOverrideTest.java
@@ -92,7 +92,12 @@ public class SendUpdatesToLeadersOverrideTest extends 
SolrCloudTestCase {
             .isSuccess());
 
     final List<Replica> allReplicas =
-        
cluster.getSolrClient().getClusterState().getCollection(COLLECTION_NAME).getReplicas();
+        cluster
+            .getSolrClient()
+            .getClusterState()
+            .getCollection(COLLECTION_NAME)
+            .replicaStream()
+            .collect(Collectors.toList());
     assertEquals(
         "test preconditions were broken, each replica should have it's own 
node",
         numNodes,
diff --git 
a/solr/solrj/src/test/org/apache/solr/common/cloud/PerReplicaStatesIntegrationTest.java
 
b/solr/solrj/src/test/org/apache/solr/common/cloud/PerReplicaStatesIntegrationTest.java
index 1bd29967657..009857689d1 100644
--- 
a/solr/solrj/src/test/org/apache/solr/common/cloud/PerReplicaStatesIntegrationTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/common/cloud/PerReplicaStatesIntegrationTest.java
@@ -97,7 +97,7 @@ public class PerReplicaStatesIntegrationTest extends 
SolrCloudTestCase {
       assertEquals(5, prs.states.size());
 
       // Test delete replica
-      Replica leader = 
c.getReplicas().stream().filter(Replica::isLeader).findFirst().orElseThrow();
+      Replica leader = 
c.replicaStream().filter(Replica::isLeader).findFirst().orElseThrow();
       CollectionAdminRequest.deleteReplica(testCollection, leader.shard, 
leader.getName())
           .process(cluster.getSolrClient());
       cluster.waitForActiveCollection(testCollection, 2, 4);
diff --git 
a/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCloudBackupRestoreTestCase.java
 
b/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCloudBackupRestoreTestCase.java
index f569f3f12e0..1e84c5e8652 100644
--- 
a/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCloudBackupRestoreTestCase.java
+++ 
b/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCloudBackupRestoreTestCase.java
@@ -395,12 +395,16 @@ public abstract class AbstractCloudBackupRestoreTestCase 
extends SolrCloudTestCa
 
     Map<String, Integer> numReplicasByNodeName = new HashMap<>();
     restoreCollection
-        .getReplicas()
+        .getSlices()
         .forEach(
-            x -> {
-              numReplicasByNodeName.put(
-                  x.getNodeName(), 
numReplicasByNodeName.getOrDefault(x.getNodeName(), 0) + 1);
-            });
+            slice ->
+                slice
+                    .getReplicas()
+                    .forEach(
+                        x ->
+                            numReplicasByNodeName.put(
+                                x.getNodeName(),
+                                
numReplicasByNodeName.getOrDefault(x.getNodeName(), 0) + 1)));
     numReplicasByNodeName.forEach(
         (k, v) -> {
           assertTrue(
diff --git 
a/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCollectionsAPIDistributedZkTestBase.java
 
b/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCollectionsAPIDistributedZkTestBase.java
index f4bc7e54bb8..cc64a27a865 100644
--- 
a/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCollectionsAPIDistributedZkTestBase.java
+++ 
b/solr/test-framework/src/java/org/apache/solr/cloud/api/collections/AbstractCollectionsAPIDistributedZkTestBase.java
@@ -363,16 +363,18 @@ public abstract class 
AbstractCollectionsAPIDistributedZkTestBase extends SolrCl
         .process(cluster.getSolrClient());
 
     DocCollection collectionState = getCollectionState("nodeset_collection");
-    for (Replica replica : collectionState.getReplicas()) {
-      String replicaUrl = replica.getCoreUrl();
-      boolean matchingJetty = false;
-      for (String jettyUrl : baseUrls) {
-        if (replicaUrl.startsWith(jettyUrl)) {
-          matchingJetty = true;
+    for (Slice slice : collectionState) {
+      for (Replica replica : slice.getReplicas()) {
+        String replicaUrl = replica.getCoreUrl();
+        boolean matchingJetty = false;
+        for (String jettyUrl : baseUrls) {
+          if (replicaUrl.startsWith(jettyUrl)) {
+            matchingJetty = true;
+          }
+        }
+        if (matchingJetty == false) {
+          fail("Expected replica to be on " + baseUrls + " but was on " + 
replicaUrl);
         }
-      }
-      if (matchingJetty == false) {
-        fail("Expected replica to be on " + baseUrls + " but was on " + 
replicaUrl);
       }
     }
   }
@@ -682,7 +684,8 @@ public abstract class 
AbstractCollectionsAPIDistributedZkTestBase extends SolrCl
   private Replica grabNewReplica(CollectionAdminResponse response, 
DocCollection docCollection) {
     String replicaName = 
response.getCollectionCoresStatus().keySet().iterator().next();
     Optional<Replica> optional =
-        docCollection.getReplicas().stream()
+        docCollection
+            .replicaStream()
             .filter(replica -> replicaName.equals(replica.getCoreName()))
             .findAny();
     if (optional.isPresent()) {
diff --git 
a/solr/webapp/src/test/org/apache/solr/webapp/AdminUiCollectionsScreenTest.java 
b/solr/webapp/src/test/org/apache/solr/webapp/AdminUiCollectionsScreenTest.java
index 4d9983af598..8dad9b0be8a 100644
--- 
a/solr/webapp/src/test/org/apache/solr/webapp/AdminUiCollectionsScreenTest.java
+++ 
b/solr/webapp/src/test/org/apache/solr/webapp/AdminUiCollectionsScreenTest.java
@@ -189,12 +189,13 @@ public class AdminUiCollectionsScreenTest extends 
AdminUiTestBase {
 
   private int replicaCount() {
     try {
-      return cluster
-          .getSolrClient()
-          .getClusterState()
-          .getCollection(COLLECTION)
-          .getReplicas()
-          .size();
+      return (int)
+          cluster
+              .getSolrClient()
+              .getClusterState()
+              .getCollection(COLLECTION)
+              .replicaStream()
+              .count();
     } catch (Exception e) {
       throw new RuntimeException(e);
     }

Reply via email to