You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ma...@apache.org on 2014/12/23 20:34:16 UTC

svn commit: r1647656 - in /lucene/dev/branches/branch_5x: ./ solr/ solr/core/ solr/core/src/java/org/apache/solr/cloud/ solr/core/src/java/org/apache/solr/handler/admin/ solr/core/src/test/org/apache/solr/cloud/

Author: markrmiller
Date: Tue Dec 23 19:34:15 2014
New Revision: 1647656

URL: http://svn.apache.org/r1647656
Log:
SOLR-6729: createNodeSet.shuffle=(true|false) support for /admin/collections?action=CREATE.

Modified:
    lucene/dev/branches/branch_5x/   (props changed)
    lucene/dev/branches/branch_5x/solr/   (props changed)
    lucene/dev/branches/branch_5x/solr/CHANGES.txt   (contents, props changed)
    lucene/dev/branches/branch_5x/solr/core/   (props changed)
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionProcessor.java
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
    lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/cloud/OverseerCollectionProcessorTest.java

Modified: lucene/dev/branches/branch_5x/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/CHANGES.txt?rev=1647656&r1=1647655&r2=1647656&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/CHANGES.txt (original)
+++ lucene/dev/branches/branch_5x/solr/CHANGES.txt Tue Dec 23 19:34:15 2014
@@ -210,6 +210,9 @@ New Features
 
 * SOLR-6801:  Load RequestHandler from blob store (Noble Paul)
 
+* SOLR-6729: createNodeSet.shuffle=(true|false) support for /admin/collections?action=CREATE.
+  (Christine Poerschke, Ramkumar Aiyengar via Mark Miller)
+  
 Bug Fixes
 ----------------------
 

Modified: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionProcessor.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionProcessor.java?rev=1647656&r1=1647655&r2=1647656&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionProcessor.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionProcessor.java Tue Dec 23 19:34:15 2014
@@ -123,6 +123,8 @@ public class OverseerCollectionProcessor
   // @Deprecated- see on ZkStateReader
   public static final String MAX_SHARDS_PER_NODE = "maxShardsPerNode";
   
