SOLR-13583: Impossible to delete a collection with the same name as an existing alias.

This commit is contained in:
Andrzej Bialecki 2019-07-03 11:05:52 +02:00
parent 1c1d77a791
commit 30a5b2cd87
21 changed files with 354 additions and 65 deletions

View File

@ -216,6 +216,9 @@ Bug Fixes
* SOLR-13159: Fix atomic update encoding issue for UUID, enum, bool, and binary fields (Thomas Wockinger via Jason Gerlowski)
* SOLR-13583: Impossible to delete a collection with the same name as an existing alias. This fixes also a bug in
REINDEXCOLLECTION when used with removeSource=true which could lead to a data loss. (ab)
Improvements
----------------------

View File

@ -27,6 +27,7 @@ import static org.apache.solr.common.cloud.ZkStateReader.PULL_REPLICAS;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.TLOG_REPLICAS;
import static org.apache.solr.common.params.CollectionAdminParams.COLL_CONF;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CollectionAdminParams.WITH_COLLECTION;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
@ -97,9 +98,15 @@ public class AddReplicaCmd implements OverseerCollectionMessageHandler.Cmd {
log.debug("addReplica() : {}", Utils.toJSONString(message));
String extCollectionName = message.getStr(COLLECTION_PROP);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String shard = message.getStr(SHARD_ID_PROP);
final String collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
final String collectionName;
if (followAliases) {
collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
} else {
collectionName = extCollectionName;
}
DocCollection coll = clusterState.getCollection(collectionName);
if (coll == null) {

View File

@ -18,6 +18,7 @@ package org.apache.solr.cloud.api.collections;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonParams.NAME;
@ -67,7 +68,13 @@ public class BackupCmd implements OverseerCollectionMessageHandler.Cmd {
@Override
public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception {
String extCollectionName = message.getStr(COLLECTION_PROP);
String collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collectionName;
if (followAliases) {
collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
} else {
collectionName = extCollectionName;
}
String backupName = message.getStr(NAME);
String repo = message.getStr(CoreAdminParams.BACKUP_REPOSITORY);

View File

@ -112,7 +112,7 @@ public class CreateCollectionCmd implements OverseerCollectionMessageHandler.Cmd
final boolean waitForFinalState = message.getBool(WAIT_FOR_FINAL_STATE, false);
final String alias = message.getStr(ALIAS, collectionName);
log.info("Create collection {}", collectionName);
if (clusterState.hasCollection(collectionName) || aliases.hasAlias(collectionName)) {
if (clusterState.hasCollection(collectionName)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "collection already exists: " + collectionName);
}
if (aliases.hasAlias(collectionName)) {

View File

@ -39,6 +39,7 @@ import static org.apache.solr.common.cloud.ZkStateReader.PULL_REPLICAS;
import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.TLOG_REPLICAS;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
public class CreateShardCmd implements OverseerCollectionMessageHandler.Cmd {
@ -59,7 +60,13 @@ public class CreateShardCmd implements OverseerCollectionMessageHandler.Cmd {
if (extCollectionName == null || sliceName == null)
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "'collection' and 'shard' are required parameters");
String collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collectionName;
if (followAliases) {
collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
} else {
collectionName = extCollectionName;
}
DocCollection collection = clusterState.getCollection(collectionName);
int numNrtReplicas = message.getInt(NRT_REPLICAS, message.getInt(REPLICATION_FACTOR, collection.getInt(NRT_REPLICAS, collection.getInt(REPLICATION_FACTOR, 1))));

View File

@ -18,6 +18,7 @@ package org.apache.solr.cloud.api.collections;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonParams.NAME;
@ -66,7 +67,14 @@ public class CreateSnapshotCmd implements OverseerCollectionMessageHandler.Cmd {
@Override
public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception {
String extCollectionName = message.getStr(COLLECTION_PROP);
String collectionName = ocmh.zkStateReader.getAliases().resolveSimpleAlias(extCollectionName);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collectionName;
if (followAliases) {
collectionName = ocmh.zkStateReader.getAliases().resolveSimpleAlias(extCollectionName);
} else {
collectionName = extCollectionName;
}
String commitName = message.getStr(CoreAdminParams.COMMIT_NAME);
String asyncId = message.getStr(ASYNC);

View File

@ -19,6 +19,7 @@
package org.apache.solr.cloud.api.collections;
import static org.apache.solr.common.params.CollectionAdminParams.COLOCATED_WITH;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CollectionAdminParams.WITH_COLLECTION;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETE;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
@ -76,10 +77,17 @@ public class DeleteCollectionCmd implements OverseerCollectionMessageHandler.Cmd
zkStateReader.aliasesManager.update(); // aliases may have been stale; get latest from ZK
}
List<String> aliasReferences = checkAliasReference(zkStateReader, extCollection);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
List<String> aliasReferences = checkAliasReference(zkStateReader, extCollection, followAliases);
Aliases aliases = zkStateReader.getAliases();
String collection = aliases.resolveSimpleAlias(extCollection);
String collection;
if (followAliases) {
collection = aliases.resolveSimpleAlias(extCollection);
} else {
collection = extCollection;
}
checkNotColocatedWith(zkStateReader, collection);
@ -118,8 +126,9 @@ public class DeleteCollectionCmd implements OverseerCollectionMessageHandler.Cmd
Set<String> okayExceptions = new HashSet<>(1);
okayExceptions.add(NonExistentCoreException.class.getName());
ZkNodeProps internalMsg = message.plus(NAME, collection);
List<Replica> failedReplicas = ocmh.collectionCmd(message, params, results, null, asyncId, okayExceptions);
List<Replica> failedReplicas = ocmh.collectionCmd(internalMsg, params, results, null, asyncId, okayExceptions);
for (Replica failedReplica : failedReplicas) {
boolean isSharedFS = failedReplica.getBool(ZkStateReader.SHARED_STORAGE_PROP, false) && failedReplica.get("dataDir") != null;
if (isSharedFS) {
@ -185,14 +194,14 @@ public class DeleteCollectionCmd implements OverseerCollectionMessageHandler.Cmd
}
// This method returns the single collection aliases to delete, if present, or null
private List<String> checkAliasReference(ZkStateReader zkStateReader, String extCollection) throws Exception {
private List<String> checkAliasReference(ZkStateReader zkStateReader, String extCollection, boolean followAliases) throws Exception {
Aliases aliases = zkStateReader.getAliases();
List<String> aliasesRefs = referencedByAlias(extCollection, aliases);
List<String> aliasesRefs = referencedByAlias(extCollection, aliases, followAliases);
List<String> aliasesToDelete = new ArrayList<>();
if (aliasesRefs.size() > 0) {
zkStateReader.aliasesManager.update(); // aliases may have been stale; get latest from ZK
aliases = zkStateReader.getAliases();
aliasesRefs = referencedByAlias(extCollection, aliases);
aliasesRefs = referencedByAlias(extCollection, aliases, followAliases);
if (aliasesRefs.size() > 0) {
for (String alias : aliasesRefs) {
// for back-compat in 8.x we don't automatically remove other
@ -209,11 +218,12 @@ public class DeleteCollectionCmd implements OverseerCollectionMessageHandler.Cmd
return aliasesToDelete;
}
public static List<String> referencedByAlias(String extCollection, Aliases aliases) throws IllegalArgumentException {
public static List<String> referencedByAlias(String extCollection, Aliases aliases, boolean followAliases) throws IllegalArgumentException {
Objects.requireNonNull(aliases);
// this quickly produces error if the name is a complex alias
String collection = aliases.resolveSimpleAlias(extCollection);
String collection = followAliases ? aliases.resolveSimpleAlias(extCollection) : extCollection;
return aliases.getCollectionAliasListMap().entrySet().stream()
.filter(e -> !e.getKey().equals(collection))
.filter(e -> e.getValue().contains(collection) || e.getValue().contains(extCollection))
.map(Map.Entry::getKey) // alias name
.collect(Collectors.toList());

View File

@ -20,6 +20,7 @@ import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.REPLICA_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.COUNT_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
import java.lang.invoke.MethodHandles;
@ -85,7 +86,13 @@ public class DeleteReplicaCmd implements Cmd {
String shard = message.getStr(SHARD_ID_PROP);
String replicaName = message.getStr(REPLICA_PROP);
String collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collectionName;
if (followAliases) {
collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
} else {
collectionName = extCollectionName;
}
DocCollection coll = clusterState.getCollection(collectionName);
Slice slice = coll.getSlice(shard);

View File

@ -20,6 +20,7 @@ package org.apache.solr.cloud.api.collections;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.NODE_NAME_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETEREPLICA;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETESHARD;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
@ -65,7 +66,13 @@ public class DeleteShardCmd implements OverseerCollectionMessageHandler.Cmd {
String extCollectionName = message.getStr(ZkStateReader.COLLECTION_PROP);
String sliceId = message.getStr(ZkStateReader.SHARD_ID_PROP);
String collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collectionName;
if (followAliases) {
collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
} else {
collectionName = extCollectionName;
}
log.info("Delete shard invoked");
Slice slice = clusterState.getCollection(collectionName).getSlice(sliceId);

View File

@ -18,6 +18,7 @@ package org.apache.solr.cloud.api.collections;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonParams.NAME;
@ -64,7 +65,13 @@ public class DeleteSnapshotCmd implements OverseerCollectionMessageHandler.Cmd {
@Override
public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception {
String extCollectionName = message.getStr(COLLECTION_PROP);
String collectionName = ocmh.zkStateReader.getAliases().resolveSimpleAlias(extCollectionName);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collectionName;
if (followAliases) {
collectionName = ocmh.zkStateReader.getAliases().resolveSimpleAlias(extCollectionName);
} else {
collectionName = extCollectionName;
}
String commitName = message.getStr(CoreAdminParams.COMMIT_NAME);
String asyncId = message.getStr(ASYNC);
NamedList shardRequestResults = new NamedList();

View File

@ -54,6 +54,7 @@ import org.slf4j.LoggerFactory;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATE;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETE;
@ -79,8 +80,16 @@ public class MigrateCmd implements OverseerCollectionMessageHandler.Cmd {
String extTargetCollectionName = message.getStr("target.collection");
int timeout = message.getInt("forward.timeout", 10 * 60) * 1000;
String sourceCollectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extSourceCollectionName);
String targetCollectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extTargetCollectionName);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String sourceCollectionName;
String targetCollectionName;
if (followAliases) {
sourceCollectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extSourceCollectionName);
targetCollectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extTargetCollectionName);
} else {
sourceCollectionName = extSourceCollectionName;
targetCollectionName = extTargetCollectionName;
}
DocCollection sourceCollection = clusterState.getCollection(sourceCollectionName);
if (sourceCollection == null) {

View File

@ -48,6 +48,7 @@ import static org.apache.solr.cloud.api.collections.OverseerCollectionMessageHan
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.REPLICA_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonAdminParams.IN_PLACE_MOVE;
import static org.apache.solr.common.params.CommonAdminParams.TIMEOUT;
@ -80,7 +81,13 @@ public class MoveReplicaCmd implements OverseerCollectionMessageHandler.Cmd {
String async = message.getStr(ASYNC);
String collection = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollection);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collection;
if (followAliases) {
collection = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollection);
} else {
collection = extCollection;
}
DocCollection coll = clusterState.getCollection(collection);
if (coll == null) {

View File

@ -64,6 +64,8 @@ import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
/**
* Reindex a collection, usually in order to change the index schema.
* <p>WARNING: Reindexing is potentially a lossy operation - some indexed data that is not available as
@ -178,7 +180,13 @@ public class ReindexCollectionCmd implements OverseerCollectionMessageHandler.Cm
if (extCollection == null) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Source collection name must be specified");
}
String collection = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollection);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collection;
if (followAliases) {
collection = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollection);
} else {
collection = extCollection;
}
if (!clusterState.hasCollection(collection)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Source collection name must exist");
}
@ -186,8 +194,9 @@ public class ReindexCollectionCmd implements OverseerCollectionMessageHandler.Cm
if (target == null) {
target = collection;
} else {
// resolve aliases
target = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(target);
if (followAliases) {
target = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(target);
}
}
boolean sameTarget = target.equals(collection) || target.equals(extCollection);
boolean removeSource = message.getBool(REMOVE_SOURCE, false);
@ -466,6 +475,7 @@ public class ReindexCollectionCmd implements OverseerCollectionMessageHandler.Cm
cmd = new ZkNodeProps(
Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.DELETE.toLower(),
CommonParams.NAME, collection,
FOLLOW_ALIASES, "false",
CoreAdminParams.DELETE_METRICS_HISTORY, "true"
);
cmdResults = new NamedList<>();
@ -770,6 +780,7 @@ public class ReindexCollectionCmd implements OverseerCollectionMessageHandler.Cm
ZkNodeProps cmd = new ZkNodeProps(
Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.DELETE.toLower(),
CommonParams.NAME, targetCollection,
FOLLOW_ALIASES, "false",
CoreAdminParams.DELETE_METRICS_HISTORY, "true"
);
ocmh.commandMap.get(CollectionParams.CollectionAction.DELETE).call(clusterState, cmd, cmdResults);
@ -782,6 +793,7 @@ public class ReindexCollectionCmd implements OverseerCollectionMessageHandler.Cm
ZkNodeProps cmd = new ZkNodeProps(
Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.DELETE.toLower(),
CommonParams.NAME, chkCollection,
FOLLOW_ALIASES, "false",
CoreAdminParams.DELETE_METRICS_HISTORY, "true"
);
cmdResults = new NamedList<>();

View File

@ -29,6 +29,8 @@ import org.apache.solr.common.util.NamedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
/**
*
*/
@ -55,7 +57,13 @@ public class RenameCmd implements OverseerCollectionMessageHandler.Cmd {
}
Aliases aliases = ocmh.zkStateReader.getAliases();
String collectionName = aliases.resolveSimpleAlias(extCollectionName);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collectionName;
if (followAliases) {
collectionName = aliases.resolveSimpleAlias(extCollectionName);
} else {
collectionName = extCollectionName;
}
if (!state.hasCollection(collectionName)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "source collection '" + collectionName + "' not found.");
}
@ -65,6 +73,5 @@ public class RenameCmd implements OverseerCollectionMessageHandler.Cmd {
}
ocmh.zkStateReader.aliasesManager.applyModificationAndExportToZk(a -> a.cloneWithRename(extCollectionName, target));
}
}

View File

@ -73,6 +73,7 @@ import org.slf4j.LoggerFactory;
import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.REPLICA_TYPE;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATESHARD;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETESHARD;
@ -111,7 +112,13 @@ public class SplitShardCmd implements OverseerCollectionMessageHandler.Cmd {
String extCollectionName = message.getStr(CoreAdminParams.COLLECTION);
String collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String collectionName;
if (followAliases) {
collectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extCollectionName);
} else {
collectionName = extCollectionName;
}
log.debug("Split shard invoked: {}", message);
ZkStateReader zkStateReader = ocmh.zkStateReader;

View File

@ -141,6 +141,7 @@ import static org.apache.solr.common.params.CollectionAdminParams.ALIAS;
import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
import static org.apache.solr.common.params.CollectionAdminParams.COLL_CONF;
import static org.apache.solr.common.params.CollectionAdminParams.COUNT_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CollectionAdminParams.PROPERTY_NAME;
import static org.apache.solr.common.params.CollectionAdminParams.PROPERTY_VALUE;
import static org.apache.solr.common.params.CollectionAdminParams.WITH_COLLECTION;
@ -544,11 +545,20 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
.getColStatus(rsp.getValues());
return null;
}),
DELETE_OP(DELETE, (req, rsp, h) -> copy(req.getParams().required(), null, NAME)),
DELETE_OP(DELETE, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null, NAME);
return copy(req.getParams(), map, FOLLOW_ALIASES);
}),
// XXX should this command support followAliases?
RELOAD_OP(RELOAD, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null, NAME);
return copy(req.getParams(), map);
}),
RELOAD_OP(RELOAD, (req, rsp, h) -> copy(req.getParams().required(), null, NAME)),
RENAME_OP(RENAME, (req, rsp, h) -> copy(req.getParams().required(), null, NAME, CollectionAdminParams.TARGET)),
RENAME_OP(RENAME, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null, NAME, CollectionAdminParams.TARGET);
return copy(req.getParams(), map, FOLLOW_ALIASES);
}),
REINDEXCOLLECTION_OP(REINDEXCOLLECTION, (req, rsp, h) -> {
Map<String, Object> m = copy(req.getParams().required(), null, NAME);
@ -571,7 +581,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
STATE_FORMAT,
CommonParams.ROWS,
CommonParams.Q,
CommonParams.FL);
CommonParams.FL,
FOLLOW_ALIASES);
if (req.getParams().get("collection." + ZkStateReader.CONFIGNAME_PROP) != null) {
m.put(ZkStateReader.CONFIGNAME_PROP, req.getParams().get("collection." + ZkStateReader.CONFIGNAME_PROP));
}
@ -748,7 +759,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
TIMING,
SPLIT_METHOD,
NUM_SUB_SHARDS,
SPLIT_FUZZ);
SPLIT_FUZZ,
FOLLOW_ALIASES);
return copyPropertiesWithPrefix(req.getParams(), map, COLL_PROP_PREFIX);
}),
DELETESHARD_OP(DELETESHARD, (req, rsp, h) -> {
@ -759,7 +771,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
DELETE_INDEX,
DELETE_DATA_DIR,
DELETE_INSTANCE_DIR,
DELETE_METRICS_HISTORY);
DELETE_METRICS_HISTORY,
FOLLOW_ALIASES);
return map;
}),
FORCELEADER_OP(FORCELEADER, (req, rsp, h) -> {
@ -772,7 +785,11 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
SHARD_ID_PROP);
ClusterState clusterState = h.coreContainer.getZkController().getClusterState();
final String newShardName = SolrIdentifierValidator.validateShardName(req.getParams().get(SHARD_ID_PROP));
if (!ImplicitDocRouter.NAME.equals(((Map) clusterState.getCollection(req.getParams().get(COLLECTION_PROP)).get(DOC_ROUTER)).get(NAME)))
boolean followAliases = req.getParams().getBool(FOLLOW_ALIASES, false);
String extCollectionName = req.getParams().get(COLLECTION_PROP);
String collectionName = followAliases ? h.coreContainer.getZkController().getZkStateReader()
.getAliases().resolveSimpleAlias(extCollectionName) : extCollectionName;
if (!ImplicitDocRouter.NAME.equals(((Map) clusterState.getCollection(collectionName).get(DOC_ROUTER)).get(NAME)))
throw new SolrException(ErrorCode.BAD_REQUEST, "shards can be added only to 'implicit' collections");
copy(req.getParams(), map,
REPLICATION_FACTOR,
@ -780,7 +797,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
TLOG_REPLICAS,
PULL_REPLICAS,
CREATE_NODE_SET,
WAIT_FOR_FINAL_STATE);
WAIT_FOR_FINAL_STATE,
FOLLOW_ALIASES);
return copyPropertiesWithPrefix(req.getParams(), map, COLL_PROP_PREFIX);
}),
DELETEREPLICA_OP(DELETEREPLICA, (req, rsp, h) -> {
@ -794,11 +812,12 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
DELETE_METRICS_HISTORY,
COUNT_PROP, REPLICA_PROP,
SHARD_ID_PROP,
ONLY_IF_DOWN);
ONLY_IF_DOWN,
FOLLOW_ALIASES);
}),
MIGRATE_OP(MIGRATE, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null, COLLECTION_PROP, "split.key", "target.collection");
return copy(req.getParams(), map, "forward.timeout");
return copy(req.getParams(), map, "forward.timeout", FOLLOW_ALIASES);
}),
ADDROLE_OP(ADDROLE, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null, "role", "node");
@ -918,7 +937,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
NRT_REPLICAS,
TLOG_REPLICAS,
PULL_REPLICAS,
CREATE_NODE_SET);
CREATE_NODE_SET,
FOLLOW_ALIASES);
return copyPropertiesWithPrefix(req.getParams(), props, COLL_PROP_PREFIX);
}),
OVERSEERSTATUS_OP(OVERSEERSTATUS, (req, rsp, h) -> (Map) new LinkedHashMap<>()),
@ -980,6 +1000,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
}
return map;
}),
// XXX should this command support followAliases?
DELETEREPLICAPROP_OP(DELETEREPLICAPROP, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null,
COLLECTION_PROP,
@ -988,6 +1009,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
REPLICA_PROP);
return copy(req.getParams(), map, PROPERTY_PROP);
}),
// XXX should this command support followAliases?
BALANCESHARDUNIQUE_OP(BALANCESHARDUNIQUE, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null,
COLLECTION_PROP,
@ -1010,6 +1032,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
new RebalanceLeaders(req, rsp, h).execute();
return null;
}),
// XXX should this command support followAliases?
MODIFYCOLLECTION_OP(MODIFYCOLLECTION, (req, rsp, h) -> {
Map<String, Object> m = copy(req.getParams(), null, CollectionAdminRequest.MODIFIABLE_COLLECTION_PROPERTIES);
copyPropertiesWithPrefix(req.getParams(), m, COLL_PROP_PREFIX);
@ -1039,8 +1062,9 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
req.getParams().required().check(NAME, COLLECTION_PROP);
String extCollectionName = req.getParams().get(COLLECTION_PROP);
String collectionName = h.coreContainer.getZkController().getZkStateReader()
.getAliases().resolveSimpleAlias(extCollectionName);
boolean followAliases = req.getParams().getBool(FOLLOW_ALIASES, false);
String collectionName = followAliases ? h.coreContainer.getZkController().getZkStateReader()
.getAliases().resolveSimpleAlias(extCollectionName) : extCollectionName;
ClusterState clusterState = h.coreContainer.getZkController().getClusterState();
if (!clusterState.hasCollection(collectionName)) {
throw new SolrException(ErrorCode.BAD_REQUEST, "Collection '" + collectionName + "' does not exist, no action taken.");
@ -1076,7 +1100,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
throw new SolrException(ErrorCode.BAD_REQUEST, "Unknown index backup strategy " + strategy);
}
Map<String, Object> params = copy(req.getParams(), null, NAME, COLLECTION_PROP, CoreAdminParams.COMMIT_NAME);
Map<String, Object> params = copy(req.getParams(), null, NAME, COLLECTION_PROP, FOLLOW_ALIASES, CoreAdminParams.COMMIT_NAME);
params.put(CoreAdminParams.BACKUP_LOCATION, location);
params.put(CollectionAdminParams.INDEX_BACKUP_STRATEGY, strategy);
return params;
@ -1143,8 +1167,9 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
req.getParams().required().check(COLLECTION_PROP, CoreAdminParams.COMMIT_NAME);
String extCollectionName = req.getParams().get(COLLECTION_PROP);
String collectionName = h.coreContainer.getZkController().getZkStateReader()
.getAliases().resolveSimpleAlias(extCollectionName);
boolean followAliases = req.getParams().getBool(FOLLOW_ALIASES, false);
String collectionName = followAliases ? h.coreContainer.getZkController().getZkStateReader()
.getAliases().resolveSimpleAlias(extCollectionName) : extCollectionName;
String commitName = req.getParams().get(CoreAdminParams.COMMIT_NAME);
ClusterState clusterState = h.coreContainer.getZkController().getClusterState();
if (!clusterState.hasCollection(collectionName)) {
@ -1158,7 +1183,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
+ collectionName + "', no action taken.");
}
Map<String, Object> params = copy(req.getParams(), null, COLLECTION_PROP, CoreAdminParams.COMMIT_NAME);
Map<String, Object> params = copy(req.getParams(), null, COLLECTION_PROP, FOLLOW_ALIASES, CoreAdminParams.COMMIT_NAME);
return params;
}),
DELETESNAPSHOT_OP(DELETESNAPSHOT, (req, rsp, h) -> {
@ -1172,7 +1197,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
throw new SolrException(ErrorCode.BAD_REQUEST, "Collection '" + collectionName + "' does not exist, no action taken.");
}
Map<String, Object> params = copy(req.getParams(), null, COLLECTION_PROP, CoreAdminParams.COMMIT_NAME);
Map<String, Object> params = copy(req.getParams(), null, COLLECTION_PROP, FOLLOW_ALIASES, CoreAdminParams.COMMIT_NAME);
return params;
}),
LISTSNAPSHOTS_OP(LISTSNAPSHOTS, (req, rsp, h) -> {
@ -1215,7 +1240,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
WAIT_FOR_FINAL_STATE,
IN_PLACE_MOVE,
"replica",
"shard");
"shard",
FOLLOW_ALIASES);
}),
DELETENODE_OP(DELETENODE, (req, rsp, h) -> copy(req.getParams().required(), null, "node"));

