You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sh...@apache.org on 2020/02/08 06:29:42 UTC

[lucene-solr] branch master updated: SOLR-14248: Improve ClusterStateMockUtil and make its methods public

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

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


The following commit(s) were added to refs/heads/master by this push:
     new f5c132b  SOLR-14248: Improve ClusterStateMockUtil and make its methods public
f5c132b is described below

commit f5c132be6d3fc20f689e630517e7c6be2166f17e
Author: Shalin Shekhar Mangar <sh...@apache.org>
AuthorDate: Sat Feb 8 11:59:27 2020 +0530

    SOLR-14248: Improve ClusterStateMockUtil and make its methods public
---
 solr/CHANGES.txt                                   |   2 +
 .../apache/solr/cloud/ClusterStateMockUtil.java    | 121 +++++++++++++--------
 .../solr/cloud/ClusterStateMockUtilTest.java       |  99 +++++++++++++++++
 3 files changed, 176 insertions(+), 46 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index e660350..7719ad3 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -297,6 +297,8 @@ Other Changes
   TestInjection.skipIndexWriterCommitOnClose.  Users that modified DUH2.commitOnClose in test cases for custom
   plugins/modicitations should now use TestInjection instead.  (hossman)
 
+* SOLR-14248: Improve ClusterStateMockUtil and make its methods public. (shalin)
+
 ==================  8.4.1 ==================
 
 Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release.
diff --git a/solr/core/src/test/org/apache/solr/cloud/ClusterStateMockUtil.java b/solr/core/src/test/org/apache/solr/cloud/ClusterStateMockUtil.java
index 63ee8ec..90df39a 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ClusterStateMockUtil.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ClusterStateMockUtil.java
@@ -27,27 +27,35 @@ import java.util.regex.Pattern;
 
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.DocRouter;
 import org.apache.solr.common.cloud.Replica;
 import org.apache.solr.common.cloud.Slice;
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.util.Utils;
 
