SOLR-5132: Added a new collection action MODIFYCOLLECTION

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1680052 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Noble Paul 2015-05-18 16:23:19 +00:00
parent 3827a5fb10
commit aecf45429c
9 changed files with 283 additions and 115 deletions

View File

@ -569,12 +569,14 @@ New Features
* SOLR-7226: Make /query/* jmx/* , requestDispatcher/*, <listener> <initParams>
properties in solrconfig.xml editable (Noble Paul)
* SOLR-7240: '/' redirects to '/solr/' for convinience (Martijn Koster, hossman)
* SOLR-7240: '/' redirects to '/solr/' for convenience (Martijn Koster, hossman)
* SOLR-5911: Added payload support for term vectors. New "termPayloads" option for fields
/ types in the schema, and "tv.payloads" param for the term vector component.
(Mike McCandless, David Smiley)
* SOLR-5132: Added a new collection action MODIFYCOLLECTION (Noble Paul)
Bug Fixes
----------------------

View File

@ -53,6 +53,7 @@ import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.util.IOUtils;
import org.apache.solr.core.CloudConfig;
import org.apache.solr.handler.admin.CollectionsHandler;
import org.apache.solr.handler.component.ShardHandler;
import org.apache.solr.update.UpdateShardHandler;
import org.apache.solr.util.stats.Clock;
@ -353,6 +354,9 @@ public class Overseer implements Closeable {
return new ZkWriteCommand(collName, dProp.getDocCollection());
}
break;
case MODIFYCOLLECTION:
CollectionsHandler.verifyRuleParams(zkController.getCoreContainer() ,message.getProperties());
return new CollectionMutator(reader).modifyCollection(clusterState,message);
default:
throw new RuntimeException("unknown operation:" + operation
+ " contents:" + message.getProperties());

View File

@ -18,6 +18,7 @@ package org.apache.solr.cloud;
*/
import static org.apache.solr.cloud.Assign.*;
import static org.apache.solr.common.cloud.ZkNodeProps.makeMap;
import static org.apache.solr.common.cloud.ZkStateReader.*;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.*;
import static org.apache.solr.common.params.CommonParams.*;
@ -135,13 +136,13 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
public int maxParallelThreads = 10;
public static final Map<String,Object> COLL_PROPS = ZkNodeProps.makeMap(
public static final Map<String, Object> COLL_PROPS = Collections.unmodifiableMap(makeMap(
ROUTER, DocRouter.DEFAULT_NAME,
ZkStateReader.REPLICATION_FACTOR, "1",
ZkStateReader.MAX_SHARDS_PER_NODE, "1",
ZkStateReader.AUTO_ADD_REPLICAS, "false",
"rule", null,
"snitch",null);
DocCollection.RULE, null,
DocCollection.SNITCH, null));
static final Random RANDOM;
static {
@ -615,6 +616,9 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
case REBALANCELEADERS:
processRebalanceLeaders(message);
break;
case MODIFYCOLLECTION:
overseer.getInQueue(zkStateReader.getZkClient()).offer(ZkStateReader.toJSON(message));
break;
default:
throw new SolrException(ErrorCode.BAD_REQUEST, "Unknown operation:"
+ operation);
@ -1045,7 +1049,7 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
String core = replica.getStr(ZkStateReader.CORE_NAME_PROP);
// assume the core exists and try to unload it
Map m = ZkNodeProps.makeMap("qt", adminPath, CoreAdminParams.ACTION,
Map m = makeMap("qt", adminPath, CoreAdminParams.ACTION,
CoreAdminAction.UNLOAD.toString(), CoreAdminParams.CORE, core,
CoreAdminParams.DELETE_INSTANCE_DIR, "true",
CoreAdminParams.DELETE_DATA_DIR, "true");
@ -1292,8 +1296,8 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
ShardHandler shardHandler = shardHandlerFactory.getShardHandler();
DocCollection collection = clusterState.getCollection(collectionName);
int maxShardsPerNode = collection.getInt(ZkStateReader.MAX_SHARDS_PER_NODE, 1);
int repFactor = message.getInt(ZkStateReader.REPLICATION_FACTOR, collection.getInt(ZkStateReader.REPLICATION_FACTOR, 1));
int maxShardsPerNode = collection.getInt(MAX_SHARDS_PER_NODE, 1);
int repFactor = message.getInt(REPLICATION_FACTOR, collection.getInt(REPLICATION_FACTOR, 1));
String createNodeSetStr = message.getStr(CREATE_NODE_SET);
ArrayList<Node> sortedNodeList = getNodesForNewShard(clusterState, collectionName, numSlices, maxShardsPerNode, repFactor, createNodeSetStr);
@ -1981,7 +1985,7 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
String tempSourceCollectionName = "split_" + sourceSlice.getName() + "_temp_" + targetSlice.getName();
if (clusterState.hasCollection(tempSourceCollectionName)) {
log.info("Deleting temporary collection: " + tempSourceCollectionName);
Map<String, Object> props = ZkNodeProps.makeMap(
Map<String, Object> props = makeMap(
Overseer.QUEUE_OPERATION, DELETE.toLower(),
NAME, tempSourceCollectionName);
@ -2065,10 +2069,10 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
// create a temporary collection with just one node on the shard leader
String configName = zkStateReader.readConfigName(sourceCollection.getName());
Map<String, Object> props = ZkNodeProps.makeMap(
Map<String, Object> props = makeMap(
Overseer.QUEUE_OPERATION, CREATE.toLower(),
NAME, tempSourceCollectionName,
ZkStateReader.REPLICATION_FACTOR, 1,
REPLICATION_FACTOR, 1,
NUM_SLICES, 1,
COLL_CONF, configName,
CREATE_NODE_SET, sourceLeader.getNodeName());
@ -2194,7 +2198,7 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
try {
log.info("Deleting temporary collection: " + tempSourceCollectionName);
props = ZkNodeProps.makeMap(
props = makeMap(
Overseer.QUEUE_OPERATION, DELETE.toLower(),
NAME, tempSourceCollectionName);
deleteCollection(new ZkNodeProps(props), results);
@ -2293,7 +2297,7 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
// look at the replication factor and see if it matches reality
// if it does not, find best nodes to create more cores
int repFactor = message.getInt(ZkStateReader.REPLICATION_FACTOR, 1);
int repFactor = message.getInt(REPLICATION_FACTOR, 1);
ShardHandler shardHandler = shardHandlerFactory.getShardHandler();
String async = null;
@ -2312,10 +2316,10 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
ClusterStateMutator.getShardNames(numSlices, shardNames);
}
int maxShardsPerNode = message.getInt(ZkStateReader.MAX_SHARDS_PER_NODE, 1);
int maxShardsPerNode = message.getInt(MAX_SHARDS_PER_NODE, 1);
if (repFactor <= 0) {
throw new SolrException(ErrorCode.BAD_REQUEST, ZkStateReader.REPLICATION_FACTOR + " must be greater than 0");
throw new SolrException(ErrorCode.BAD_REQUEST, REPLICATION_FACTOR + " must be greater than 0");
}
if (numSlices <= 0) {
@ -2330,7 +2334,7 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
if (repFactor > nodeList.size()) {
log.warn("Specified "
+ ZkStateReader.REPLICATION_FACTOR
+ REPLICATION_FACTOR
+ " of "
+ repFactor
+ " on collection "
@ -2344,11 +2348,11 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
int requestedShardsToCreate = numSlices * repFactor;
if (maxShardsAllowedToCreate < requestedShardsToCreate) {
throw new SolrException(ErrorCode.BAD_REQUEST, "Cannot create collection " + collectionName + ". Value of "
+ ZkStateReader.MAX_SHARDS_PER_NODE + " is " + maxShardsPerNode
+ MAX_SHARDS_PER_NODE + " is " + maxShardsPerNode
+ ", 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
+ " and value of " + REPLICATION_FACTOR + " is " + repFactor
+ ". This requires " + requestedShardsToCreate
+ " shards to be created (higher than the allowed number)");
}
@ -2556,7 +2560,7 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
ShardHandler shardHandler = shardHandlerFactory.getShardHandler();
if (node == null) {
node = getNodesForNewShard(clusterState, collection, coll.getSlices().size(), coll.getInt(ZkStateReader.MAX_SHARDS_PER_NODE, 1), coll.getInt(ZkStateReader.REPLICATION_FACTOR, 1), null).get(0).nodeName;
node = getNodesForNewShard(clusterState, collection, coll.getSlices().size(), coll.getInt(MAX_SHARDS_PER_NODE, 1), coll.getInt(REPLICATION_FACTOR, 1), null).get(0).nodeName;
log.info("Node not provided, Identified {} for creating new replica", node);
}
@ -2684,7 +2688,7 @@ public class OverseerCollectionProcessor implements Runnable, Closeable {
if (configName != null) {
String collDir = ZkStateReader.COLLECTIONS_ZKNODE + "/" + coll;
log.info("creating collections conf node {} ", collDir);
byte[] data = ZkStateReader.toJSON(ZkNodeProps.makeMap(ZkController.CONFIGNAME_PROP, configName));
byte[] data = ZkStateReader.toJSON(makeMap(ZkController.CONFIGNAME_PROP, configName));
if (zkStateReader.getZkClient().exists(collDir, true)) {
zkStateReader.getZkClient().setData(collDir, data, true);
} else {

View File

@ -29,9 +29,11 @@ import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.handler.admin.CollectionsHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.params.CommonParams.NAME;
public class CollectionMutator {
@ -84,6 +86,17 @@ public class CollectionMutator {
return new ZkWriteCommand(collection, newCollection);
}
public ZkWriteCommand modifyCollection(final ClusterState clusterState, ZkNodeProps message){
if (!checkCollectionKeyExistence(message)) return ZkStateWriter.NO_OP;
DocCollection coll = clusterState.getCollection(message.getStr(COLLECTION_PROP));
Map<String, Object> m = coll.shallowCopy();
for (String prop : CollectionsHandler.MODIFIABLE_COLL_PROPS) {
if(message.get(prop)!= null) m.put(prop,message.get(prop));
}
return new ZkWriteCommand(coll.getName(),
new DocCollection(coll.getName(),coll.getSlicesMap(),m,coll.getRouter(),coll.getZNodeVersion(),coll.getZNode()));
}
public static DocCollection updateSlice(String collectionName, DocCollection collection, Slice slice) {
DocCollection newCollection = null;
Map<String, Slice> slices;

View File

@ -326,62 +326,39 @@ public class ReplicaAssigner {
public Map<String, SnitchContext> failedNodes = new HashMap<>();
static class SnitchInfoImpl extends SnitchContext.SnitchInfo {
final Snitch snitch;
final Set<String> myTags = new HashSet<>();
final Map<String, SnitchContext> nodeVsContext = new HashMap<>();
private final CoreContainer cc;
SnitchInfoImpl(Map<String, Object> conf, Snitch snitch, CoreContainer cc) {
super(conf);
this.snitch = snitch;
this.cc = cc;
}
@Override
public Set<String> getTagNames() {
return myTags;
}
@Override
public CoreContainer getCoreContainer() {
return cc;
}
}
/**
* This method uses the snitches and get the tags for all the nodes
*/
private Map<String, Map<String, Object>> getTagsForNodes(final CoreContainer cc, List snitchConf) {
class Info extends SnitchContext.SnitchInfo {
final Snitch snitch;
final Set<String> myTags = new HashSet<>();
final Map<String, SnitchContext> nodeVsContext = new HashMap<>();
Info(Map<String, Object> conf, Snitch snitch) {
super(conf);
this.snitch = snitch;
}
@Override
public Set<String> getTagNames() {
return myTags;
}
@Override
public CoreContainer getCoreContainer() {
return cc;
}
}
Map<Class, Info> snitches = new LinkedHashMap<>();
for (Object o : snitchConf) {
//instantiating explicitly specified snitches
String klas = null;
Map map = Collections.emptyMap();
if (o instanceof Map) {//it can be a Map
map = (Map) o;
klas = (String) map.get("class");
if (klas == null) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "snitch must have a class attribute");
}
} else { //or just the snitch name
klas = o.toString();
}
try {
if (klas.indexOf('.') == -1) klas = Snitch.class.getPackage().getName() + "." + klas;
Snitch inst = cc == null ?
(Snitch) Snitch.class.getClassLoader().loadClass(klas).newInstance() :
cc.getResourceLoader().newInstance(klas, Snitch.class);
snitches.put(inst.getClass(), new Info(map, inst));
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
}
}
Map<Class, SnitchInfoImpl> snitches = getSnitchInfos(cc, snitchConf);
for (Class c : Snitch.WELL_KNOWN_SNITCHES) {
if (snitches.containsKey(c)) continue;// it is already specified explicitly , ignore
try {
snitches.put(c, new Info(Collections.EMPTY_MAP, (Snitch) c.newInstance()));
snitches.put(c, new SnitchInfoImpl(Collections.EMPTY_MAP, (Snitch) c.newInstance(), cc));
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error instantiating Snitch " + c.getName());
}
@ -389,7 +366,7 @@ public class ReplicaAssigner {
for (String tagName : tagNames) {
//identify which snitch is going to provide values for a given tag
boolean foundProvider = false;
for (Info info : snitches.values()) {
for (SnitchInfoImpl info : snitches.values()) {
if (info.snitch.isKnownTag(tagName)) {
foundProvider = true;
info.myTags.add(tagName);
@ -403,7 +380,7 @@ public class ReplicaAssigner {
for (String node : liveNodes) {
//now use the Snitch to get the tags
for (Info info : snitches.values()) {
for (SnitchInfoImpl info : snitches.values()) {
if (!info.myTags.isEmpty()) {
SnitchContext context = new SnitchContext(info, node);
info.nodeVsContext.put(node, context);
@ -417,7 +394,7 @@ public class ReplicaAssigner {
}
Map<String, Map<String, Object>> result = new HashMap<>();
for (Info info : snitches.values()) {
for (SnitchInfoImpl info : snitches.values()) {
for (Map.Entry<String, SnitchContext> e : info.nodeVsContext.entrySet()) {
SnitchContext context = e.getValue();
String node = e.getKey();
@ -448,4 +425,40 @@ public class ReplicaAssigner {
}
public static void verifySnitchConf(CoreContainer cc, List snitchConf) {
getSnitchInfos(cc, snitchConf);
}
static Map<Class, SnitchInfoImpl> getSnitchInfos(CoreContainer cc, List snitchConf) {
Map<Class, SnitchInfoImpl> snitches = new LinkedHashMap<>();
if (snitchConf == null) return snitches;
for (Object o : snitchConf) {
//instantiating explicitly specified snitches
String klas = null;
Map map = Collections.emptyMap();
if (o instanceof Map) {//it can be a Map
map = (Map) o;
klas = (String) map.get("class");
if (klas == null) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "snitch must have a class attribute");
}
} else { //or just the snitch name
klas = o.toString();
}
try {
if (klas.indexOf('.') == -1) klas = Snitch.class.getPackage().getName() + "." + klas;
Snitch inst = cc == null ?
(Snitch) Snitch.class.getClassLoader().loadClass(klas).newInstance() :
cc.getResourceLoader().newInstance(klas, Snitch.class);
snitches.put(inst.getClass(), new SnitchInfoImpl(map, inst, cc));
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
}
}
return snitches;
}
}

View File

@ -19,6 +19,7 @@ package org.apache.solr.handler.admin;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
@ -27,6 +28,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang.StringUtils;
import org.apache.solr.client.solrj.SolrResponse;
@ -35,12 +37,15 @@ import org.apache.solr.client.solrj.request.CoreAdminRequest;
import org.apache.solr.client.solrj.request.CoreAdminRequest.RequestSyncShard;
import org.apache.solr.cloud.DistributedQueue;
import org.apache.solr.cloud.DistributedQueue.QueueEvent;
import org.apache.solr.cloud.Overseer;
import org.apache.solr.cloud.OverseerSolrResponse;
import org.apache.solr.cloud.overseer.SliceMutator;
import org.apache.solr.cloud.rule.ReplicaAssigner;
import org.apache.solr.cloud.rule.Rule;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.ImplicitDocRouter;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkCmdExecutor;
@ -76,6 +81,8 @@ import static org.apache.solr.cloud.OverseerCollectionProcessor.REQUESTID;
import static org.apache.solr.cloud.OverseerCollectionProcessor.SHARDS_PROP;
import static org.apache.solr.cloud.OverseerCollectionProcessor.SHARD_UNIQUE;
import static org.apache.solr.common.cloud.DocCollection.DOC_ROUTER;
import static org.apache.solr.common.cloud.DocCollection.RULE;
import static org.apache.solr.common.cloud.DocCollection.SNITCH;
import static org.apache.solr.common.cloud.DocCollection.STATE_FORMAT;
import static org.apache.solr.common.cloud.ZkStateReader.AUTO_ADD_REPLICAS;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
@ -91,6 +98,7 @@ import static org.apache.solr.common.params.CommonParams.VALUE_LONG;
import static org.apache.solr.common.params.CoreAdminParams.DATA_DIR;
import static org.apache.solr.common.params.CoreAdminParams.INSTANCE_DIR;
import static org.apache.solr.common.params.ShardParams._ROUTE_;
import static org.apache.solr.common.util.StrUtils.formatString;
public class CollectionsHandler extends RequestHandlerBase {
protected static Logger log = LoggerFactory.getLogger(CollectionsHandler.class);
@ -157,7 +165,9 @@ public class CollectionsHandler extends RequestHandlerBase {
if (result != null) {
result.put(QUEUE_OPERATION, operation.action.toLower());
ZkNodeProps props = new ZkNodeProps(result);
handleResponse(operation.action.toLower(), props, rsp, operation.timeOut);
if (operation.sendToOCPQueue) handleResponse(operation.action.toLower(), props, rsp, operation.timeOut);
else Overseer.getInQueue(coreContainer.getZkController().getZkClient()).offer(ZkStateReader.toJSON(props));
}
} else {
throw new SolrException(ErrorCode.BAD_REQUEST, "action is a required param");
@ -287,7 +297,6 @@ public class CollectionsHandler extends RequestHandlerBase {
Map<String, Object> props = req.getParams().required().getAll(null, NAME);
props.put("fromApi", "true");
req.getParams().getAll(props,
NAME,
REPLICATION_FACTOR,
COLL_CONF,
NUM_SLICES,
@ -296,16 +305,18 @@ public class CollectionsHandler extends RequestHandlerBase {
SHARDS_PROP,
ASYNC,
STATE_FORMAT,
AUTO_ADD_REPLICAS);
AUTO_ADD_REPLICAS,
RULE,
SNITCH);
if (props.get(STATE_FORMAT) == null) {
props.put(STATE_FORMAT, "2");
}
addRuleMap(req.getParams(), props, "rule");
addRuleMap(req.getParams(), props, "snitch");
addMapObject(props, RULE);
addMapObject(props, SNITCH);
verifyRuleParams(h.coreContainer, props);
if (SYSTEM_COLL.equals(props.get(NAME))) {
//We must always create asystem collection with only a single shard
//We must always create a .system collection with only a single shard
props.put(NUM_SLICES, 1);
props.remove(SHARDS_PROP);
createSysConfigSet(h.coreContainer);
@ -316,15 +327,6 @@ public class CollectionsHandler extends RequestHandlerBase {
}
private void addRuleMap(SolrParams params, Map<String, Object> props, String key) {
String[] rules = params.getParams(key);
if (rules != null && rules.length > 0) {
ArrayList<Map> l = new ArrayList<>();
for (String rule : rules) l.add(Rule.parseRule(rule));
props.put(key, l);
}
}
private void createSysConfigSet(CoreContainer coreContainer) throws KeeperException, InterruptedException {
SolrZkClient zk = coreContainer.getZkController().getZkStateReader().getZkClient();
ZkCmdExecutor cmdExecutor = new ZkCmdExecutor(zk.getZkClientTimeout());
@ -396,7 +398,7 @@ public class CollectionsHandler extends RequestHandlerBase {
}
},
SPLITSHARD_OP(SPLITSHARD, DEFAULT_ZK_TIMEOUT * 5) {
SPLITSHARD_OP(SPLITSHARD, DEFAULT_ZK_TIMEOUT * 5, true) {
@Override
Map<String, Object> call(SolrQueryRequest req, SolrQueryResponse rsp, CollectionsHandler h)
throws Exception {
@ -670,17 +672,35 @@ public class CollectionsHandler extends RequestHandlerBase {
new RebalanceLeaders(req,rsp,h).execute();
return null;
}
},
MODIFYCOLLECTION_OP(MODIFYCOLLECTION, DEFAULT_ZK_TIMEOUT, false) {
@Override
Map<String, Object> call(SolrQueryRequest req, SolrQueryResponse rsp, CollectionsHandler h) throws Exception {
Map<String, Object> m = req.getParams().getAll(null, MODIFIABLE_COLL_PROPS.toArray(new String[0]));
if (m.isEmpty()) throw new SolrException(ErrorCode.BAD_REQUEST,
formatString("no supported values provided rule, snitch, masShardsPerNode, replicationFactor"));
req.getParams().required().getAll(m, COLLECTION_PROP);
addMapObject(m, RULE);
addMapObject(m, SNITCH);
for (String prop : MODIFIABLE_COLL_PROPS) DocCollection.verifyProp(m, prop);
verifyRuleParams(h.coreContainer, m);
return m;
}
};
CollectionAction action;
long timeOut;
boolean sendToOCPQueue;
CollectionOperation(CollectionAction action) {
this(action, DEFAULT_ZK_TIMEOUT);
this(action, DEFAULT_ZK_TIMEOUT, true);
}
CollectionOperation(CollectionAction action, long timeOut) {
CollectionOperation(CollectionAction action, long timeOut, boolean sendToOCPQueue) {
this.action = action;
this.timeOut = timeOut;
this.sendToOCPQueue = sendToOCPQueue;
}
/**
@ -697,4 +717,46 @@ public class CollectionsHandler extends RequestHandlerBase {
}
}
public static void verifyRuleParams(CoreContainer cc, Map<String, Object> m) {
List l = (List) m.get(RULE);
if (l != null) {
for (Object o : l) {
Map map = (Map) o;
try {
new Rule(map);
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error in rule " + m, e);
}
}
}
ReplicaAssigner.verifySnitchConf(cc, (List) m.get(SNITCH));
}
/**
* Converts a String of the form a:b,c:d to a Map
*/
private static Map<String, Object> addMapObject(Map<String, Object> props, String key) {
Object v = props.get(key);
if (v == null) return props;
List<String> val = new ArrayList<>();
if (v instanceof String[]) {
val.addAll(Arrays.asList((String[]) v));
} else {
val.add(v.toString());
}
if (val.size() > 0) {
ArrayList<Map> l = new ArrayList<>();
for (String rule : val) l.add(Rule.parseRule(rule));
props.put(key, l);
}
return props;
}
public static final List<String> MODIFIABLE_COLL_PROPS = ImmutableList.of(
RULE,
SNITCH,
REPLICATION_FACTOR,
MAX_SHARDS_PER_NODE,
AUTO_ADD_REPLICAS);
}

View File

@ -21,15 +21,24 @@ import java.util.List;
import java.util.Map;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.client.solrj.response.CollectionAdminResponse;
import org.apache.solr.cloud.AbstractFullDistribZkTestBase;
import org.apache.solr.cloud.OverseerCollectionProcessor;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.core.CoreContainer;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
import static org.apache.solr.core.CoreContainer.COLLECTIONS_HANDLER_PATH;
public class RulesTest extends AbstractFullDistribZkTestBase {
static final Logger log = LoggerFactory.getLogger(RulesTest.class);
@ -62,6 +71,53 @@ public class RulesTest extends AbstractFullDistribZkTestBase {
}
@Test
public void testModifyColl() throws Exception {
String rulesColl = "modifyColl";
try (SolrClient client = createNewSolrClient("", getBaseUrl((HttpSolrClient) clients.get(0)))) {
CollectionAdminResponse rsp;
CollectionAdminRequest.Create create = new CollectionAdminRequest.Create();
create.setCollectionName(rulesColl);
create.setNumShards(1);
create.setReplicationFactor(2);
create.setRule("cores:<4", "node:*,replica:1", "freedisk:>1");
create.setSnitch("class:ImplicitSnitch");
rsp = create.process(client);
assertEquals(0, rsp.getStatus());
assertTrue(rsp.isSuccess());
ModifiableSolrParams p = new ModifiableSolrParams();
p.add("collection", rulesColl);
p.add("action", "MODIFYCOLLECTION");
p.add("rule", "cores:<5");
p.add("rule", "node:*,replica:1");
p.add("rule", "freedisk:>5");
p.add("autoAddReplicas", "true");
client.request(new GenericSolrRequest(POST, COLLECTIONS_HANDLER_PATH, p));
}
for (int i = 0; i < 20; i++) {
DocCollection rulesCollection = ZkStateReader.getCollectionLive(cloudClient.getZkStateReader(), rulesColl);
log.info("version_of_coll {} ", rulesCollection.getZNodeVersion());
List list = (List) rulesCollection.get("rule");
assertEquals(3, list.size());
if (!"<5".equals(((Map) list.get(0)).get("cores"))) {
if (i < 19) {
Thread.sleep(100);
continue;
}
}
assertEquals("<5", ((Map) list.get(0)).get("cores"));
assertEquals("1", ((Map) list.get(1)).get("replica"));
assertEquals(">5", ((Map) list.get(2)).get("freedisk"));
assertEquals("true", String.valueOf(rulesCollection.getProperties().get("autoAddReplicas")));
list = (List) rulesCollection.get("snitch");
assertEquals(1, list.size());
assertEquals("ImplicitSnitch", ((Map) list.get(0)).get("class"));
}
}
}

View File

@ -21,6 +21,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.solr.common.SolrException;
@ -28,6 +29,10 @@ import org.apache.solr.common.SolrException.ErrorCode;
import org.noggit.JSONUtil;
import org.noggit.JSONWriter;
import static org.apache.solr.common.cloud.ZkStateReader.AUTO_ADD_REPLICAS;
import static org.apache.solr.common.cloud.ZkStateReader.MAX_SHARDS_PER_NODE;
import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR;
/**
* Models a Collection in zookeeper (but that Java name is obviously taken, hence "DocCollection")
*/
@ -35,6 +40,8 @@ public class DocCollection extends ZkNodeProps {
public static final String DOC_ROUTER = "router";
public static final String SHARDS = "shards";
public static final String STATE_FORMAT = "stateFormat";
public static final String RULE = "rule";
public static final String SNITCH = "snitch";
private int znodeVersion = -1; // sentinel
private final String name;
@ -45,7 +52,7 @@ public class DocCollection extends ZkNodeProps {
private final Integer replicationFactor;
private final Integer maxShardsPerNode;
private final boolean autoAddReplicas;
private final Boolean autoAddReplicas;
public DocCollection(String name, Map<String, Slice> slices, Map<String, Object> props, DocRouter router) {
@ -64,25 +71,12 @@ public class DocCollection extends ZkNodeProps {
this.slices = slices;
this.activeSlices = new HashMap<>();
Object replicationFactorObject = (Object) props.get(ZkStateReader.REPLICATION_FACTOR);
if (replicationFactorObject != null) {
this.replicationFactor = Integer.parseInt(replicationFactorObject.toString());
} else {
this.replicationFactor = null;
}
Object maxShardsPerNodeObject = (Object) props.get(ZkStateReader.MAX_SHARDS_PER_NODE);
if (maxShardsPerNodeObject != null) {
this.maxShardsPerNode = Integer.parseInt(maxShardsPerNodeObject.toString());
} else {
this.maxShardsPerNode = null;
}
Object autoAddReplicasObject = (Object) props.get(ZkStateReader.AUTO_ADD_REPLICAS);
if (autoAddReplicasObject != null) {
this.autoAddReplicas = Boolean.parseBoolean(autoAddReplicasObject.toString());
} else {
this.autoAddReplicas = false;
}
this.replicationFactor = (Integer) verifyProp(props, REPLICATION_FACTOR);
this.maxShardsPerNode = (Integer) verifyProp(props, MAX_SHARDS_PER_NODE);
Boolean autoAddReplicas = (Boolean) verifyProp(props, AUTO_ADD_REPLICAS);
this.autoAddReplicas = autoAddReplicas == null ? false : autoAddReplicas;
verifyProp(props, RULE);
verifyProp(props, SNITCH);
Iterator<Map.Entry<String, Slice>> iter = slices.entrySet().iterator();
while (iter.hasNext()) {
@ -95,6 +89,24 @@ public class DocCollection extends ZkNodeProps {
assert name != null && slices != null;
}
public static Object verifyProp(Map<String, Object> props, String propName) {
Object o = props.get(propName);
if (o == null) return null;
switch (propName) {
case MAX_SHARDS_PER_NODE:
case REPLICATION_FACTOR:
return Integer.parseInt(o.toString());
case AUTO_ADD_REPLICAS:
return Boolean.parseBoolean(o.toString());
case "snitch":
case "rule":
return (List) o;
default:
throw new SolrException(ErrorCode.SERVER_ERROR, "Unknown property " + propName);
}
}
/**Use this to make an exact copy of DocCollection with a new set of Slices and every other property as is
* @param slices the new set of Slices
* @return the resulting DocCollection
@ -164,7 +176,7 @@ public class DocCollection extends ZkNodeProps {
public int getMaxShardsPerNode() {
if (maxShardsPerNode == null) {
throw new SolrException(ErrorCode.BAD_REQUEST, ZkStateReader.MAX_SHARDS_PER_NODE + " is not in the cluster state.");
throw new SolrException(ErrorCode.BAD_REQUEST, MAX_SHARDS_PER_NODE + " is not in the cluster state.");
}
return maxShardsPerNode;
}

View File

@ -19,6 +19,8 @@ package org.apache.solr.common.params;
import java.util.Locale;
import org.apache.solr.common.SolrException;
public interface CollectionParams
{
/** What action **/
@ -50,17 +52,17 @@ public interface CollectionParams
ADDREPLICAPROP,
DELETEREPLICAPROP,
BALANCESHARDUNIQUE,
REBALANCELEADERS;
public static CollectionAction get( String p )
{
REBALANCELEADERS,
MODIFYCOLLECTION;
public static CollectionAction get(String p) {
if( p != null ) {
try {
return CollectionAction.valueOf( p.toUpperCase(Locale.ROOT) );
}
catch( Exception ex ) {}
}
return null;
return null;
}
public boolean isEqual(String s){
if(s == null) return false;