View File

@ -53,9 +53,11 @@ import org.apache.solr.client.solrj.response.CollectionAdminResponse;
import org.apache.solr.client.solrj.response.CoreAdminResponse;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.V2Response;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.cloud.Aliases;
import org.apache.solr.common.cloud.ClusterProperties;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice;
@ -794,8 +796,20 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
@Test
public void testRenameCollection() throws Exception {
String collectionName1 = "testRename_collection1";
String collectionName2 = "testRename_collection2";
doTestRenameCollection(true);
CollectionAdminRequest.deleteAlias("col1").process(cluster.getSolrClient());
CollectionAdminRequest.deleteAlias("col2").process(cluster.getSolrClient());
CollectionAdminRequest.deleteAlias("foo").process(cluster.getSolrClient());
CollectionAdminRequest.deleteAlias("simpleAlias").process(cluster.getSolrClient());
CollectionAdminRequest.deleteAlias("catAlias").process(cluster.getSolrClient());
CollectionAdminRequest.deleteAlias("compoundAlias").process(cluster.getSolrClient());
cluster.getSolrClient().getZkStateReader().aliasesManager.update();
doTestRenameCollection(false);
}
private void doTestRenameCollection(boolean followAliases) throws Exception {
String collectionName1 = "testRename1_" + followAliases;
String collectionName2 = "testRename2_" + followAliases;
CollectionAdminRequest.createCollection(collectionName1, "conf", 1, 1).setAlias("col1").process(cluster.getSolrClient());
CollectionAdminRequest.createCollection(collectionName2, "conf", 1, 1).setAlias("col2").process(cluster.getSolrClient());
@ -810,45 +824,159 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
CollectionAdminRequest.createCategoryRoutedAlias("catAlias", "field1", 100,
CollectionAdminRequest.createCollection("_unused_", "conf", 1, 1)).process(cluster.getSolrClient());
CollectionAdminRequest.renameCollection("col1", "foo").process(cluster.getSolrClient());
CollectionAdminRequest.Rename rename = CollectionAdminRequest.renameCollection("col1", "foo");
rename.setFollowAliases(followAliases);
ZkStateReader zkStateReader = cluster.getSolrClient().getZkStateReader();
zkStateReader.aliasesManager.update();
Aliases aliases;
if (!followAliases) {
try {
rename.process(cluster.getSolrClient());
} catch (Exception e) {
assertTrue(e.toString(), e.toString().contains("source collection 'col1' not found"));
}
} else {
rename.process(cluster.getSolrClient());
zkStateReader.aliasesManager.update();
Aliases aliases = zkStateReader.getAliases();
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName1, aliases.resolveSimpleAlias("foo"));
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName1, aliases.resolveSimpleAlias("simpleAlias"));
List<String> compoundAliases = aliases.resolveAliases("compoundAlias");
assertEquals(compoundAliases.toString(), 2, compoundAliases.size());
assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName1));
assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName2));
aliases = zkStateReader.getAliases();
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName1, aliases.resolveSimpleAlias("foo"));
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName1, aliases.resolveSimpleAlias("simpleAlias"));
List<String> compoundAliases = aliases.resolveAliases("compoundAlias");
assertEquals(compoundAliases.toString(), 2, compoundAliases.size());
assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName1));
assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName2));
}
CollectionAdminRequest.renameCollection(collectionName1, collectionName2).process(cluster.getSolrClient());
zkStateReader.aliasesManager.update();
aliases = zkStateReader.getAliases();
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName2, aliases.resolveSimpleAlias("foo"));
if (followAliases) {
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName2, aliases.resolveSimpleAlias("foo"));
}
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName2, aliases.resolveSimpleAlias("simpleAlias"));
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName2, aliases.resolveSimpleAlias(collectionName1));
// we renamed col1 -> col2 so the compound alias contains only "col2,col2" which is reduced to col2
compoundAliases = aliases.resolveAliases("compoundAlias");
List<String> compoundAliases = aliases.resolveAliases("compoundAlias");
assertEquals(compoundAliases.toString(), 1, compoundAliases.size());
assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName2));
try {
CollectionAdminRequest.renameCollection("catAlias", "bar").process(cluster.getSolrClient());
rename = CollectionAdminRequest.renameCollection("catAlias", "bar");
rename.setFollowAliases(followAliases);
rename.process(cluster.getSolrClient());
fail("category-based alias renaming should fail");
} catch (Exception e) {
assertTrue(e.toString().contains("is a routed alias"));
if (followAliases) {
assertTrue(e.toString(), e.toString().contains("is a routed alias"));
} else {
assertTrue(e.toString(), e.toString().contains("source collection 'catAlias' not found"));
}
}
try {
CollectionAdminRequest.renameCollection("col2", "foo").process(cluster.getSolrClient());
fail("shuold fail because 'foo' already exists");
rename = CollectionAdminRequest.renameCollection("col2", "foo");
rename.setFollowAliases(followAliases);
rename.process(cluster.getSolrClient());
fail("should fail because 'foo' already exists");
} catch (Exception e) {
assertTrue(e.toString().contains("exists"));
if (followAliases) {
assertTrue(e.toString(), e.toString().contains("exists"));
} else {
assertTrue(e.toString(), e.toString().contains("source collection 'col2' not found"));
}
}
}
@Test
public void testDeleteAliasedCollection() throws Exception {
CloudSolrClient solrClient = cluster.getSolrClient();
String collectionName1 = "aliasedCollection1";
String collectionName2 = "aliasedCollection2";
CollectionAdminRequest.createCollection(collectionName1, "conf", 1, 1).process(solrClient);
CollectionAdminRequest.createCollection(collectionName2, "conf", 1, 1).process(solrClient);
cluster.waitForActiveCollection(collectionName1, 1, 1);
cluster.waitForActiveCollection(collectionName2, 1, 1);
waitForState("Expected collection1 to be created with 1 shard and 1 replica", collectionName1, clusterShape(1, 1));
waitForState("Expected collection2 to be created with 1 shard and 1 replica", collectionName2, clusterShape(1, 1));
SolrInputDocument doc = new SolrInputDocument("id", "1");
solrClient.add(collectionName1, doc);
doc = new SolrInputDocument("id", "2");
solrClient.add(collectionName2, doc);
solrClient.commit(collectionName1);
solrClient.commit(collectionName2);
assertDoc(solrClient, collectionName1, "1");
assertDoc(solrClient, collectionName2, "2");
CollectionAdminRequest.createAlias(collectionName1, collectionName2).process(solrClient);
RetryUtil.retryUntil("didn't get the new aliases", 10, 1000, TimeUnit.MILLISECONDS, () -> {
try {
solrClient.getZkStateReader().aliasesManager.update();
return solrClient.getZkStateReader().getAliases()
.resolveSimpleAlias(collectionName1).equals(collectionName2);
} catch (Exception e) {
fail("exception caught refreshing aliases: " + e);
return false;
}
});
// both results should come from collection 2
assertDoc(solrClient, collectionName1, "2"); // aliased
assertDoc(solrClient, collectionName2, "2"); // direct
// should be able to remove collection 1 when followAliases = false
CollectionAdminRequest.Delete delete = CollectionAdminRequest.deleteCollection(collectionName1);
delete.setFollowAliases(false);
delete.process(solrClient);
ClusterState state = solrClient.getClusterStateProvider().getClusterState();
assertFalse(state.getCollectionsMap().toString(), state.hasCollection(collectionName1));
// search should still work, returning results from collection 2
assertDoc(solrClient, collectionName1, "2"); // aliased
assertDoc(solrClient, collectionName2, "2"); // direct
// without aliases this collection doesn't exist anymore
delete = CollectionAdminRequest.deleteCollection(collectionName1);
delete.setFollowAliases(false);
try {
delete.process(solrClient);
fail("delete of nonexistent collection 1 should have failed when followAliases=false");
} catch (Exception e) {
assertTrue(e.toString(), e.toString().contains(collectionName1));
}
// with followAliases=true collection 2 (and the alias) should both be removed
delete.setFollowAliases(true);
delete.process(solrClient);
state = solrClient.getClusterStateProvider().getClusterState();
// the collection is gone
assertFalse(state.getCollectionsMap().toString(), state.hasCollection(collectionName2));
// and the alias is gone
RetryUtil.retryUntil("didn't get the new aliases", 10, 1000, TimeUnit.MILLISECONDS, () -> {
try {
solrClient.getZkStateReader().aliasesManager.update();
return !solrClient.getZkStateReader().getAliases().hasAlias(collectionName1);
} catch (Exception e) {
fail("exception caught refreshing aliases: " + e);
return false;
}
});
}
private void assertDoc(CloudSolrClient solrClient, String collection, String id) throws Exception {
QueryResponse rsp = solrClient.query(collection, params(CommonParams.Q, "*:*"));
assertEquals(rsp.toString(), 1, rsp.getResults().getNumFound());
SolrDocument sdoc = rsp.getResults().get(0);
assertEquals(sdoc.toString(), id, sdoc.getFieldValue("id"));
}
@Test
public void testOverseerStatus() throws IOException, SolrServerException {
CollectionAdminResponse response = new CollectionAdminRequest.OverseerStatus().process(cluster.getSolrClient());

View File

@ -150,8 +150,16 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
assertEquals("copied num docs", NUM_DOCS, queryResponse.getResults().getNumFound());
}
@Test
public void testSameTargetReindexing() throws Exception {
final String sourceCollection = "sameTargetReindexing";
doTestSameTargetReindexing(false, false);
doTestSameTargetReindexing(false, true);
doTestSameTargetReindexing(true, false);
doTestSameTargetReindexing(true, true);
}
private void doTestSameTargetReindexing(boolean sourceRemove, boolean followAliases) throws Exception {
final String sourceCollection = "sameTargetReindexing_" + sourceRemove + "_" + followAliases;
final String targetCollection = sourceCollection;
createCollection(sourceCollection, "conf1", 2, 2);
@ -160,6 +168,8 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
CollectionAdminRequest.ReindexCollection req = CollectionAdminRequest.reindexCollection(sourceCollection)
.setTarget(targetCollection);
req.setRemoveSource(sourceRemove);
req.setFollowAliases(followAliases);
req.process(solrClient);
String realTargetCollection = null;
@ -183,11 +193,16 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
ReindexCollectionCmd.State state = ReindexCollectionCmd.State.get(coll.getStr(ReindexCollectionCmd.REINDEXING_STATE));
return ReindexCollectionCmd.State.FINISHED == state;
});
solrClient.getZkStateReader().aliasesManager.update();
SolrTestCaseJ4.Solr11035BandAid(solrClient, targetCollection, "id", NUM_DOCS, "*:*",
"ReindexCollectionTest.testSameTargetReindex", false);
// verify the target docs exist
QueryResponse rsp = solrClient.query(targetCollection, params(CommonParams.Q, "*:*"));
assertEquals("copied num docs", NUM_DOCS, rsp.getResults().getNumFound());
ClusterState state = solrClient.getClusterStateProvider().getClusterState();
if (sourceRemove) {
assertFalse("source collection still present", state.hasCollection(sourceCollection));
}
}
@Test