+/**
+ * A utility class that can create mock ZkStateReader objects with custom ClusterState objects created
+ * using a simple string based description. See {@link #buildClusterState(String, int, int, String...)} for
+ * details on how the cluster state can be created.
+ *
+ * @lucene.experimental
+ */
 public class ClusterStateMockUtil {
 
   private final static Pattern BLUEPRINT = Pattern.compile("([a-z])(\\d+)?(?:(['A','R','D','F']))?(\\*)?");
 
-  protected static ZkStateReader buildClusterState(String string, String ... liveNodes) {
-    return buildClusterState(string, 1, liveNodes);
+  public static ZkStateReader buildClusterState(String clusterDescription, String ... liveNodes) {
+    return buildClusterState(clusterDescription, 1, liveNodes);
   }
 
-  protected static ZkStateReader buildClusterState(String string, int replicationFactor, String ... liveNodes) {
-    return buildClusterState(string, replicationFactor, 10, liveNodes);
+  public static ZkStateReader buildClusterState(String clusterDescription, int replicationFactor, String ... liveNodes) {
+    return buildClusterState(clusterDescription, replicationFactor, 10, liveNodes);
   }
 
   /**
    * This method lets you construct a complex ClusterState object by using simple strings of letters.
    *
-   * c = collection, s = slice, r = replica, \d = node number (r2 means the replica is on node 2),
+   * c = collection, s = slice, r = replica (nrt type, default), n = nrt replica, t = tlog replica, p = pull replica, \d = node number (r2 means the replica is on node 2),
    * state = [A,R,D,F], * = replica to replace, binds to the left.
    *
    * For example:
@@ -104,7 +112,7 @@ public class ClusterStateMockUtil {
    *
    */
   @SuppressWarnings("resource")
-  protected static ZkStateReader buildClusterState(String clusterDescription, int replicationFactor, int maxShardsPerNode, String ... liveNodes) {
+  public static ZkStateReader buildClusterState(String clusterDescription, int replicationFactor, int maxShardsPerNode, String ... liveNodes) {
     Map<String,Slice> slices = null;
     Map<String,Replica> replicas = null;
     Map<String,Object> collectionProps = new HashMap<>();
@@ -123,7 +131,7 @@ public class ClusterStateMockUtil {
       switch (m.group(1)) {
         case "c":
           slices = new HashMap<>();
-          docCollection = new DocCollection(collName = "collection" + (collectionStates.size() + 1), slices, collectionProps, null);
+          docCollection = new DocCollection(collName = "collection" + (collectionStates.size() + 1), slices, collectionProps, DocRouter.DEFAULT);
           collectionStates.put(docCollection.getName(), docCollection);
           break;
         case "s":
@@ -131,49 +139,25 @@ public class ClusterStateMockUtil {
           if(collName == null) collName = "collection" + (collectionStates.size() + 1);
           slice = new Slice(sliceName = "slice" + (slices.size() + 1), replicas, null,  collName);
           slices.put(slice.getName(), slice);
+
+          // hack alert: the DocCollection constructor copies over active slices to its active slice map in the constructor
+          // but here we construct the DocCollection before creating the slices which breaks code that calls DocCollection.getActiveSlices
+          // so here we re-create doc collection with the latest slices map to workaround this problem
+          // todo: a better fix would be to have a builder class for DocCollection that builds the final object once all the slices and replicas have been created.
+          docCollection = docCollection.copyWithSlices(slices);
+          collectionStates.put(docCollection.getName(), docCollection);
           break;
         case "r":
-          Map<String,Object> replicaPropMap = new HashMap<>();
-          String node;
-
-          node = m.group(2);
-
-          if (node == null || node.trim().length() == 0) {
-            node = "1";
-          }
-
-          Replica.State state = Replica.State.ACTIVE;
-          String stateCode = m.group(3);
-
-          if (stateCode != null) {
-            switch (stateCode.charAt(0)) {
-              case 'S':
-                state = Replica.State.ACTIVE;
-                break;
-              case 'R':
-                state = Replica.State.RECOVERING;
-                break;
-              case 'D':
-                state = Replica.State.DOWN;
-                break;
-              case 'F':
-                state = Replica.State.RECOVERY_FAILED;
-                break;
-              default:
-                throw new IllegalArgumentException(
-                    "Unexpected state for replica: " + stateCode);
-            }
-          }
-
-          String nodeName = "baseUrl" + node + "_";
+        case "n":
+        case "t":
+        case "p":
+          String node = m.group(2);
           String replicaName = "replica" + replicaCount++;
+          String stateCode = m.group(3);
 
-          replicaPropMap.put(ZkStateReader.NODE_NAME_PROP, nodeName);
-          replicaPropMap.put(ZkStateReader.BASE_URL_PROP, "http://baseUrl" + node);
-          replicaPropMap.put(ZkStateReader.STATE_PROP, state.toString());
-          replicaPropMap.put(ZkStateReader.CORE_NAME_PROP, "core_" + replicaName);
-          if(collName == null) collName = "collection" + (collectionStates.size() + 1);
-          if(sliceName == null) collName = "slice" + (slices.size() + 1);
+          Map<String, Object> replicaPropMap = makeReplicaProps(sliceName, node, replicaName, stateCode, m.group(1));
+          if (collName == null) collName = "collection" + (collectionStates.size() + 1);
+          if (sliceName == null) collName = "slice" + (slices.size() + 1);
           replica = new Replica(replicaName, replicaPropMap, collName, sliceName);
 
           replicas.put(replica.getName(), replica);
@@ -193,5 +177,50 @@ public class ClusterStateMockUtil {
     return reader;
   }
 
+  private static Map<String, Object> makeReplicaProps(String sliceName, String node, String replicaName, String stateCode, String replicaTypeCode) {
+    if (node == null || node.trim().length() == 0) {
+      node = "1";
+    }
+
+    Replica.State state = Replica.State.ACTIVE;
+    if (stateCode != null) {
+      switch (stateCode.charAt(0)) {
+        case 'S':
+          state = Replica.State.ACTIVE;
+          break;
+        case 'R':
+          state = Replica.State.RECOVERING;
+          break;
+        case 'D':
+          state = Replica.State.DOWN;
+          break;
+        case 'F':
+          state = Replica.State.RECOVERY_FAILED;
+          break;
+        default:
+          throw new IllegalArgumentException(
+              "Unexpected state for replica: " + stateCode);
+      }
+    }
+
+    Replica.Type replicaType = Replica.Type.NRT;
+    switch (replicaTypeCode)  {
+      case "t":
+        replicaType = Replica.Type.TLOG;
+        break;
+      case "p":
+        replicaType = Replica.Type.PULL;
+        break;
+    }
+
+    Map<String,Object> replicaPropMap = new HashMap<>();
+    replicaPropMap.put(ZkStateReader.NODE_NAME_PROP, "baseUrl" + node + "_");
+    replicaPropMap.put(ZkStateReader.BASE_URL_PROP, "http://baseUrl" + node);
+    replicaPropMap.put(ZkStateReader.STATE_PROP, state.toString());
+    replicaPropMap.put(ZkStateReader.CORE_NAME_PROP, sliceName + "_" + replicaName);
+    replicaPropMap.put(ZkStateReader.REPLICA_TYPE, replicaType.name());
+    return replicaPropMap;
+  }
+
 
 }
diff --git a/solr/core/src/test/org/apache/solr/cloud/ClusterStateMockUtilTest.java b/solr/core/src/test/org/apache/solr/cloud/ClusterStateMockUtilTest.java
new file mode 100644
index 0000000..89e9007
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/ClusterStateMockUtilTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.cloud;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.DocRouter;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.junit.Test;
+
+/**
+ * Tests for {@link ClusterStateMockUtil}
+ */
+public class ClusterStateMockUtilTest extends SolrTestCaseJ4 {
+
+  @Test
+  public void testBuildClusterState_Simple() {
+    try (ZkStateReader zkStateReader = ClusterStateMockUtil.buildClusterState("csr", "baseUrl1_")) {
+      ClusterState clusterState = zkStateReader.getClusterState();
+      assertNotNull(clusterState);
+      assertEquals(1, clusterState.getCollectionStates().size());
+      DocCollection collection1 = clusterState.getCollectionOrNull("collection1");
+      assertNotNull(collection1);
+      assertEquals(DocRouter.DEFAULT, collection1.getRouter());
+      assertEquals(1, collection1.getActiveSlices().size());
+      assertEquals(1, collection1.getSlices().size());
+      Slice slice1 = collection1.getSlice("slice1");
+      assertNotNull(slice1);
+      assertEquals(1, slice1.getReplicas().size());
+      Replica replica1 = slice1.getReplica("replica1");
+      assertNotNull(replica1);
+      assertEquals("baseUrl1_", replica1.getNodeName());
+      assertEquals("slice1_replica1", replica1.getCoreName());
+      assertEquals("http://baseUrl1", replica1.getBaseUrl());
+      assertEquals("http://baseUrl1/slice1_replica1/", replica1.getCoreUrl());
+      assertEquals(Replica.State.ACTIVE, replica1.getState());
+      assertEquals(Replica.Type.NRT, replica1.getType());
+    }
+  }
+
+  @Test
+  public void testBuildClusterState_ReplicaTypes() {
+    try (ZkStateReader zkStateReader = ClusterStateMockUtil.buildClusterState("csntp", "baseUrl1_")) {
+      ClusterState clusterState = zkStateReader.getClusterState();
+      assertNotNull(clusterState);
+      assertEquals(1, clusterState.getCollectionStates().size());
+      DocCollection collection1 = clusterState.getCollectionOrNull("collection1");
+      assertNotNull(collection1);
+      assertEquals(DocRouter.DEFAULT, collection1.getRouter());
+      assertEquals(1, collection1.getActiveSlices().size());
+      assertEquals(1, collection1.getSlices().size());
+      Slice slice1 = collection1.getSlice("slice1");
+      assertNotNull(slice1);
+      assertEquals(3, slice1.getReplicas().size());
+      assertEquals(1, slice1.getReplicas(replica -> replica.getType() == Replica.Type.NRT).size());
+      assertEquals(1, slice1.getReplicas(replica -> replica.getType() == Replica.Type.TLOG).size());
+      assertEquals(1, slice1.getReplicas(replica -> replica.getType() == Replica.Type.PULL).size());
+    }
+  }
+
+  @Test
+  public void testBuildClusterState_ReplicaStateAndType() {
+    try (ZkStateReader zkStateReader = ClusterStateMockUtil.buildClusterState("csrStRpDnF", "baseUrl1_")) {
+      ClusterState clusterState = zkStateReader.getClusterState();
+      assertNotNull(clusterState);
+      assertEquals(1, clusterState.getCollectionStates().size());
+      DocCollection collection1 = clusterState.getCollectionOrNull("collection1");
+      assertNotNull(collection1);
+      assertEquals(DocRouter.DEFAULT, collection1.getRouter());
+      assertEquals(1, collection1.getActiveSlices().size());
+      assertEquals(1, collection1.getSlices().size());
+      Slice slice1 = collection1.getSlice("slice1");
+      assertNotNull(slice1);
+      assertEquals(4, slice1.getReplicas().size());
+      assertEquals(1, slice1.getReplicas(replica -> replica.getType() == Replica.Type.NRT && replica.getState() == Replica.State.ACTIVE).size());
+      assertEquals(1, slice1.getReplicas(replica -> replica.getType() == Replica.Type.NRT && replica.getState() == Replica.State.RECOVERY_FAILED).size());
+      assertEquals(1, slice1.getReplicas(replica -> replica.getType() == Replica.Type.TLOG && replica.getState() == Replica.State.RECOVERING).size());
+      assertEquals(1, slice1.getReplicas(replica -> replica.getType() == Replica.Type.PULL && replica.getState() == Replica.State.DOWN).size());
+    }
+  }
+}
\ No newline at end of file