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

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


The following commit(s) were added to refs/heads/branch_9x by this push:
     new 60e2c6309fd SOLR-16712: Simplify DocCollection ctor for PRS enabled 
collection (#1477)
60e2c6309fd is described below

commit 60e2c6309fd762eccbf62c4256ab2ab72a8c4694
Author: patsonluk <[email protected]>
AuthorDate: Wed Apr 26 06:05:18 2023 -0700

    SOLR-16712: Simplify DocCollection ctor for PRS enabled collection (#1477)
---
 .../solr/cloud/DistributedClusterStateUpdater.java |   3 +-
 .../solr/cloud/overseer/ClusterStateMutator.java   |   2 +-
 .../solr/cloud/overseer/CollectionMutator.java     |   2 +-
 .../apache/solr/cloud/overseer/ZkStateWriter.java  |  13 +-
 .../org/apache/solr/cloud/ClusterStateTest.java    |   6 +-
 .../OverseerCollectionConfigSetProcessorTest.java  |   2 +-
 .../solr/cloud/overseer/ZkStateReaderTest.java     |  44 +++----
 .../solr/cloud/overseer/ZkStateWriterTest.java     |  14 +--
 .../client/solrj/cloud/DistribStateManager.java    |  15 ++-
 .../solrj/impl/ZkClientClusterStateProvider.java   |   2 +-
 .../solr/common/cloud/PerReplicaStatesFetcher.java |   7 +-
 .../apache/solr/common/cloud/ZkStateReader.java    |   4 +-
 .../solrj/impl/BaseHttpClusterStateProvider.java   |  12 +-
 .../org/apache/solr/common/cloud/ClusterState.java |   2 +-
 .../apache/solr/common/cloud/DocCollection.java    | 134 ++++++++++++++-------
 .../apache/solr/common/cloud/PerReplicaStates.java |   4 +
 .../java/org/apache/solr/common/cloud/Replica.java |  19 +--
 .../java/org/apache/solr/common/cloud/Slice.java   |  11 +-
 18 files changed, 176 insertions(+), 120 deletions(-)

diff --git 
a/solr/core/src/java/org/apache/solr/cloud/DistributedClusterStateUpdater.java 
b/solr/core/src/java/org/apache/solr/cloud/DistributedClusterStateUpdater.java
index d8c05146d33..935f32e5940 100644
--- 
a/solr/core/src/java/org/apache/solr/cloud/DistributedClusterStateUpdater.java
+++ 
b/solr/core/src/java/org/apache/solr/cloud/DistributedClusterStateUpdater.java
@@ -511,7 +511,8 @@ public class DistributedClusterStateUpdater {
             // Transpose the per replica states into the cluster state
             updatedState =
                 updatedState.copyWith(
-                    updater.getCollectionName(), 
docCollection.copyWith(fetchedPerReplicaStates));
+                    updater.getCollectionName(),
+                    
docCollection.setPerReplicaStates(fetchedPerReplicaStates));
           }
         }
 