View File

@ -282,9 +282,15 @@ As always, patches and pull requests are welcome!
Starting with version 8.1 SolrCloud supports using alias names in collection commands where normally a
collection name is expected. This works only when the following criteria are satisfied:
* a request parameter `followAliases=true` is used
* an alias must not refer to more than one collection
* an alias must not refer to a <<Routed Aliases,Routed Alias>>
If all criteria are satisfied then the command will resolve alias names and operate on the collections the aliases
If all criteria are satisfied then the command will resolve all alias names and operate on the collections the aliases
refer to as if it was invoked with the collection names instead. Otherwise the command will not be executed and
an exception will be thrown.
The `followAliases=true` parameter should be used with care so that the resolved targets are indeed the intended ones.
In case of multi-level aliases or shadow aliases (an alias with the same name as an existing collection but pointing
to other collections) the use of this option is strongly discouraged because effects may be difficult to
predict correctly.

View File

@ -246,6 +246,7 @@ public abstract class CollectionAdminRequest<T extends CollectionAdminResponse>
protected abstract static class AsyncCollectionSpecificAdminRequest extends AsyncCollectionAdminRequest {
protected String collection;
protected Boolean followAliases;
public AsyncCollectionSpecificAdminRequest(CollectionAction action, String collection) {
super(action);
@ -256,10 +257,15 @@ public abstract class CollectionAdminRequest<T extends CollectionAdminResponse>
return collection;
}
public void setFollowAliases(Boolean followAliases) {
this.followAliases = followAliases;
}
@Override
public SolrParams getParams() {
ModifiableSolrParams params = new ModifiableSolrParams(super.getParams());
params.set(CoreAdminParams.NAME, collection);
params.setNonNull(CollectionAdminParams.FOLLOW_ALIASES, followAliases);
return params;
}
}

View File

@ -124,4 +124,7 @@ public interface CollectionAdminParams {
* Prefix for {@link org.apache.solr.common.cloud.DocRouter} properties
*/
String ROUTER_PREFIX = "router.";
/** Option to follow aliases when deciding the target of a collection admin command. */
String FOLLOW_ALIASES = "followAliases";
}