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;