diff --git 
a/solr/core/src/java/org/apache/solr/cloud/overseer/ClusterStateMutator.java 
b/solr/core/src/java/org/apache/solr/cloud/overseer/ClusterStateMutator.java
index a8faa27fbf8..2c625b2c5c4 100644
--- a/solr/core/src/java/org/apache/solr/cloud/overseer/ClusterStateMutator.java
+++ b/solr/core/src/java/org/apache/solr/cloud/overseer/ClusterStateMutator.java
@@ -127,7 +127,7 @@ public class ClusterStateMutator {
 
     assert !collectionProps.containsKey(CollectionAdminParams.COLL_CONF);
     DocCollection newCollection =
-        new DocCollection(
+        DocCollection.create(
             cName, slices, collectionProps, router, -1, 
stateManager.getPrsSupplier(cName));
 
     return new ZkWriteCommand(cName, newCollection);
diff --git 
a/solr/core/src/java/org/apache/solr/cloud/overseer/CollectionMutator.java 
b/solr/core/src/java/org/apache/solr/cloud/overseer/CollectionMutator.java
index 492fe9efeb5..028beb37dfd 100644
--- a/solr/core/src/java/org/apache/solr/cloud/overseer/CollectionMutator.java
+++ b/solr/core/src/java/org/apache/solr/cloud/overseer/CollectionMutator.java
@@ -171,7 +171,7 @@ public class CollectionMutator {
     }
 
     DocCollection collection =
-        new DocCollection(
+        DocCollection.create(
             coll.getName(),
             coll.getSlicesMap(),
             props,
diff --git 
a/solr/core/src/java/org/apache/solr/cloud/overseer/ZkStateWriter.java 
b/solr/core/src/java/org/apache/solr/cloud/overseer/ZkStateWriter.java
index 9e457f53f89..e38e58b3798 100644
--- a/solr/core/src/java/org/apache/solr/cloud/overseer/ZkStateWriter.java
+++ b/solr/core/src/java/org/apache/solr/cloud/overseer/ZkStateWriter.java
@@ -268,10 +268,11 @@ public class ZkStateWriter {
           // Update the Per Replica State znodes if needed
           if (cmd.ops != null) {
             cmd.ops.persist(path, reader.getZkClient());
+
             clusterState =
                 clusterState.copyWith(
                     name,
-                    cmd.collection.copyWith(
+                    cmd.collection.setPerReplicaStates(
                         PerReplicaStatesFetcher.fetch(
                             cmd.collection.getZNode(), reader.getZkClient(), 
null)));
           }
@@ -295,25 +296,25 @@ public class ZkStateWriter {
               }
               Stat stat = reader.getZkClient().setData(path, data, 
c.getZNodeVersion(), true);
               DocCollection newCollection =
-                  new DocCollection(
+                  DocCollection.create(
                       name,
                       c.getSlicesMap(),
                       c.getProperties(),
                       c.getRouter(),
                       stat.getVersion(),
-                      new 
PerReplicaStatesFetcher.LazyPrsSupplier(reader.getZkClient(), path));
+                      
PerReplicaStatesFetcher.getZkClientPrsSupplier(reader.getZkClient(), path));
               clusterState = clusterState.copyWith(name, newCollection);
             } else {
               log.debug("going to create_collection {}", path);
               reader.getZkClient().create(path, data, CreateMode.PERSISTENT, 
true);
               DocCollection newCollection =
-                  new DocCollection(
+                  DocCollection.create(
                       name,
                       c.getSlicesMap(),
                       c.getProperties(),
                       c.getRouter(),
                       0,
-                      new 
PerReplicaStatesFetcher.LazyPrsSupplier(reader.getZkClient(), path));
+                      
PerReplicaStatesFetcher.getZkClientPrsSupplier(reader.getZkClient(), path));
               clusterState = clusterState.copyWith(name, newCollection);
             }
           }
@@ -324,7 +325,7 @@ public class ZkStateWriter {
               clusterState =
                   clusterState.copyWith(
                       name,
-                      currentCollState.copyWith(
+                      currentCollState.setPerReplicaStates(
                           PerReplicaStatesFetcher.fetch(
                               currentCollState.getZNode(), 
reader.getZkClient(), null)));
             }
diff --git a/solr/core/src/test/org/apache/solr/cloud/ClusterStateTest.java 
b/solr/core/src/test/org/apache/solr/cloud/ClusterStateTest.java
index 28ffd0bd564..ac9b5ffa856 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ClusterStateTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ClusterStateTest.java
@@ -59,9 +59,11 @@ public class ClusterStateTest extends SolrTestCaseJ4 {
     Slice slice2 = new Slice("shard2", sliceToProps, null, "collection1");
     slices.put("shard2", slice2);
     collectionStates.put(
-        "collection1", new DocCollection("collection1", slices, props, 
DocRouter.DEFAULT, 0, null));
+        "collection1",
+        DocCollection.create("collection1", slices, props, DocRouter.DEFAULT, 
0, null));
     collectionStates.put(
-        "collection2", new DocCollection("collection2", slices, props, 
DocRouter.DEFAULT, 0, null));
+        "collection2",
+        DocCollection.create("collection2", slices, props, DocRouter.DEFAULT, 
0, null));
 
     ClusterState clusterState = new ClusterState(liveNodes, collectionStates);
     
assertFalse(clusterState.getCollection("collection1").getProperties().containsKey("shards"));
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/OverseerCollectionConfigSetProcessorTest.java
 
b/solr/core/src/test/org/apache/solr/cloud/OverseerCollectionConfigSetProcessorTest.java
index 9f5a2f26e8e..ac3df8b4373 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/OverseerCollectionConfigSetProcessorTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/OverseerCollectionConfigSetProcessorTest.java
@@ -685,7 +685,7 @@ public class OverseerCollectionConfigSetProcessorTest 
extends SolrTestCaseJ4 {
           collectionsSet.put(
               collName,
               new ClusterState.CollectionRef(
-                  new DocCollection(
+                  DocCollection.create(
                       collName,
                       new HashMap<>(),
                       props.getProperties(),
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 b2b98fa1ea4..52671ed003b 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
@@ -138,13 +138,13 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
     ZkWriteCommand c1 =
         new ZkWriteCommand(
             "c1",
-            new DocCollection(
+            DocCollection.create(
                 "c1",
                 new HashMap<>(),
                 Map.of(ZkStateReader.CONFIGNAME_PROP, 
ConfigSetsHandler.DEFAULT_CONFIGSET_NAME),
                 DocRouter.DEFAULT,
                 0,
-                new PerReplicaStatesFetcher.LazyPrsSupplier(
+                PerReplicaStatesFetcher.getZkClientPrsSupplier(
                     fixture.zkClient, DocCollection.getCollectionPath("c1"))));
 
     writer.enqueueUpdate(reader.getClusterState(), 
Collections.singletonList(c1), null);
@@ -165,13 +165,13 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
     fixture.zkClient.makePath(ZkStateReader.COLLECTIONS_ZKNODE + "/c1", true);
 
     DocCollection state =
-        new DocCollection(
+        DocCollection.create(
             "c1",
             new HashMap<>(),
             Map.of(ZkStateReader.CONFIGNAME_PROP, 
ConfigSetsHandler.DEFAULT_CONFIGSET_NAME),
             DocRouter.DEFAULT,
             0,
-            new PerReplicaStatesFetcher.LazyPrsSupplier(
+            PerReplicaStatesFetcher.getZkClientPrsSupplier(
                 fixture.zkClient, DocCollection.getCollectionPath("c1")));
     ZkWriteCommand wc = new ZkWriteCommand("c1", state);
     writer.enqueueUpdate(reader.getClusterState(), 
Collections.singletonList(wc), null);
@@ -184,13 +184,13 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
     props.put("x", "y");
     props.put(ZkStateReader.CONFIGNAME_PROP, 
ConfigSetsHandler.DEFAULT_CONFIGSET_NAME);
     state =
-        new DocCollection(
+        DocCollection.create(
             "c1",
             new HashMap<>(),
             props,
             DocRouter.DEFAULT,
             0,
-            new PerReplicaStatesFetcher.LazyPrsSupplier(
+            PerReplicaStatesFetcher.getZkClientPrsSupplier(
                 fixture.zkClient, DocCollection.getCollectionPath("c1")));
     wc = new ZkWriteCommand("c1", state);
     writer.enqueueUpdate(reader.getClusterState(), 
Collections.singletonList(wc), null);
@@ -225,13 +225,13 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
 
     // create new collection
     DocCollection state =
-        new DocCollection(
+        DocCollection.create(
             "c1",
             new HashMap<>(),
             Map.of(ZkStateReader.CONFIGNAME_PROP, 
ConfigSetsHandler.DEFAULT_CONFIGSET_NAME),
             DocRouter.DEFAULT,
             0,
-            new PerReplicaStatesFetcher.LazyPrsSupplier(
+            PerReplicaStatesFetcher.getZkClientPrsSupplier(
                 fixture.zkClient, DocCollection.getCollectionPath("c1")));
     ZkWriteCommand wc = new ZkWriteCommand("c1", state);
     writer.enqueueUpdate(reader.getClusterState(), 
Collections.singletonList(wc), null);
@@ -259,7 +259,7 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
     ClusterState clusterState = reader.getClusterState();
     // create new collection
     DocCollection state =
-        new DocCollection(
+        DocCollection.create(
             "c1",
             new HashMap<>(),
             Map.of(
@@ -269,7 +269,7 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
                 "true"),
             DocRouter.DEFAULT,
             0,
-            new PerReplicaStatesFetcher.LazyPrsSupplier(
+            PerReplicaStatesFetcher.getZkClientPrsSupplier(
                 fixture.zkClient, DocCollection.getCollectionPath("c1")));
     ZkWriteCommand wc = new ZkWriteCommand("c1", state);
     writer.enqueueUpdate(clusterState, Collections.singletonList(wc), null);
@@ -383,13 +383,13 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
 
     // create new collection
     DocCollection state =
-        new DocCollection(
+        DocCollection.create(
             "c1",
             new HashMap<>(),
             Map.of(ZkStateReader.CONFIGNAME_PROP, 
ConfigSetsHandler.DEFAULT_CONFIGSET_NAME),
             DocRouter.DEFAULT,
             0,
-            new PerReplicaStatesFetcher.LazyPrsSupplier(
+            PerReplicaStatesFetcher.getZkClientPrsSupplier(
                 fixture.zkClient, DocCollection.getCollectionPath("c1")));
     ZkWriteCommand wc = new ZkWriteCommand("c1", state);
     writer.enqueueUpdate(reader.getClusterState(), 
Collections.singletonList(wc), null);
@@ -405,13 +405,13 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
 
     // update the collection
     state =
-        new DocCollection(
+        DocCollection.create(
             "c1",
             new HashMap<>(),
             Map.of(ZkStateReader.CONFIGNAME_PROP, 
ConfigSetsHandler.DEFAULT_CONFIGSET_NAME),
             DocRouter.DEFAULT,
             ref.get().getZNodeVersion(),
-            new PerReplicaStatesFetcher.LazyPrsSupplier(
+            PerReplicaStatesFetcher.getZkClientPrsSupplier(
                 fixture.zkClient, DocCollection.getCollectionPath("c1")));
     wc = new ZkWriteCommand("c1", state);
     writer.enqueueUpdate(reader.getClusterState(), 
Collections.singletonList(wc), null);
@@ -428,13 +428,13 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
 
     fixture.zkClient.makePath(ZkStateReader.COLLECTIONS_ZKNODE + "/c2", true);
     state =
-        new DocCollection(
+        DocCollection.create(
             "c2",
             new HashMap<>(),
             Map.of(ZkStateReader.CONFIGNAME_PROP, 
ConfigSetsHandler.DEFAULT_CONFIGSET_NAME),
             DocRouter.DEFAULT,
             0,
-            new PerReplicaStatesFetcher.LazyPrsSupplier(
+            PerReplicaStatesFetcher.getZkClientPrsSupplier(
                 fixture.zkClient, DocCollection.getCollectionPath("c2")));
     ZkWriteCommand wc2 = new ZkWriteCommand("c2", state);
 
@@ -544,23 +544,23 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
 
     // now create both c1 (watched) and c2 (not watched)
     DocCollection state1 =
-        new DocCollection(
+        DocCollection.create(
             "c1",
             new HashMap<>(),
             Map.of(ZkStateReader.CONFIGNAME_PROP, 
ConfigSetsHandler.DEFAULT_CONFIGSET_NAME),
             DocRouter.DEFAULT,
             0,
-            new PerReplicaStatesFetcher.LazyPrsSupplier(
+            PerReplicaStatesFetcher.getZkClientPrsSupplier(
                 fixture.zkClient, DocCollection.getCollectionPath("c1")));
     ZkWriteCommand wc1 = new ZkWriteCommand("c1", state1);
     DocCollection state2 =
-        new DocCollection(
+        DocCollection.create(
             "c2",
             new HashMap<>(),
             Map.of(ZkStateReader.CONFIGNAME_PROP, 
ConfigSetsHandler.DEFAULT_CONFIGSET_NAME),
             DocRouter.DEFAULT,
             0,
-            new PerReplicaStatesFetcher.LazyPrsSupplier(
+            PerReplicaStatesFetcher.getZkClientPrsSupplier(
                 fixture.zkClient, DocCollection.getCollectionPath("c1")));
 
     // do not listen to c2
@@ -607,7 +607,7 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
                 int currentVersion = collection != null ? 
collection.getZNodeVersion() : 0;
                 // create new collection
                 DocCollection state =
-                    new DocCollection(
+                    DocCollection.create(
                         "c1",
                         new HashMap<>(),
                         Map.of(
@@ -615,7 +615,7 @@ public class ZkStateReaderTest extends SolrTestCaseJ4 {
                             ConfigSetsHandler.DEFAULT_CONFIGSET_NAME),
                         DocRouter.DEFAULT,
                         currentVersion,
-                        new PerReplicaStatesFetcher.LazyPrsSupplier(
+                        PerReplicaStatesFetcher.getZkClientPrsSupplier(
                             fixture.zkClient, 
DocCollection.getCollectionPath("c1")));
                 ZkWriteCommand wc = new ZkWriteCommand("c1", state);
                 writer.enqueueUpdate(clusterState, 
Collections.singletonList(wc), null);
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateWriterTest.java 
b/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateWriterTest.java
index 12d5f9eaa02..d99097d8573 100644
--- a/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateWriterTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/overseer/ZkStateWriterTest.java
@@ -178,13 +178,13 @@ public class ZkStateWriterTest extends SolrTestCaseJ4 {
         ZkWriteCommand prs1 =
             new ZkWriteCommand(
                 "prs1",
-                new DocCollection(
+                DocCollection.create(
                     "prs1",
                     new HashMap<>(),
                     prsProps,
                     DocRouter.DEFAULT,
                     0,
-                    new PerReplicaStatesFetcher.LazyPrsSupplier(
+                    PerReplicaStatesFetcher.getZkClientPrsSupplier(
                         zkClient, DocCollection.getCollectionPath("c1"))));
         ZkStateWriter writer =
             new ZkStateWriter(reader, new Stats(), -1, 
STATE_COMPRESSION_PROVIDER);
@@ -247,30 +247,30 @@ public class ZkStateWriterTest extends SolrTestCaseJ4 {
         ZkWriteCommand c1 =
             new ZkWriteCommand(
                 "c1",
-                new DocCollection(
+                DocCollection.create(
                     "c1", new HashMap<>(), new HashMap<>(), DocRouter.DEFAULT, 
0, null));
         ZkWriteCommand c2 =
             new ZkWriteCommand(
                 "c2",
-                new DocCollection(
+                DocCollection.create(
                     "c2", new HashMap<>(), new HashMap<>(), DocRouter.DEFAULT, 
0, null));
         ZkWriteCommand c3 =
             new ZkWriteCommand(
                 "c3",
-                new DocCollection(
+                DocCollection.create(
                     "c3", new HashMap<>(), new HashMap<>(), DocRouter.DEFAULT, 
0, null));
         Map<String, Object> prsProps = new HashMap<>();
         prsProps.put("perReplicaState", Boolean.TRUE);
         ZkWriteCommand prs1 =
             new ZkWriteCommand(
                 "prs1",
-                new DocCollection(
+                DocCollection.create(
                     "prs1",
                     new HashMap<>(),
                     prsProps,
                     DocRouter.DEFAULT,
                     0,
-                    new PerReplicaStatesFetcher.LazyPrsSupplier(
+                    PerReplicaStatesFetcher.getZkClientPrsSupplier(
                         zkClient, DocCollection.getCollectionPath("prs1"))));
         ZkStateWriter writer =
             new ZkStateWriter(reader, new Stats(), -1, 
STATE_COMPRESSION_PROVIDER);
diff --git 
a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
 
b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
index 9374230ac2b..3ec636de1a8 100644
--- 
a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
+++ 
b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
@@ -125,14 +125,13 @@ public interface DistribStateManager extends 
SolrCloseable {
   }
 
   default DocCollection.PrsSupplier getPrsSupplier(String collName) {
-    return new DocCollection.PrsSupplier(
-        () -> {
-          try {
-            return getReplicaStates(DocCollection.getCollectionPath(collName));
-          } catch (Exception e) {
-            throw new RuntimeException(e);
-          }
-        });
+    return () -> {
+      try {
+        return getReplicaStates(DocCollection.getCollectionPath(collName));
+      } catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    };
   }
 
   /**
diff --git 
a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
 
b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
index 98c89b1ee0d..d6005da2a3f 100644
--- 
a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
+++ 
b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
@@ -118,7 +118,7 @@ public class ZkClientClusterStateProvider implements 
ClusterStateProvider {
         version,
         stateMap,
         liveNodes,
-        new PerReplicaStatesFetcher.LazyPrsSupplier(
+        PerReplicaStatesFetcher.getZkClientPrsSupplier(
             zkClient, DocCollection.getCollectionPath(coll)));
   }
 
diff --git 
a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/PerReplicaStatesFetcher.java
 
b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/PerReplicaStatesFetcher.java
index c8baa8b4f07..1de41b93b93 100644
--- 
a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/PerReplicaStatesFetcher.java
+++ 
b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/PerReplicaStatesFetcher.java
@@ -55,9 +55,8 @@ public class PerReplicaStatesFetcher {
     }
   }
 
-  public static class LazyPrsSupplier extends DocCollection.PrsSupplier {
-    public LazyPrsSupplier(SolrZkClient zkClient, String collectionPath) {
-      super(() -> PerReplicaStatesFetcher.fetch(collectionPath, zkClient, 
null));
-    }
+  public static DocCollection.PrsSupplier getZkClientPrsSupplier(
+      SolrZkClient zkClient, String collectionPath) {
+    return () -> PerReplicaStatesFetcher.fetch(collectionPath, zkClient, null);
   }
 }
diff --git 
a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java 
b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java
index 822d713096f..0074ab74762 100644
--- 
a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java
+++ 
b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java
@@ -1395,7 +1395,9 @@ public class ZkStateReader implements SolrCloseable {
             new PerReplicaStates(collectionPath, stat.getCversion(), 
replicaStates);
         DocCollection oldState = collectionWatches.getDocCollection(coll);
         final DocCollection newState =
-            oldState != null ? oldState.copyWith(newStates) : 
fetchCollectionState(coll, null);
+            oldState != null
+                ? oldState.setPerReplicaStates(newStates)
+                : fetchCollectionState(coll, null);
         collectionWatches.updateDocCollection(coll, newState);
         synchronized (getUpdateLock()) {
           constructState(Collections.singleton(coll));
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java
index 2c6e2e9134a..375b3c38742 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java
@@ -172,13 +172,13 @@ public abstract class BaseHttpClusterStateProvider 
implements ClusterStateProvid
     if (m.containsKey("PRS")) {
       Map prs = (Map) m.remove("PRS");
       prsSupplier =
-          new DocCollection.PrsSupplier(
-              () ->
-                  new PerReplicaStates(
-                      (String) prs.get("path"),
-                      (Integer) prs.get("cversion"),
-                      (List<String>) prs.get("states")));
+          () ->
+              new PerReplicaStates(
+                  (String) prs.get("path"),
+                  (Integer) prs.get("cversion"),
+                  (List<String>) prs.get("states"));
     }
+
     return ClusterState.collectionFromObjects(e.getKey(), m, znodeVersion, 
prsSupplier);
   }
 
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/ClusterState.java 
b/solr/solrj/src/java/org/apache/solr/common/cloud/ClusterState.java
index 784acecd2ab..6d74dd49015 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/ClusterState.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/ClusterState.java
@@ -302,7 +302,7 @@ public class ClusterState implements JSONWriter.Writable {
       router = DocRouter.getDocRouter((String) routerProps.get("name"));
     }
 
-    return new DocCollection(name, slices, props, router, version, 
prsSupplier);
+    return DocCollection.create(name, slices, props, router, version, 
prsSupplier);
   }
 
   @Override
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 55068c70aac..40b41ba2171 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
@@ -29,6 +29,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiConsumer;
 import java.util.function.BiPredicate;
 import java.util.function.Supplier;
@@ -65,14 +66,20 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
   private final Boolean readOnly;
   private final Boolean perReplicaState;
   private final Map<String, Replica> replicaMap = new HashMap<>();
-  private PrsSupplier prsSupplier;
+  private AtomicReference<PerReplicaStates> perReplicaStatesRef;
 
+  /**
+   * @see DocCollection#create(String, Map, Map, DocRouter, int, PrsSupplier)
+   */
   @Deprecated
   public DocCollection(
       String name, Map<String, Slice> slices, Map<String, Object> props, 
DocRouter router) {
     this(name, slices, props, router, Integer.MAX_VALUE, null);
   }
 
+  /**
+   * @see DocCollection#create(String, Map, Map, DocRouter, int, PrsSupplier)
+   */
   @Deprecated
   public DocCollection(
       String name,
@@ -89,14 +96,15 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
    * @param props The properties of the slice. This is used directly and a 
copy is not made.
    * @param zkVersion The version of the Collection node in Zookeeper (used 
for conditional
    *     updates).
+   * @see DocCollection#create(String, Map, Map, DocRouter, int, PrsSupplier)
    */
-  public DocCollection(
+  private DocCollection(
       String name,
       Map<String, Slice> slices,
       Map<String, Object> props,
       DocRouter router,
       int zkVersion,
-      PrsSupplier prsSupplier) {
+      AtomicReference<PerReplicaStates> perReplicaStatesRef) {
     super(props);
     // -1 means any version in ZK CAS, so we choose Integer.MAX_VALUE instead 
to avoid accidental
     // overwrites
@@ -114,14 +122,14 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
     this.perReplicaState =
         (Boolean) verifyProp(props, CollectionStateProps.PER_REPLICA_STATE, 
Boolean.FALSE);
     if (this.perReplicaState) {
-      if (prsSupplier == null) {
+      if (perReplicaStatesRef == null || perReplicaStatesRef.get() == null) {
         throw new RuntimeException(
             CollectionStateProps.PER_REPLICA_STATE
-                + " = true , but per-replica state supplier is not provided");
+                + " = true , but perReplicatStates param is not provided");
       }
-      this.prsSupplier = prsSupplier;
-      for (Slice s : this.slices.values()) {
-        s.setPrsSupplier(prsSupplier);
+      this.perReplicaStatesRef = perReplicaStatesRef;
+      for (Slice s : this.slices.values()) { // set the same reference to all 
slices too
+        s.setPerReplicaStatesRef(this.perReplicaStatesRef);
       }
     }
     Boolean readOnly = (Boolean) verifyProp(props, 
CollectionStateProps.READ_ONLY);
@@ -147,6 +155,64 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
     assert name != null && slices != null;
   }
 
+  /**
+   * Builds a DocCollection with an optional PrsSupplier
+   *
+   * @param name The name of the collection
+   * @param slices The logical shards of the collection. This is used directly 
and a copy is not
+   *     made.
+   * @param props The properties of the slice. This is used directly and a 
copy is not made.
+   * @param router router to partition int range into n ranges
+   * @param zkVersion The version of the Collection node in Zookeeper (used 
for conditional
+   *     updates).
+   * @param prsSupplier optional supplier for PerReplicaStates (PRS) for PRS 
enabled collections
+   * @return a newly constructed DocCollection
+   */
+  public static DocCollection create(
+      String name,
+      Map<String, Slice> slices,
+      Map<String, Object> props,
+      DocRouter router,
+      int zkVersion,
+      DocCollection.PrsSupplier prsSupplier) {
+    boolean perReplicaState =
+        (Boolean) verifyProp(props, CollectionStateProps.PER_REPLICA_STATE, 
Boolean.FALSE);
+    PerReplicaStates perReplicaStates;
+    if (perReplicaState) {
+      if (prsSupplier == null) {
+        throw new IllegalArgumentException(
+            CollectionStateProps.PER_REPLICA_STATE + " = true , but prsSuppler 
is not provided");
+      }
+
+      if (!hasAnyReplica(
+          slices)) { // a special case, if there is no replica, it should not 
fetch (first PRS
+        // collection creation with no replicas). Otherwise, it would trigger 
exception
+        // on fetching a state.json that does not exist yet
+        perReplicaStates = PerReplicaStates.empty(name);
+      } else {
+        perReplicaStates = prsSupplier.get();
+      }
+    } else {
+      perReplicaStates = null;
+    }
+    return new DocCollection(
+        name,
+        slices,
+        props,
+        router,
+        zkVersion,
+        perReplicaStates != null ? new AtomicReference<>(perReplicaStates) : 
null);
+  }
+
+  private static boolean hasAnyReplica(Map<String, Slice> slices) {
+    for (Slice slice : slices.values()) {
+      if (!slice.getReplicasMap().isEmpty()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   public static String getCollectionPath(String coll) {
     return getCollectionPathRoot(coll) + "/state.json";
   }
@@ -156,14 +222,20 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
   }
 
   /**
-   * Update our state with a state of a {@link Replica} Used to create a new 
Collection State when
-   * only a replica is updated
+   * Update our state with a state of a {@link PerReplicaStates} which could 
override states of
+   * {@link Replica}.
+   *
+   * <p>Take note that it updates the underlying AtomicReference such that all 
Slice and Replica
+   * that holds the same AtomicReference will see the same update
+   *
+   * <p>This does not create a new DocCollection.
    */
-  public DocCollection copyWith(PerReplicaStates newPerReplicaStates) {
-    if (this.prsSupplier != null) {
+  public final DocCollection setPerReplicaStates(PerReplicaStates 
newPerReplicaStates) {
+    if (this.perReplicaStatesRef != null) {
       log.debug("In-place update of PRS: {}", newPerReplicaStates);
-      this.prsSupplier.prs = newPerReplicaStates;
+      this.perReplicaStatesRef.set(newPerReplicaStates);
     }
+
     return this;
   }
 
@@ -216,7 +288,7 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
    */
   public DocCollection copyWithSlices(Map<String, Slice> slices) {
     DocCollection result =
-        new DocCollection(getName(), slices, propMap, router, znodeVersion, 
prsSupplier);
+        new DocCollection(getName(), slices, propMap, router, znodeVersion, 
perReplicaStatesRef);
     return result;
   }
   /** Return collection name. */
@@ -283,8 +355,7 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
   }
 
   public int getChildNodesVersion() {
-    PerReplicaStates prs = prsSupplier == null ? null : prsSupplier.get();
-    return prs == null ? 0 : prs.cversion;
+    return perReplicaStatesRef == null ? 0 : 
perReplicaStatesRef.get().cversion;
   }
 
   public boolean isModified(int dataVersion, int childVersion) {
@@ -321,7 +392,7 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
         + "/"
         + znodeVersion
         + " "
-        + (prsSupplier == null ? "" : prsSupplier.get())
+        + (perReplicaStatesRef == null ? "" : perReplicaStatesRef.get())
         + ")="
         + toJSONString(this);
   }
@@ -468,11 +539,7 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
   }
 
   public PerReplicaStates getPerReplicaStates() {
-    return prsSupplier != null ? prsSupplier.get() : null;
-  }
-
-  public PrsSupplier getPrsSupplier() {
-    return prsSupplier;
+    return perReplicaStatesRef != null ? perReplicaStatesRef.get() : null;
   }
 
   public int getExpectedReplicaCount(Replica.Type type, int def) {
@@ -496,26 +563,5 @@ public class DocCollection extends ZkNodeProps implements 
Iterable<Slice> {
     String PER_REPLICA_STATE = "perReplicaState";
   }
 
-  public static class PrsSupplier implements Supplier<PerReplicaStates> {
-
-    private volatile PerReplicaStates prs;
-
-    private Supplier<PerReplicaStates> supplier;
-
-    public PrsSupplier(Supplier<PerReplicaStates> supplier) {
-      this.supplier = supplier;
-    }
-
-    public PrsSupplier(PerReplicaStates prs) {
-      this.prs = prs;
-    }
-
-    @Override
-    public PerReplicaStates get() {
-      if (prs == null) {
-        prs = supplier.get();
-      }
-      return prs;
-    }
-  }
+  public interface PrsSupplier extends Supplier<PerReplicaStates> {}
 }
diff --git 
a/solr/solrj/src/java/org/apache/solr/common/cloud/PerReplicaStates.java 
b/solr/solrj/src/java/org/apache/solr/common/cloud/PerReplicaStates.java
index 698da842845..60616d8fd70 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/PerReplicaStates.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/PerReplicaStates.java
@@ -88,6 +88,10 @@ public class PerReplicaStates implements ReflectMapWriter {
     this.states = new WrappedSimpleMap<>(tmp);
   }
 
+  public static PerReplicaStates empty(String collectionName) {
+    return new 
PerReplicaStates(DocCollection.getCollectionPath(collectionName), 0, List.of());
+  }
+
   /** Check and return if all replicas are ACTIVE */
   public boolean allActive() {
     if (this.allActive != null) return allActive;
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/Replica.java 
b/solr/solrj/src/java/org/apache/solr/common/cloud/Replica.java
index 067bd408c03..b54e29f4d6e 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/Replica.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/Replica.java
@@ -24,6 +24,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 import org.apache.solr.common.MapWriter;
 import org.apache.solr.common.util.Utils;
 import org.noggit.JSONWriter;
@@ -145,13 +146,13 @@ public class Replica extends ZkNodeProps implements 
MapWriter {
   public final String core;
   public final Type type;
   public final String shard, collection;
-  private DocCollection.PrsSupplier prsSupplier;
+  private AtomicReference<PerReplicaStates> perReplicaStatesRef;
 
   // mutable
   private State state;
 
-  void setPrsSupplier(DocCollection.PrsSupplier prsSupplier) {
-    this.prsSupplier = prsSupplier;
+  void setPerReplicaStatesRef(AtomicReference<PerReplicaStates> 
perReplicaStatesRef) {
+    this.perReplicaStatesRef = perReplicaStatesRef;
   }
 
   public Replica(String name, Map<String, Object> map, String collection, 
String shard) {
@@ -293,8 +294,8 @@ public class Replica extends ZkNodeProps implements 
MapWriter {
 
   /** Returns the {@link State} of this replica. */
   public State getState() {
-    if (prsSupplier != null) {
-      PerReplicaStates.State s = prsSupplier.get().get(name);
+    if (perReplicaStatesRef != null) {
+      PerReplicaStates.State s = perReplicaStatesRef.get().get(name);
       if (s != null) {
         return s.state;
       } else {
@@ -318,8 +319,8 @@ public class Replica extends ZkNodeProps implements 
MapWriter {
   }
 
   public boolean isLeader() {
-    if (prsSupplier != null) {
-      PerReplicaStates.State st = prsSupplier.get().get(name);
+    if (perReplicaStatesRef != null) {
+      PerReplicaStates.State st = perReplicaStatesRef.get().get(name);
       return st == null ? false : st.isLeader;
     }
     return getBool(ReplicaStateProps.LEADER, false);
@@ -360,8 +361,8 @@ public class Replica extends ZkNodeProps implements 
MapWriter {
   }
 
   public PerReplicaStates.State getReplicaState() {
-    if (prsSupplier != null) {
-      return prsSupplier.get().get(name);
+    if (perReplicaStatesRef != null) {
+      return perReplicaStatesRef.get().get(name);
     }
     return null;
   }
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/Slice.java 
b/solr/solrj/src/java/org/apache/solr/common/cloud/Slice.java
index 197d10199cc..0007c679690 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/Slice.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/Slice.java
@@ -29,6 +29,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import org.apache.solr.common.cloud.Replica.Type;
@@ -46,12 +47,12 @@ public class Slice extends ZkNodeProps implements 
Iterable<Replica> {
 
   public final String collection;
 
-  private DocCollection.PrsSupplier prsSupplier;
+  private AtomicReference<PerReplicaStates> perReplicaStatesRef;
 
-  void setPrsSupplier(DocCollection.PrsSupplier prsSupplier) {
-    this.prsSupplier = prsSupplier;
+  void setPerReplicaStatesRef(AtomicReference<PerReplicaStates> 
perReplicaStatesRef) {
+    this.perReplicaStatesRef = perReplicaStatesRef;
     for (Replica r : replicas.values()) {
-      r.setPrsSupplier(prsSupplier);
+      r.setPerReplicaStatesRef(perReplicaStatesRef);
     }
     if (leader == null) {
       leader = findLeader();
@@ -286,7 +287,7 @@ public class Slice extends ZkNodeProps implements 
Iterable<Replica> {
   }
 
   public Replica getLeader() {
-    if (prsSupplier != null) {
+    if (perReplicaStatesRef != null) {
       // this  is a PRS collection. leader may keep changing
       return findLeader();
     } else {


Reply via email to