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-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 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.SHARD_ID_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.TLOG_REPLICAS; 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.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.CollectionAdminParams.WITH_COLLECTION;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA; import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC; 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)); log.debug("addReplica() : {}", Utils.toJSONString(message));
String extCollectionName = message.getStr(COLLECTION_PROP); String extCollectionName = message.getStr(COLLECTION_PROP);
boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String shard = message.getStr(SHARD_ID_PROP); 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); DocCollection coll = clusterState.getCollection(collectionName);
if (coll == null) { 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.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_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.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonParams.NAME; import static org.apache.solr.common.params.CommonParams.NAME;
@ -67,7 +68,13 @@ public class BackupCmd implements OverseerCollectionMessageHandler.Cmd {
@Override @Override
public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception { public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception {
String extCollectionName = message.getStr(COLLECTION_PROP); 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 backupName = message.getStr(NAME);
String repo = message.getStr(CoreAdminParams.BACKUP_REPOSITORY); 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 boolean waitForFinalState = message.getBool(WAIT_FOR_FINAL_STATE, false);
final String alias = message.getStr(ALIAS, collectionName); final String alias = message.getStr(ALIAS, collectionName);
log.info("Create collection {}", 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); throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "collection already exists: " + collectionName);
} }
if (aliases.hasAlias(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.REPLICATION_FACTOR;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP; 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.cloud.ZkStateReader.TLOG_REPLICAS;
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.ASYNC;
public class CreateShardCmd implements OverseerCollectionMessageHandler.Cmd { public class CreateShardCmd implements OverseerCollectionMessageHandler.Cmd {
@ -59,7 +60,13 @@ public class CreateShardCmd implements OverseerCollectionMessageHandler.Cmd {
if (extCollectionName == null || sliceName == null) if (extCollectionName == null || sliceName == null)
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "'collection' and 'shard' are required parameters"); 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); DocCollection collection = clusterState.getCollection(collectionName);
int numNrtReplicas = message.getInt(NRT_REPLICAS, message.getInt(REPLICATION_FACTOR, collection.getInt(NRT_REPLICAS, collection.getInt(REPLICATION_FACTOR, 1)))); 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.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_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.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonParams.NAME; import static org.apache.solr.common.params.CommonParams.NAME;
@ -66,7 +67,14 @@ public class CreateSnapshotCmd implements OverseerCollectionMessageHandler.Cmd {
@Override @Override
public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception { public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception {
String extCollectionName = message.getStr(COLLECTION_PROP); 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 commitName = message.getStr(CoreAdminParams.COMMIT_NAME);
String asyncId = message.getStr(ASYNC); String asyncId = message.getStr(ASYNC);

View File

@ -19,6 +19,7 @@
package org.apache.solr.cloud.api.collections; 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.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.CollectionAdminParams.WITH_COLLECTION;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETE;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC; 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 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(); Aliases aliases = zkStateReader.getAliases();
String collection = aliases.resolveSimpleAlias(extCollection);
String collection;
if (followAliases) {
collection = aliases.resolveSimpleAlias(extCollection);
} else {
collection = extCollection;
}
checkNotColocatedWith(zkStateReader, collection); checkNotColocatedWith(zkStateReader, collection);
@ -118,8 +126,9 @@ public class DeleteCollectionCmd implements OverseerCollectionMessageHandler.Cmd
Set<String> okayExceptions = new HashSet<>(1); Set<String> okayExceptions = new HashSet<>(1);
okayExceptions.add(NonExistentCoreException.class.getName()); 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) { for (Replica failedReplica : failedReplicas) {
boolean isSharedFS = failedReplica.getBool(ZkStateReader.SHARED_STORAGE_PROP, false) && failedReplica.get("dataDir") != null; boolean isSharedFS = failedReplica.getBool(ZkStateReader.SHARED_STORAGE_PROP, false) && failedReplica.get("dataDir") != null;
if (isSharedFS) { 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 // 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(); Aliases aliases = zkStateReader.getAliases();
List<String> aliasesRefs = referencedByAlias(extCollection, aliases); List<String> aliasesRefs = referencedByAlias(extCollection, aliases, followAliases);
List<String> aliasesToDelete = new ArrayList<>(); List<String> aliasesToDelete = new ArrayList<>();
if (aliasesRefs.size() > 0) { if (aliasesRefs.size() > 0) {
zkStateReader.aliasesManager.update(); // aliases may have been stale; get latest from ZK zkStateReader.aliasesManager.update(); // aliases may have been stale; get latest from ZK
aliases = zkStateReader.getAliases(); aliases = zkStateReader.getAliases();
aliasesRefs = referencedByAlias(extCollection, aliases); aliasesRefs = referencedByAlias(extCollection, aliases, followAliases);
if (aliasesRefs.size() > 0) { if (aliasesRefs.size() > 0) {
for (String alias : aliasesRefs) { for (String alias : aliasesRefs) {
// for back-compat in 8.x we don't automatically remove other // for back-compat in 8.x we don't automatically remove other
@ -209,11 +218,12 @@ public class DeleteCollectionCmd implements OverseerCollectionMessageHandler.Cmd
return aliasesToDelete; 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); Objects.requireNonNull(aliases);
// this quickly produces error if the name is a complex alias // 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() return aliases.getCollectionAliasListMap().entrySet().stream()
.filter(e -> !e.getKey().equals(collection))
.filter(e -> e.getValue().contains(collection) || e.getValue().contains(extCollection)) .filter(e -> e.getValue().contains(collection) || e.getValue().contains(extCollection))
.map(Map.Entry::getKey) // alias name .map(Map.Entry::getKey) // alias name
.collect(Collectors.toList()); .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.REPLICA_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_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.COUNT_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.ASYNC;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -85,7 +86,13 @@ public class DeleteReplicaCmd implements Cmd {
String shard = message.getStr(SHARD_ID_PROP); String shard = message.getStr(SHARD_ID_PROP);
String replicaName = message.getStr(REPLICA_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); DocCollection coll = clusterState.getCollection(collectionName);
Slice slice = coll.getSlice(shard); 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.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.NODE_NAME_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.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.DELETEREPLICA;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETESHARD; import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETESHARD;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC; 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 extCollectionName = message.getStr(ZkStateReader.COLLECTION_PROP);
String sliceId = message.getStr(ZkStateReader.SHARD_ID_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"); log.info("Delete shard invoked");
Slice slice = clusterState.getCollection(collectionName).getSlice(sliceId); 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.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_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.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonParams.NAME; import static org.apache.solr.common.params.CommonParams.NAME;
@ -64,7 +65,13 @@ public class DeleteSnapshotCmd implements OverseerCollectionMessageHandler.Cmd {
@Override @Override
public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception { public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception {
String extCollectionName = message.getStr(COLLECTION_PROP); 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 commitName = message.getStr(CoreAdminParams.COMMIT_NAME);
String asyncId = message.getStr(ASYNC); String asyncId = message.getStr(ASYNC);
NamedList shardRequestResults = new NamedList(); 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.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS; 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.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.ADDREPLICA;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATE;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETE; 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"); String extTargetCollectionName = message.getStr("target.collection");
int timeout = message.getInt("forward.timeout", 10 * 60) * 1000; int timeout = message.getInt("forward.timeout", 10 * 60) * 1000;
String sourceCollectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extSourceCollectionName); boolean followAliases = message.getBool(FOLLOW_ALIASES, false);
String targetCollectionName = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(extTargetCollectionName); 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); DocCollection sourceCollection = clusterState.getCollection(sourceCollectionName);
if (sourceCollection == null) { 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.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.REPLICA_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.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.ASYNC;
import static org.apache.solr.common.params.CommonAdminParams.IN_PLACE_MOVE; import static org.apache.solr.common.params.CommonAdminParams.IN_PLACE_MOVE;
import static org.apache.solr.common.params.CommonAdminParams.TIMEOUT; 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 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); DocCollection coll = clusterState.getCollection(collection);
if (coll == null) { if (coll == null) {

View File

@ -64,6 +64,8 @@ import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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. * 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 * <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) { if (extCollection == null) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Source collection name must be specified"); 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)) { if (!clusterState.hasCollection(collection)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Source collection name must exist"); 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) { if (target == null) {
target = collection; target = collection;
} else { } else {
// resolve aliases if (followAliases) {
target = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(target); target = ocmh.cloudManager.getClusterStateProvider().resolveSimpleAlias(target);
}
} }
boolean sameTarget = target.equals(collection) || target.equals(extCollection); boolean sameTarget = target.equals(collection) || target.equals(extCollection);
boolean removeSource = message.getBool(REMOVE_SOURCE, false); boolean removeSource = message.getBool(REMOVE_SOURCE, false);
@ -466,6 +475,7 @@ public class ReindexCollectionCmd implements OverseerCollectionMessageHandler.Cm
cmd = new ZkNodeProps( cmd = new ZkNodeProps(
Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.DELETE.toLower(), Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.DELETE.toLower(),
CommonParams.NAME, collection, CommonParams.NAME, collection,
FOLLOW_ALIASES, "false",
CoreAdminParams.DELETE_METRICS_HISTORY, "true" CoreAdminParams.DELETE_METRICS_HISTORY, "true"
); );
cmdResults = new NamedList<>(); cmdResults = new NamedList<>();
@ -770,6 +780,7 @@ public class ReindexCollectionCmd implements OverseerCollectionMessageHandler.Cm
ZkNodeProps cmd = new ZkNodeProps( ZkNodeProps cmd = new ZkNodeProps(
Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.DELETE.toLower(), Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.DELETE.toLower(),
CommonParams.NAME, targetCollection, CommonParams.NAME, targetCollection,
FOLLOW_ALIASES, "false",
CoreAdminParams.DELETE_METRICS_HISTORY, "true" CoreAdminParams.DELETE_METRICS_HISTORY, "true"
); );
ocmh.commandMap.get(CollectionParams.CollectionAction.DELETE).call(clusterState, cmd, cmdResults); ocmh.commandMap.get(CollectionParams.CollectionAction.DELETE).call(clusterState, cmd, cmdResults);
@ -782,6 +793,7 @@ public class ReindexCollectionCmd implements OverseerCollectionMessageHandler.Cm
ZkNodeProps cmd = new ZkNodeProps( ZkNodeProps cmd = new ZkNodeProps(
Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.DELETE.toLower(), Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.DELETE.toLower(),
CommonParams.NAME, chkCollection, CommonParams.NAME, chkCollection,
FOLLOW_ALIASES, "false",
CoreAdminParams.DELETE_METRICS_HISTORY, "true" CoreAdminParams.DELETE_METRICS_HISTORY, "true"
); );
cmdResults = new NamedList<>(); cmdResults = new NamedList<>();

View File

@ -29,6 +29,8 @@ import org.apache.solr.common.util.NamedList;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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(); 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)) { if (!state.hasCollection(collectionName)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "source collection '" + collectionName + "' not found."); 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)); 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.COLLECTION_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.REPLICA_TYPE; 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.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.ADDREPLICA;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATESHARD; import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATESHARD;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETESHARD; 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 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); log.debug("Split shard invoked: {}", message);
ZkStateReader zkStateReader = ocmh.zkStateReader; 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.COLLECTION;
import static org.apache.solr.common.params.CollectionAdminParams.COLL_CONF; 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.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_NAME;
import static org.apache.solr.common.params.CollectionAdminParams.PROPERTY_VALUE; import static org.apache.solr.common.params.CollectionAdminParams.PROPERTY_VALUE;
import static org.apache.solr.common.params.CollectionAdminParams.WITH_COLLECTION; import static org.apache.solr.common.params.CollectionAdminParams.WITH_COLLECTION;
@ -544,11 +545,20 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
.getColStatus(rsp.getValues()); .getColStatus(rsp.getValues());
return null; 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) -> {
Map<String, Object> map = copy(req.getParams().required(), null, NAME, CollectionAdminParams.TARGET);
RENAME_OP(RENAME, (req, rsp, h) -> copy(req.getParams().required(), null, NAME, CollectionAdminParams.TARGET)), return copy(req.getParams(), map, FOLLOW_ALIASES);
}),
REINDEXCOLLECTION_OP(REINDEXCOLLECTION, (req, rsp, h) -> { REINDEXCOLLECTION_OP(REINDEXCOLLECTION, (req, rsp, h) -> {
Map<String, Object> m = copy(req.getParams().required(), null, NAME); Map<String, Object> m = copy(req.getParams().required(), null, NAME);
@ -571,7 +581,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
STATE_FORMAT, STATE_FORMAT,
CommonParams.ROWS, CommonParams.ROWS,
CommonParams.Q, CommonParams.Q,
CommonParams.FL); CommonParams.FL,
FOLLOW_ALIASES);
if (req.getParams().get("collection." + ZkStateReader.CONFIGNAME_PROP) != null) { if (req.getParams().get("collection." + ZkStateReader.CONFIGNAME_PROP) != null) {
m.put(ZkStateReader.CONFIGNAME_PROP, req.getParams().get("collection." + ZkStateReader.CONFIGNAME_PROP)); m.put(ZkStateReader.CONFIGNAME_PROP, req.getParams().get("collection." + ZkStateReader.CONFIGNAME_PROP));
} }
@ -748,7 +759,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
TIMING, TIMING,
SPLIT_METHOD, SPLIT_METHOD,
NUM_SUB_SHARDS, NUM_SUB_SHARDS,
SPLIT_FUZZ); SPLIT_FUZZ,
FOLLOW_ALIASES);
return copyPropertiesWithPrefix(req.getParams(), map, COLL_PROP_PREFIX); return copyPropertiesWithPrefix(req.getParams(), map, COLL_PROP_PREFIX);
}), }),
DELETESHARD_OP(DELETESHARD, (req, rsp, h) -> { DELETESHARD_OP(DELETESHARD, (req, rsp, h) -> {
@ -759,7 +771,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
DELETE_INDEX, DELETE_INDEX,
DELETE_DATA_DIR, DELETE_DATA_DIR,
DELETE_INSTANCE_DIR, DELETE_INSTANCE_DIR,
DELETE_METRICS_HISTORY); DELETE_METRICS_HISTORY,
FOLLOW_ALIASES);
return map; return map;
}), }),
FORCELEADER_OP(FORCELEADER, (req, rsp, h) -> { FORCELEADER_OP(FORCELEADER, (req, rsp, h) -> {
@ -772,7 +785,11 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
SHARD_ID_PROP); SHARD_ID_PROP);
ClusterState clusterState = h.coreContainer.getZkController().getClusterState(); ClusterState clusterState = h.coreContainer.getZkController().getClusterState();
final String newShardName = SolrIdentifierValidator.validateShardName(req.getParams().get(SHARD_ID_PROP)); 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"); throw new SolrException(ErrorCode.BAD_REQUEST, "shards can be added only to 'implicit' collections");
copy(req.getParams(), map, copy(req.getParams(), map,
REPLICATION_FACTOR, REPLICATION_FACTOR,
@ -780,7 +797,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
TLOG_REPLICAS, TLOG_REPLICAS,
PULL_REPLICAS, PULL_REPLICAS,
CREATE_NODE_SET, CREATE_NODE_SET,
WAIT_FOR_FINAL_STATE); WAIT_FOR_FINAL_STATE,
FOLLOW_ALIASES);
return copyPropertiesWithPrefix(req.getParams(), map, COLL_PROP_PREFIX); return copyPropertiesWithPrefix(req.getParams(), map, COLL_PROP_PREFIX);
}), }),
DELETEREPLICA_OP(DELETEREPLICA, (req, rsp, h) -> { DELETEREPLICA_OP(DELETEREPLICA, (req, rsp, h) -> {
@ -794,11 +812,12 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
DELETE_METRICS_HISTORY, DELETE_METRICS_HISTORY,
COUNT_PROP, REPLICA_PROP, COUNT_PROP, REPLICA_PROP,
SHARD_ID_PROP, SHARD_ID_PROP,
ONLY_IF_DOWN); ONLY_IF_DOWN,
FOLLOW_ALIASES);
}), }),
MIGRATE_OP(MIGRATE, (req, rsp, h) -> { MIGRATE_OP(MIGRATE, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null, COLLECTION_PROP, "split.key", "target.collection"); 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) -> { ADDROLE_OP(ADDROLE, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null, "role", "node"); Map<String, Object> map = copy(req.getParams().required(), null, "role", "node");
@ -918,7 +937,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
NRT_REPLICAS, NRT_REPLICAS,
TLOG_REPLICAS, TLOG_REPLICAS,
PULL_REPLICAS, PULL_REPLICAS,
CREATE_NODE_SET); CREATE_NODE_SET,
FOLLOW_ALIASES);
return copyPropertiesWithPrefix(req.getParams(), props, COLL_PROP_PREFIX); return copyPropertiesWithPrefix(req.getParams(), props, COLL_PROP_PREFIX);
}), }),
OVERSEERSTATUS_OP(OVERSEERSTATUS, (req, rsp, h) -> (Map) new LinkedHashMap<>()), OVERSEERSTATUS_OP(OVERSEERSTATUS, (req, rsp, h) -> (Map) new LinkedHashMap<>()),
@ -980,6 +1000,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
} }
return map; return map;
}), }),
// XXX should this command support followAliases?
DELETEREPLICAPROP_OP(DELETEREPLICAPROP, (req, rsp, h) -> { DELETEREPLICAPROP_OP(DELETEREPLICAPROP, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null, Map<String, Object> map = copy(req.getParams().required(), null,
COLLECTION_PROP, COLLECTION_PROP,
@ -988,6 +1009,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
REPLICA_PROP); REPLICA_PROP);
return copy(req.getParams(), map, PROPERTY_PROP); return copy(req.getParams(), map, PROPERTY_PROP);
}), }),
// XXX should this command support followAliases?
BALANCESHARDUNIQUE_OP(BALANCESHARDUNIQUE, (req, rsp, h) -> { BALANCESHARDUNIQUE_OP(BALANCESHARDUNIQUE, (req, rsp, h) -> {
Map<String, Object> map = copy(req.getParams().required(), null, Map<String, Object> map = copy(req.getParams().required(), null,
COLLECTION_PROP, COLLECTION_PROP,
@ -1010,6 +1032,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
new RebalanceLeaders(req, rsp, h).execute(); new RebalanceLeaders(req, rsp, h).execute();
return null; return null;
}), }),
// XXX should this command support followAliases?
MODIFYCOLLECTION_OP(MODIFYCOLLECTION, (req, rsp, h) -> { MODIFYCOLLECTION_OP(MODIFYCOLLECTION, (req, rsp, h) -> {
Map<String, Object> m = copy(req.getParams(), null, CollectionAdminRequest.MODIFIABLE_COLLECTION_PROPERTIES); Map<String, Object> m = copy(req.getParams(), null, CollectionAdminRequest.MODIFIABLE_COLLECTION_PROPERTIES);
copyPropertiesWithPrefix(req.getParams(), m, COLL_PROP_PREFIX); 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); req.getParams().required().check(NAME, COLLECTION_PROP);
String extCollectionName = req.getParams().get(COLLECTION_PROP); String extCollectionName = req.getParams().get(COLLECTION_PROP);
String collectionName = h.coreContainer.getZkController().getZkStateReader() boolean followAliases = req.getParams().getBool(FOLLOW_ALIASES, false);
.getAliases().resolveSimpleAlias(extCollectionName); String collectionName = followAliases ? h.coreContainer.getZkController().getZkStateReader()
.getAliases().resolveSimpleAlias(extCollectionName) : extCollectionName;
ClusterState clusterState = h.coreContainer.getZkController().getClusterState(); ClusterState clusterState = h.coreContainer.getZkController().getClusterState();
if (!clusterState.hasCollection(collectionName)) { if (!clusterState.hasCollection(collectionName)) {
throw new SolrException(ErrorCode.BAD_REQUEST, "Collection '" + collectionName + "' does not exist, no action taken."); 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); 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(CoreAdminParams.BACKUP_LOCATION, location);
params.put(CollectionAdminParams.INDEX_BACKUP_STRATEGY, strategy); params.put(CollectionAdminParams.INDEX_BACKUP_STRATEGY, strategy);
return params; return params;
@ -1143,8 +1167,9 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
req.getParams().required().check(COLLECTION_PROP, CoreAdminParams.COMMIT_NAME); req.getParams().required().check(COLLECTION_PROP, CoreAdminParams.COMMIT_NAME);
String extCollectionName = req.getParams().get(COLLECTION_PROP); String extCollectionName = req.getParams().get(COLLECTION_PROP);
String collectionName = h.coreContainer.getZkController().getZkStateReader() boolean followAliases = req.getParams().getBool(FOLLOW_ALIASES, false);
.getAliases().resolveSimpleAlias(extCollectionName); String collectionName = followAliases ? h.coreContainer.getZkController().getZkStateReader()
.getAliases().resolveSimpleAlias(extCollectionName) : extCollectionName;
String commitName = req.getParams().get(CoreAdminParams.COMMIT_NAME); String commitName = req.getParams().get(CoreAdminParams.COMMIT_NAME);
ClusterState clusterState = h.coreContainer.getZkController().getClusterState(); ClusterState clusterState = h.coreContainer.getZkController().getClusterState();
if (!clusterState.hasCollection(collectionName)) { if (!clusterState.hasCollection(collectionName)) {
@ -1158,7 +1183,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
+ collectionName + "', no action taken."); + 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; return params;
}), }),
DELETESNAPSHOT_OP(DELETESNAPSHOT, (req, rsp, h) -> { 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."); 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; return params;
}), }),
LISTSNAPSHOTS_OP(LISTSNAPSHOTS, (req, rsp, h) -> { LISTSNAPSHOTS_OP(LISTSNAPSHOTS, (req, rsp, h) -> {
@ -1215,7 +1240,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
WAIT_FOR_FINAL_STATE, WAIT_FOR_FINAL_STATE,
IN_PLACE_MOVE, IN_PLACE_MOVE,
"replica", "replica",
"shard"); "shard",
FOLLOW_ALIASES);
}), }),
DELETENODE_OP(DELETENODE, (req, rsp, h) -> copy(req.getParams().required(), null, "node")); 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.CoreAdminResponse;
import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.V2Response; 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.SolrInputDocument;
import org.apache.solr.common.cloud.Aliases; import org.apache.solr.common.cloud.Aliases;
import org.apache.solr.common.cloud.ClusterProperties; 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.DocCollection;
import org.apache.solr.common.cloud.Replica; import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice; import org.apache.solr.common.cloud.Slice;
@ -794,8 +796,20 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
@Test @Test
public void testRenameCollection() throws Exception { public void testRenameCollection() throws Exception {
String collectionName1 = "testRename_collection1"; doTestRenameCollection(true);
String collectionName2 = "testRename_collection2"; 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(collectionName1, "conf", 1, 1).setAlias("col1").process(cluster.getSolrClient());
CollectionAdminRequest.createCollection(collectionName2, "conf", 1, 1).setAlias("col2").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.createCategoryRoutedAlias("catAlias", "field1", 100,
CollectionAdminRequest.createCollection("_unused_", "conf", 1, 1)).process(cluster.getSolrClient()); 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 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(); aliases = zkStateReader.getAliases();
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName1, aliases.resolveSimpleAlias("foo")); assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName1, aliases.resolveSimpleAlias("foo"));
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName1, aliases.resolveSimpleAlias("simpleAlias")); assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName1, aliases.resolveSimpleAlias("simpleAlias"));
List<String> compoundAliases = aliases.resolveAliases("compoundAlias"); List<String> compoundAliases = aliases.resolveAliases("compoundAlias");
assertEquals(compoundAliases.toString(), 2, compoundAliases.size()); assertEquals(compoundAliases.toString(), 2, compoundAliases.size());
assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName1)); assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName1));
assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName2)); assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName2));
}
CollectionAdminRequest.renameCollection(collectionName1, collectionName2).process(cluster.getSolrClient()); CollectionAdminRequest.renameCollection(collectionName1, collectionName2).process(cluster.getSolrClient());
zkStateReader.aliasesManager.update(); zkStateReader.aliasesManager.update();
aliases = zkStateReader.getAliases(); 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("simpleAlias"));
assertEquals(aliases.getCollectionAliasListMap().toString(), collectionName2, aliases.resolveSimpleAlias(collectionName1)); 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 // 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()); assertEquals(compoundAliases.toString(), 1, compoundAliases.size());
assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName2)); assertTrue(compoundAliases.toString(), compoundAliases.contains(collectionName2));
try { 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"); fail("category-based alias renaming should fail");
} catch (Exception e) { } 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 { try {
CollectionAdminRequest.renameCollection("col2", "foo").process(cluster.getSolrClient()); rename = CollectionAdminRequest.renameCollection("col2", "foo");
fail("shuold fail because 'foo' already exists"); rename.setFollowAliases(followAliases);
rename.process(cluster.getSolrClient());
fail("should fail because 'foo' already exists");
} catch (Exception e) { } 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 @Test
public void testOverseerStatus() throws IOException, SolrServerException { public void testOverseerStatus() throws IOException, SolrServerException {
CollectionAdminResponse response = new CollectionAdminRequest.OverseerStatus().process(cluster.getSolrClient()); 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()); assertEquals("copied num docs", NUM_DOCS, queryResponse.getResults().getNumFound());
} }
@Test
public void testSameTargetReindexing() throws Exception { 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; final String targetCollection = sourceCollection;
createCollection(sourceCollection, "conf1", 2, 2); createCollection(sourceCollection, "conf1", 2, 2);
@ -160,6 +168,8 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
CollectionAdminRequest.ReindexCollection req = CollectionAdminRequest.reindexCollection(sourceCollection) CollectionAdminRequest.ReindexCollection req = CollectionAdminRequest.reindexCollection(sourceCollection)
.setTarget(targetCollection); .setTarget(targetCollection);
req.setRemoveSource(sourceRemove);
req.setFollowAliases(followAliases);
req.process(solrClient); req.process(solrClient);
String realTargetCollection = null; String realTargetCollection = null;
@ -183,11 +193,16 @@ public class ReindexCollectionTest extends SolrCloudTestCase {
ReindexCollectionCmd.State state = ReindexCollectionCmd.State.get(coll.getStr(ReindexCollectionCmd.REINDEXING_STATE)); ReindexCollectionCmd.State state = ReindexCollectionCmd.State.get(coll.getStr(ReindexCollectionCmd.REINDEXING_STATE));
return ReindexCollectionCmd.State.FINISHED == state; return ReindexCollectionCmd.State.FINISHED == state;
}); });
solrClient.getZkStateReader().aliasesManager.update();
SolrTestCaseJ4.Solr11035BandAid(solrClient, targetCollection, "id", NUM_DOCS, "*:*", SolrTestCaseJ4.Solr11035BandAid(solrClient, targetCollection, "id", NUM_DOCS, "*:*",
"ReindexCollectionTest.testSameTargetReindex", false); "ReindexCollectionTest.testSameTargetReindex", false);
// verify the target docs exist // verify the target docs exist
QueryResponse rsp = solrClient.query(targetCollection, params(CommonParams.Q, "*:*")); QueryResponse rsp = solrClient.query(targetCollection, params(CommonParams.Q, "*:*"));
assertEquals("copied num docs", NUM_DOCS, rsp.getResults().getNumFound()); 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 @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 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: 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 more than one collection
* an alias must not refer to a <<Routed Aliases,Routed Alias>> * 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 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. 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 abstract static class AsyncCollectionSpecificAdminRequest extends AsyncCollectionAdminRequest {
protected String collection; protected String collection;
protected Boolean followAliases;
public AsyncCollectionSpecificAdminRequest(CollectionAction action, String collection) { public AsyncCollectionSpecificAdminRequest(CollectionAction action, String collection) {
super(action); super(action);
@ -256,10 +257,15 @@ public abstract class CollectionAdminRequest<T extends CollectionAdminResponse>
return collection; return collection;
} }
public void setFollowAliases(Boolean followAliases) {
this.followAliases = followAliases;
}
@Override @Override
public SolrParams getParams() { public SolrParams getParams() {
ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); ModifiableSolrParams params = new ModifiableSolrParams(super.getParams());
params.set(CoreAdminParams.NAME, collection); params.set(CoreAdminParams.NAME, collection);
params.setNonNull(CollectionAdminParams.FOLLOW_ALIASES, followAliases);
return params; return params;
} }
} }

View File

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