+  static final boolean CREATE_NODE_SET_SHUFFLE_DEFAULT = true;
+  public static final String CREATE_NODE_SET_SHUFFLE = "createNodeSet.shuffle";
   public static final String CREATE_NODE_SET = "createNodeSet";
 
   /**
@@ -171,7 +173,7 @@ public class OverseerCollectionProcessor
       ZkStateReader.MAX_SHARDS_PER_NODE, "1",
       ZkStateReader.AUTO_ADD_REPLICAS, "false");
 
-  private static final Random RANDOM;
+  static final Random RANDOM;
   static {
     // We try to make things reproducible in the context of our tests by initializing the random instance
     // based on the current seed
@@ -2297,6 +2299,30 @@ public class OverseerCollectionProcessor
     }
   }
 
+  private static List<String> getLiveOrLiveAndCreateNodeSetList(final Set<String> liveNodes, final ZkNodeProps message, final Random random) {
+    // TODO: add smarter options that look at the current number of cores per
+    // node?
+    // for now we just go random (except when createNodeSet and createNodeSet.shuffle=false are passed in)
+
+    List<String> nodeList;
+
+    final String createNodeSetStr = message.getStr(CREATE_NODE_SET);
+    final List<String> createNodeList = (createNodeSetStr == null)?null:StrUtils.splitSmart(createNodeSetStr, ",", true);
+
+    if (createNodeList != null) {
+      nodeList = new ArrayList<>(createNodeList);
+      nodeList.retainAll(liveNodes);
+      if (message.getBool(CREATE_NODE_SET_SHUFFLE, CREATE_NODE_SET_SHUFFLE_DEFAULT)) {
+        Collections.shuffle(nodeList, random);
+      }
+    } else {
+      nodeList = new ArrayList<>(liveNodes);
+      Collections.shuffle(nodeList, random);
+    }
+    
+    return nodeList;    
+  }
+  
   private void createCollection(ClusterState clusterState, ZkNodeProps message, NamedList results) throws KeeperException, InterruptedException {
     String collectionName = message.getStr("name");
     if (clusterState.hasCollection(collectionName)) {
@@ -2328,8 +2354,6 @@ public class OverseerCollectionProcessor
       }
 
       int maxShardsPerNode = message.getInt(ZkStateReader.MAX_SHARDS_PER_NODE, 1);
-      String createNodeSetStr; 
-      List<String> createNodeList = ((createNodeSetStr = message.getStr(CREATE_NODE_SET)) == null)?null:StrUtils.splitSmart(createNodeSetStr, ",", true);
       
       if (repFactor <= 0) {
         throw new SolrException(ErrorCode.BAD_REQUEST, ZkStateReader.REPLICATION_FACTOR + " must be greater than 0");
@@ -2343,19 +2367,7 @@ public class OverseerCollectionProcessor
       // add our new cores to existing nodes serving the least number of cores
       // but (for now) require that each core goes on a distinct node.
       
-      // TODO: add smarter options that look at the current number of cores per
-      // node?
-      // for now we just go random
-      Set<String> nodes = clusterState.getLiveNodes();
-      List<String> nodeList = new ArrayList<>(nodes.size());
-      nodeList.addAll(nodes);
-      if (createNodeList != null) nodeList.retainAll(createNodeList);
-      Collections.shuffle(nodeList, RANDOM);
-      
-      if (nodeList.size() <= 0) {
-        throw new SolrException(ErrorCode.BAD_REQUEST, "Cannot create collection " + collectionName
-            + ". No live Solr-instances" + ((createNodeList != null)?" among Solr-instances specified in " + CREATE_NODE_SET + ":" + createNodeSetStr:""));
-      }
+      final List<String> nodeList = getLiveOrLiveAndCreateNodeSetList(clusterState.getLiveNodes(), message, RANDOM);
       
       if (repFactor > nodeList.size()) {
         log.warn("Specified "
@@ -2364,7 +2376,7 @@ public class OverseerCollectionProcessor
             + repFactor
             + " on collection "
             + collectionName
-            + " is higher than or equal to the number of Solr instances currently live or part of your " + CREATE_NODE_SET + "("
+            + " is higher than or equal to the number of Solr instances currently live or live and part of your " + CREATE_NODE_SET + "("
             + nodeList.size()
             + "). Its unusual to run two replica of the same slice on the same Solr-instance.");
       }
@@ -2374,7 +2386,7 @@ public class OverseerCollectionProcessor
       if (maxShardsAllowedToCreate < requestedShardsToCreate) {
         throw new SolrException(ErrorCode.BAD_REQUEST, "Cannot create collection " + collectionName + ". Value of "
             + ZkStateReader.MAX_SHARDS_PER_NODE + " is " + maxShardsPerNode
-            + ", and the number of live nodes is " + nodeList.size()
+            + ", and the number of nodes currently live or live and part of your "+CREATE_NODE_SET+" is " + nodeList.size()
             + ". This allows a maximum of " + maxShardsAllowedToCreate
             + " to be created. Value of " + NUM_SLICES + " is " + numSlices
             + " and value of " + ZkStateReader.REPLICATION_FACTOR + " is " + repFactor

Modified: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java?rev=1647656&r1=1647655&r2=1647656&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java Tue Dec 23 19:34:15 2014
@@ -22,6 +22,7 @@ import static org.apache.solr.cloud.Over
 import static org.apache.solr.cloud.OverseerCollectionProcessor.COLL_CONF;
 import static org.apache.solr.cloud.OverseerCollectionProcessor.CREATE_NODE_SET;
 import static org.apache.solr.cloud.OverseerCollectionProcessor.SHARD_UNIQUE;
+import static org.apache.solr.cloud.OverseerCollectionProcessor.CREATE_NODE_SET_SHUFFLE;
 import static org.apache.solr.cloud.OverseerCollectionProcessor.NUM_SLICES;
 import static org.apache.solr.cloud.OverseerCollectionProcessor.ONLY_ACTIVE_NODES;
 import static org.apache.solr.cloud.OverseerCollectionProcessor.ONLY_IF_DOWN;
@@ -735,7 +736,7 @@ public class CollectionsHandler extends
          COLL_CONF,
          NUM_SLICES,
          MAX_SHARDS_PER_NODE,
-         CREATE_NODE_SET,
+         CREATE_NODE_SET, CREATE_NODE_SET_SHUFFLE,
          SHARDS_PROP,
          ASYNC,
          DocCollection.STATE_FORMAT,

Modified: lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/cloud/OverseerCollectionProcessorTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/cloud/OverseerCollectionProcessorTest.java?rev=1647656&r1=1647655&r2=1647656&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/cloud/OverseerCollectionProcessorTest.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/cloud/OverseerCollectionProcessorTest.java Tue Dec 23 19:34:15 2014
@@ -48,6 +48,7 @@ import org.junit.Test;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -387,29 +388,24 @@ public class OverseerCollectionProcessor
   }
   
   protected void issueCreateJob(Integer numberOfSlices,
-      Integer replicationFactor, Integer maxShardsPerNode, List<String> createNodeList, boolean sendCreateNodeList) {
-    ZkNodeProps props;
+      Integer replicationFactor, Integer maxShardsPerNode, List<String> createNodeList, boolean sendCreateNodeList, boolean createNodeSetShuffle) {
+    Map<String,Object> propMap = ZkNodeProps.makeMap(
+        Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.CREATE.toLower(),
+        ZkStateReader.REPLICATION_FACTOR, replicationFactor.toString(),
+        "name", COLLECTION_NAME,
+        "collection.configName", CONFIG_NAME,
+        OverseerCollectionProcessor.NUM_SLICES, numberOfSlices.toString(),
+        ZkStateReader.MAX_SHARDS_PER_NODE, maxShardsPerNode.toString()
+    );
     if (sendCreateNodeList) {
-      props = new ZkNodeProps(Overseer.QUEUE_OPERATION,
-          CollectionParams.CollectionAction.CREATE.toLower(),
-          ZkStateReader.REPLICATION_FACTOR,
-          replicationFactor.toString(), "name", COLLECTION_NAME,
-          "collection.configName", CONFIG_NAME,
-          OverseerCollectionProcessor.NUM_SLICES, numberOfSlices.toString(),
-          ZkStateReader.MAX_SHARDS_PER_NODE,
-          maxShardsPerNode.toString(),
-          OverseerCollectionProcessor.CREATE_NODE_SET,
+      propMap.put(OverseerCollectionProcessor.CREATE_NODE_SET,
           (createNodeList != null)?StrUtils.join(createNodeList, ','):null);
-    } else {
-      props = new ZkNodeProps(Overseer.QUEUE_OPERATION,
-          CollectionParams.CollectionAction.CREATE.toLower(),
-          ZkStateReader.REPLICATION_FACTOR,
-          replicationFactor.toString(), "name", COLLECTION_NAME,
-          "collection.configName", CONFIG_NAME,
-          OverseerCollectionProcessor.NUM_SLICES, numberOfSlices.toString(),
-          ZkStateReader.MAX_SHARDS_PER_NODE,
-          maxShardsPerNode.toString());
+      if (OverseerCollectionProcessor.CREATE_NODE_SET_SHUFFLE_DEFAULT != createNodeSetShuffle || random().nextBoolean()) {
+        propMap.put(OverseerCollectionProcessor.CREATE_NODE_SET_SHUFFLE, createNodeSetShuffle);
+      }
     }
+
+    ZkNodeProps props = new ZkNodeProps(propMap);
     QueueEvent qe = new QueueEvent("id", ZkStateReader.toJSON(props), null){
       @Override
       public void setBytes(byte[] bytes) {
@@ -420,7 +416,7 @@ public class OverseerCollectionProcessor
   }
   
   protected void verifySubmitCaptures(List<SubmitCapture> submitCaptures,
-      Integer numberOfSlices, Integer numberOfReplica, Collection<String> createNodes) {
+      Integer numberOfSlices, Integer numberOfReplica, Collection<String> createNodes, boolean dontShuffleCreateNodeSet) {
     List<String> coreNames = new ArrayList<>();
     Map<String,Map<String,Integer>> sliceToNodeUrlsWithoutProtocolPartToNumberOfShardsRunningMapMap = new HashMap<>();
     List<String> nodeUrlWithoutProtocolPartForLiveNodes = new ArrayList<>(
@@ -431,6 +427,7 @@ public class OverseerCollectionProcessor
           .substring(7);
       nodeUrlWithoutProtocolPartForLiveNodes.add(nodeUrlWithoutProtocolPart);
     }
+    final Map<String,String> coreName_TO_nodeUrlWithoutProtocolPartForLiveNodes_map = new HashMap<>();
     
     for (SubmitCapture submitCapture : submitCaptures) {
       ShardRequest shardRequest = submitCapture.shardRequestCapture.getValue();
@@ -456,6 +453,7 @@ public class OverseerCollectionProcessor
           + shardRequest.shards[0],
           nodeUrlWithoutProtocolPartForLiveNodes
               .contains(shardRequest.shards[0]));
+      coreName_TO_nodeUrlWithoutProtocolPartForLiveNodes_map.put(coreName, shardRequest.shards[0]);
       assertEquals(shardRequest.shards, shardRequest.actualShards);
       
       String sliceName = shardRequest.params.get(CoreAdminParams.SHARD);
@@ -481,6 +479,16 @@ public class OverseerCollectionProcessor
         String coreName = COLLECTION_NAME + "_shard" + i + "_replica" + j;
         assertTrue("Shard " + coreName + " was not created",
             coreNames.contains(coreName));
+        
+        if (dontShuffleCreateNodeSet) {
+          final String expectedNodeName = nodeUrlWithoutProtocolPartForLiveNodes.get((numberOfReplica * (i - 1) + (j - 1)) % nodeUrlWithoutProtocolPartForLiveNodes.size());
+          assertFalse("expectedNodeName is null for coreName="+coreName, null == expectedNodeName);
+          
+          final String actualNodeName = coreName_TO_nodeUrlWithoutProtocolPartForLiveNodes_map.get(coreName);
+          assertFalse("actualNodeName is null for coreName="+coreName, null == actualNodeName);
+
+          assertTrue("node name mismatch for coreName="+coreName+" ( actual="+actualNodeName+" versus expected="+expectedNodeName+" )", actualNodeName.equals(expectedNodeName));
+        }
       }
     }
     
@@ -569,6 +577,8 @@ public class OverseerCollectionProcessor
       }
     }
     
+    if (random().nextBoolean()) Collections.shuffle(createNodeList, OverseerCollectionProcessor.RANDOM);
+    
     List<SubmitCapture> submitCaptures = null;
     if (collectionExceptedToBeCreated) {
       submitCaptures = mockShardHandlerForCreateJob(numberOfSlices,
@@ -587,7 +597,10 @@ public class OverseerCollectionProcessor
 
     startComponentUnderTest();
     
-    issueCreateJob(numberOfSlices, replicationFactor, maxShardsPerNode, (createNodeListOption != CreateNodeListOptions.SEND_NULL) ? createNodeList : null, (createNodeListOption != CreateNodeListOptions.DONT_SEND));
+    final List<String> createNodeListToSend = ((createNodeListOption != CreateNodeListOptions.SEND_NULL) ? createNodeList : null);
+    final boolean sendCreateNodeList = (createNodeListOption != CreateNodeListOptions.DONT_SEND);
+    final boolean dontShuffleCreateNodeSet = (createNodeListToSend != null) && sendCreateNodeList && random().nextBoolean();
+    issueCreateJob(numberOfSlices, replicationFactor, maxShardsPerNode, createNodeListToSend, sendCreateNodeList, !dontShuffleCreateNodeSet);
     waitForEmptyQueue(10000);
     
     if (collectionExceptedToBeCreated) {
@@ -598,11 +611,10 @@ public class OverseerCollectionProcessor
 
     if (collectionExceptedToBeCreated) {
       verifySubmitCaptures(submitCaptures, numberOfSlices, replicationFactor,
-          createNodeList);
+          createNodeList, dontShuffleCreateNodeSet);
     }
   }
-  
-  @Test
+    @Test
   public void testNoReplicationEqualNumberOfSlicesPerNode() throws Exception {
     Integer numberOfNodes = 4;
     Integer numberOfNodesToCreateOn = 4;