You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by gu...@apache.org on 2019/01/28 23:07:40 UTC
[lucene-solr] 05/05: SOLR-13148 Factory for routed aliases,
remove CollectionHandler deps on TimeRoutedAlias, minor cleanup
This is an automated email from the ASF dual-hosted git repository.
gus pushed a commit to branch solr-13131
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
commit c78e1b61fb48d6bf5f24ddd93a6592d5e1dd3e32
Author: Gus Heck <gu...@apache.org>
AuthorDate: Mon Jan 28 16:02:08 2019 -0500
SOLR-13148 Factory for routed aliases, remove CollectionHandler deps
on TimeRoutedAlias, minor cleanup
---
.../cloud/api/collections/CategoryRoutedAlias.java | 20 ++-
.../solr/cloud/api/collections/CreateAliasCmd.java | 48 +++---
.../solr/cloud/api/collections/RoutedAlias.java | 81 ++++++++--
.../cloud/api/collections/TimeRoutedAlias.java | 166 ++++++++++-----------
.../solr/handler/admin/CollectionsHandler.java | 41 +++--
.../processor/RoutedAliasUpdateProcessor.java | 20 +--
.../apache/solr/cloud/CreateRoutedAliasTest.java | 2 +-
7 files changed, 216 insertions(+), 162 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/CategoryRoutedAlias.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/CategoryRoutedAlias.java
index 30b0747..5a352a6 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/CategoryRoutedAlias.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/CategoryRoutedAlias.java
@@ -18,8 +18,11 @@
package org.apache.solr.cloud.api.collections;
import java.time.Instant;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.cloud.Aliases;
@@ -29,10 +32,9 @@ public class CategoryRoutedAlias implements RoutedAlias {
private final String aliasName;
private final Map<String, String> aliasProperties;
- public CategoryRoutedAlias(String aliasName, Map<String, String> aliasProperties) {
-
+ CategoryRoutedAlias(String aliasName, Map<String, String> aliasMetadata) {
this.aliasName = aliasName;
- this.aliasProperties = aliasProperties;
+ this.aliasProperties = aliasMetadata;
}
@Override
@@ -61,7 +63,7 @@ public class CategoryRoutedAlias implements RoutedAlias {
}
@Override
- public String computeInitialCollectionName(String collection) {
+ public Optional<String> computeInitialCollectionName() {
return null;
}
@@ -69,4 +71,14 @@ public class CategoryRoutedAlias implements RoutedAlias {
public Map<String, String> getAliasMetadata() {
return aliasProperties;
}
+
+ @Override
+ public Set<String> getRequiredParams() {
+ return new HashSet<>();
+ }
+
+ @Override
+ public Set<String> getOptionalParams() {
+ return new HashSet<>();
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/CreateAliasCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/CreateAliasCmd.java
index 182eda3..320a4ae 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/CreateAliasCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/CreateAliasCmd.java
@@ -19,11 +19,12 @@ package org.apache.solr.cloud.api.collections;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
-import java.util.function.BiFunction;
import java.util.stream.Collectors;
import org.apache.solr.common.SolrException;
@@ -97,36 +98,29 @@ public class CreateAliasCmd extends AliasCmd {
}
private void callCreateRoutedAlias(ZkNodeProps message, String aliasName, ZkStateReader zkStateReader, ClusterState state) throws Exception {
- // Validate we got everything we need
+ // Validate we got a basic minimum
if (!message.getProperties().keySet().containsAll(RoutedAlias.MINIMAL_REQUIRED_PARAMS)) {
throw new SolrException(BAD_REQUEST, "A routed alias requires these params: " + RoutedAlias.MINIMAL_REQUIRED_PARAMS
+ " plus some create-collection prefixed ones.");
}
- String aliasType = message.getStr(RoutedAlias.ROUTER_TYPE_NAME);
-
- BiFunction<String, ZkNodeProps, RoutedAlias> aliasConstructor = RoutedAlias.constructorFactory.get(aliasType);
-
- if (aliasConstructor == null) {
- throw new SolrException(BAD_REQUEST, "Router name: " + aliasType + " is not in supported types, "
- + String.join(", ", RoutedAlias.constructorFactory.keySet()));
- }
-
-
-
- RoutedAlias routedAlias = aliasConstructor.apply(aliasName, message);
-
- String start = message.getStr(RoutedAlias.ROUTER_START);
- if (start != null) {
- String initialCollectionName = routedAlias.computeInitialCollectionName(start);
- if (initialCollectionName != null) {
- ensureCollection(aliasName, zkStateReader, state, routedAlias.getAliasMetadata(), initialCollectionName);
- // Create/update the alias
- zkStateReader.aliasesManager.applyModificationAndExportToZk(aliases -> aliases
- .cloneWithCollectionAlias(aliasName, initialCollectionName)
- .cloneWithCollectionAliasProperties(aliasName, routedAlias.getAliasMetadata()));
- return;
- }
+ // convert values to strings
+ Map<String, String> props = new LinkedHashMap<>();
+ message.getProperties().forEach((key, value) -> props.put(key, String.valueOf(value)));
+
+ // Further validation happens here
+ RoutedAlias routedAlias = RoutedAlias.fromProps(aliasName, props);
+
+ // If we can, create the first collection.
+ Optional<String> initialCollectionName = routedAlias.computeInitialCollectionName();
+ if (initialCollectionName.isPresent()) {
+ String initialColl = initialCollectionName.get();
+ ensureAliasCollection(aliasName, zkStateReader, state, routedAlias.getAliasMetadata(), initialColl);
+ // Create/update the alias
+ zkStateReader.aliasesManager.applyModificationAndExportToZk(aliases -> aliases
+ .cloneWithCollectionAlias(aliasName, initialColl)
+ .cloneWithCollectionAliasProperties(aliasName, routedAlias.getAliasMetadata()));
+ return;
}
// Create/update the alias
@@ -134,7 +128,7 @@ public class CreateAliasCmd extends AliasCmd {
.cloneWithCollectionAliasProperties(aliasName, routedAlias.getAliasMetadata()));
}
- private void ensureCollection(String aliasName, ZkStateReader zkStateReader, ClusterState state, Map<String, String> aliasProperties, String initialCollectionName) throws Exception {
+ private void ensureAliasCollection(String aliasName, ZkStateReader zkStateReader, ClusterState state, Map<String, String> aliasProperties, String initialCollectionName) throws Exception {
// Create the collection
createCollectionAndWait(state, aliasName, aliasProperties, initialCollectionName, ocmh);
validateAllCollectionsExistAndNoDups(Collections.singletonList(initialCollectionName), zkStateReader);
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/RoutedAlias.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/RoutedAlias.java
index 1a607ba..5e7df68 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/RoutedAlias.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/RoutedAlias.java
@@ -18,30 +18,35 @@
package org.apache.solr.cloud.api.collections;
import java.time.Instant;
+import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
-import java.util.function.BiFunction;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.Aliases;
-import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.update.AddUpdateCommand;
+import static org.apache.solr.common.SolrException.ErrorCode.BAD_REQUEST;
+import static org.apache.solr.common.SolrException.ErrorCode.SERVER_ERROR;
+
public interface RoutedAlias {
- Map<String, BiFunction<String, ZkNodeProps, RoutedAlias>> constructorFactory = ImmutableMap.<String, BiFunction<String, ZkNodeProps, RoutedAlias>>builder()
- .put("time", TimeRoutedAlias::fromZkProps)
- .build();
+ /**
+ * Types supported. Every entry here must have a case in the switch statement in {@link #fromProps(String, Map)}
+ */
+ enum SupportedRouterTypes {
+ TIME,
+ CATEGORY
+ }
String ROUTER_PREFIX = "router.";
String ROUTER_TYPE_NAME = ROUTER_PREFIX + "name";
String ROUTER_FIELD = ROUTER_PREFIX + "field";
- String ROUTER_AUTO_DELETE_AGE = ROUTER_PREFIX + "autoDeleteAge";
- String ROUTER_START = ROUTER_PREFIX + "start";
String CREATE_COLLECTION_PREFIX = "create-collection.";
Set<String> MINIMAL_REQUIRED_PARAMS = Sets.newHashSet(ROUTER_TYPE_NAME, ROUTER_FIELD);
String ROUTED_ALIAS_NAME_CORE_PROP = "routedAliasName"; // core prop
@@ -52,6 +57,42 @@ public interface RoutedAlias {
}
/**
+ * Factory method for implementations of this interface. There should be no reason to construct instances
+ * elsewhere, and routed alias types are encouraged to have package private constructors.
+ *
+ * @param aliasName The alias name (will be returned by {@link #getAliasName()}
+ * @param props The properties from an overseer message.
+ * @return An implementation appropriate for the supplied properties, or null if no type is specified.
+ * @throws SolrException If the properties are invalid or the router type is unknown.
+ */
+ static RoutedAlias fromProps(String aliasName, Map<String, String> props) throws SolrException {
+
+ String typeStr = props.get(ROUTER_TYPE_NAME);
+ if (typeStr == null) {
+ return null;
+ }
+ SupportedRouterTypes routerType;
+ try {
+ routerType = SupportedRouterTypes.valueOf(typeStr.toUpperCase(Locale.ENGLISH));
+ } catch (IllegalArgumentException e) {
+ throw new SolrException(BAD_REQUEST, "Router name: " + typeStr + " is not in supported types, "
+ + Arrays.asList(SupportedRouterTypes.values()));
+ }
+ switch (routerType) {
+ case TIME:
+ return new TimeRoutedAlias(aliasName, props);
+ case CATEGORY:
+ return new CategoryRoutedAlias(aliasName, props);
+ default:
+ // if we got a type not handled by the switch there's been a bogus implementation.
+ throw new SolrException(SERVER_ERROR, "Router " + routerType + " is not fully implemented. If you see this" +
+ "error in an official release please file a bug report. Available types were:"
+ + Arrays.asList(SupportedRouterTypes.values()));
+
+ }
+ }
+
+ /**
* Ensure our parsed version of the alias collection list is up to date. If it was modified, return true.
* Note that this will return true if some other alias was modified or if properties were modified. These
* are spurious and the caller should be written to be tolerant of no material changes.
@@ -59,19 +100,28 @@ public interface RoutedAlias {
boolean updateParsedCollectionAliases(ZkController zkController);
/**
+ * Create the initial collection for this RoutedAlias if applicable.
+ *
+ * Routed Aliases do not aggregate existing collections, instead they create collections on the fly. If the initial
+ * collection can be determined from initialization parameters it should be calculated here.
*
- * @param startParam the start parameter passed to create alias cmd
* @return optional string of initial collection name
*/
- String computeInitialCollectionName(String startParam);
+ Optional<String> computeInitialCollectionName();
+
+ /**
+ * The name of the alias. This name is used in place of a collection name for both queries and updates.
+ *
+ * @return The name of the Alias.
+ */
String getAliasName();
/**
* Parses the elements of the collection list. Result is returned them in sorted order (desc) if there
* is a natural order for this type of routed alias
*/
- List<Map.Entry<Instant,String>> parseCollections(Aliases aliases);
+ List<Map.Entry<Instant, String>> parseCollections(Aliases aliases);
/**
* Check that the value we will be routing on is legal for this type of routed alias.
@@ -85,13 +135,16 @@ public interface RoutedAlias {
*
* @param cmd The command that might cause collection creation
* @return The name of the proper destination collection for the document which may or may not be a
- * newly created collection
+ * newly created collection
*/
- String createCollectionsIfRequired( AddUpdateCommand cmd);
+ String createCollectionsIfRequired(AddUpdateCommand cmd);
/**
- *
* @return get alias related metadata
*/
Map<String, String> getAliasMetadata();
+
+ Set<String> getRequiredParams();
+
+ Set<String> getOptionalParams();
}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/TimeRoutedAlias.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/TimeRoutedAlias.java
index c8b0ebb..afc9ce4 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/TimeRoutedAlias.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/TimeRoutedAlias.java
@@ -30,19 +30,19 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
-import java.util.LinkedHashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
import com.google.common.base.Objects;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.Aliases;
-import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.RequiredSolrParams;
@@ -58,7 +58,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
-import static org.apache.solr.cloud.api.collections.TimeRoutedAlias.CreationType.*;
+import static org.apache.solr.cloud.api.collections.TimeRoutedAlias.CreationType.ASYNC_PREEMPTIVE;
+import static org.apache.solr.cloud.api.collections.TimeRoutedAlias.CreationType.NONE;
+import static org.apache.solr.cloud.api.collections.TimeRoutedAlias.CreationType.SYNCHRONOUS;
import static org.apache.solr.common.SolrException.ErrorCode.BAD_REQUEST;
import static org.apache.solr.common.params.CommonParams.TZ;
@@ -86,33 +88,30 @@ public class TimeRoutedAlias implements RoutedAlias {
public static final String ROUTER_START = ROUTER_PREFIX + "start";
public static final String ROUTER_INTERVAL = ROUTER_PREFIX + "interval";
public static final String ROUTER_MAX_FUTURE = ROUTER_PREFIX + "maxFutureMs";
+ public static final String ROUTER_AUTO_DELETE_AGE = ROUTER_PREFIX + "autoDeleteAge";
public static final String ROUTER_PREEMPTIVE_CREATE_MATH = ROUTER_PREFIX + "preemptiveCreateMath";
// plus TZ and NAME
/**
* Parameters required for creating a routed alias
*/
- public static final List<String> REQUIRED_ROUTER_PARAMS = Collections.unmodifiableList(Arrays.asList(
+ public static final Set<String> REQUIRED_ROUTER_PARAMS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
CommonParams.NAME,
ROUTER_TYPE_NAME,
ROUTER_FIELD,
ROUTER_START,
- ROUTER_INTERVAL));
+ ROUTER_INTERVAL)));
/**
* Optional parameters for creating a routed alias excluding parameters for collection creation.
*/
//TODO lets find a way to remove this as it's harder to maintain than required list
- public static final List<String> OPTIONAL_ROUTER_PARAMS = Collections.unmodifiableList(Arrays.asList(
+ public static final Set<String> OPTIONAL_ROUTER_PARAMS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
ROUTER_MAX_FUTURE,
ROUTER_AUTO_DELETE_AGE,
ROUTER_PREEMPTIVE_CREATE_MATH,
- TZ)); // kinda special
+ TZ))); // kinda special
- static Predicate<String> PARAM_IS_PROP =
- key -> key.equals(TZ) ||
- (key.startsWith(ROUTER_PREFIX) && !key.equals(ROUTER_START)) || //TODO reconsider START special case
- key.startsWith(CREATE_COLLECTION_PREFIX);
// This format must be compatible with collection name limitations
private static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()
@@ -122,58 +121,6 @@ public class TimeRoutedAlias implements RoutedAlias {
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter(Locale.ROOT).withZone(ZoneOffset.UTC); // deliberate -- collection names disregard TZ
- @Override
- public String computeInitialCollectionName(String dateStr) {
- return formatCollectionNameFromInstant(aliasName, parseStringAsInstant(dateStr, timeZone));
- }
-
- public static Instant parseInstantFromCollectionName(String aliasName, String collection) {
- final String dateTimePart = collection.substring(aliasName.length() + 1);
- return DATE_TIME_FORMATTER.parse(dateTimePart, Instant::from);
- }
-
- public static String formatCollectionNameFromInstant(String aliasName, Instant timestamp) {
- String nextCollName = DATE_TIME_FORMATTER.format(timestamp);
- for (int i = 0; i < 3; i++) { // chop off seconds, minutes, hours
- if (nextCollName.endsWith("_00")) {
- nextCollName = nextCollName.substring(0, nextCollName.length()-3);
- }
- }
- assert DATE_TIME_FORMATTER.parse(nextCollName, Instant::from).equals(timestamp);
- return aliasName + "_" + nextCollName;
- }
-
- Instant parseStringAsInstant(String str, TimeZone zone) {
- Instant start = DateMathParser.parseMath(new Date(), str, zone).toInstant();
- checkMilis(start);
- return start;
- }
-
- private void checkMilis(Instant date) {
- if (!date.truncatedTo(ChronoUnit.SECONDS).equals(date)) {
- throw new SolrException(BAD_REQUEST,
- "Date or date math for start time includes milliseconds, which is not supported. " +
- "(Hint: 'NOW' used without rounding always has this problem)");
- }
- }
-
-
- @Override
- public boolean updateParsedCollectionAliases(ZkController zkController) {
- final Aliases aliases = zkController.getZkStateReader().getAliases(); // note: might be different from last request
- if (this.parsedCollectionsAliases != aliases) {
- if (this.parsedCollectionsAliases != null) {
- log.debug("Observing possibly updated alias: {}", getAliasName());
- }
- this.parsedCollectionsDesc = parseCollections(aliases );
- this.parsedCollectionsAliases = aliases;
- return true;
- }
- return false;
- }
-
-
-
//
// Instance data and methods
//
@@ -186,11 +133,20 @@ public class TimeRoutedAlias implements RoutedAlias {
private final String preemptiveCreateMath;
private final String autoDeleteAgeMath; // ex: /DAY-30DAYS *optional*
private final TimeZone timeZone;
+ private String start;
+
+ TimeRoutedAlias(String aliasName, Map<String, String> aliasMetadata) throws SolrException {
+ // Validate we got everything we need
+ if (!aliasMetadata.keySet().containsAll(TimeRoutedAlias.REQUIRED_ROUTER_PARAMS)) {
+ throw new SolrException(BAD_REQUEST, "A time routed alias requires these params: " + TimeRoutedAlias.REQUIRED_ROUTER_PARAMS
+ + " plus some create-collection prefixed ones.");
+ }
- public TimeRoutedAlias(String aliasName, Map<String, String> aliasMetadata) {
- this.aliasName = aliasName;
this.aliasMetadata = aliasMetadata;
- final MapSolrParams params = new MapSolrParams(aliasMetadata); // for convenience
+
+ this.start = this.aliasMetadata.get(ROUTER_START);
+ this.aliasName = aliasName;
+ final MapSolrParams params = new MapSolrParams(this.aliasMetadata); // for convenience
final RequiredSolrParams required = params.required();
if (!"time".equals(required.get(ROUTER_TYPE_NAME))) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Only 'time' routed aliases is supported right now.");
@@ -204,7 +160,7 @@ public class TimeRoutedAlias implements RoutedAlias {
String pcmTmp = params.get(ROUTER_PREEMPTIVE_CREATE_MATH);
preemptiveCreateMath = pcmTmp != null ? (pcmTmp.startsWith("-") ? pcmTmp : "-" + pcmTmp) : null;
autoDeleteAgeMath = params.get(ROUTER_AUTO_DELETE_AGE); // no default
- timeZone = TimeZoneUtils.parseTimezone(aliasMetadata.get(CommonParams.TZ));
+ timeZone = TimeZoneUtils.parseTimezone(this.aliasMetadata.get(CommonParams.TZ));
// More validation:
@@ -243,6 +199,55 @@ public class TimeRoutedAlias implements RoutedAlias {
}
@Override
+ public Optional<String> computeInitialCollectionName() {
+ return Optional.of(formatCollectionNameFromInstant(aliasName, parseStringAsInstant(this.start, timeZone)));
+ }
+
+ public static Instant parseInstantFromCollectionName(String aliasName, String collection) {
+ final String dateTimePart = collection.substring(aliasName.length() + 1);
+ return DATE_TIME_FORMATTER.parse(dateTimePart, Instant::from);
+ }
+
+ public static String formatCollectionNameFromInstant(String aliasName, Instant timestamp) {
+ String nextCollName = DATE_TIME_FORMATTER.format(timestamp);
+ for (int i = 0; i < 3; i++) { // chop off seconds, minutes, hours
+ if (nextCollName.endsWith("_00")) {
+ nextCollName = nextCollName.substring(0, nextCollName.length()-3);
+ }
+ }
+ assert DATE_TIME_FORMATTER.parse(nextCollName, Instant::from).equals(timestamp);
+ return aliasName + "_" + nextCollName;
+ }
+
+ Instant parseStringAsInstant(String str, TimeZone zone) {
+ Instant start = DateMathParser.parseMath(new Date(), str, zone).toInstant();
+ checkMilis(start);
+ return start;
+ }
+
+ private void checkMilis(Instant date) {
+ if (!date.truncatedTo(ChronoUnit.SECONDS).equals(date)) {
+ throw new SolrException(BAD_REQUEST,
+ "Date or date math for start time includes milliseconds, which is not supported. " +
+ "(Hint: 'NOW' used without rounding always has this problem)");
+ }
+ }
+
+ @Override
+ public boolean updateParsedCollectionAliases(ZkController zkController) {
+ final Aliases aliases = zkController.getZkStateReader().getAliases(); // note: might be different from last request
+ if (this.parsedCollectionsAliases != aliases) {
+ if (this.parsedCollectionsAliases != null) {
+ log.debug("Observing possibly updated alias: {}", getAliasName());
+ }
+ this.parsedCollectionsDesc = parseCollections(aliases );
+ this.parsedCollectionsAliases = aliases;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public String getAliasName() {
return aliasName;
}
@@ -321,8 +326,6 @@ public class TimeRoutedAlias implements RoutedAlias {
}
}
-
-
@Override
public String createCollectionsIfRequired(AddUpdateCommand cmd) {
SolrQueryRequest req = cmd.getReq();
@@ -381,19 +384,14 @@ public class TimeRoutedAlias implements RoutedAlias {
return aliasMetadata;
}
- static RoutedAlias fromZkProps(String aliasName, ZkNodeProps aliasProps) throws SolrException {
- // Validate we got everything we need
- if (!aliasProps.getProperties().keySet().containsAll(TimeRoutedAlias.REQUIRED_ROUTER_PARAMS)) {
- throw new SolrException(BAD_REQUEST, "A time routed alias requires these params: " + TimeRoutedAlias.REQUIRED_ROUTER_PARAMS
- + " plus some create-collection prefixed ones.");
- }
-
- Map<String, String> aliasProperties = new LinkedHashMap<>();
- aliasProps.getProperties().entrySet().stream()
- .filter(entry -> TimeRoutedAlias.PARAM_IS_PROP.test(entry.getKey()))
- .forEach(entry -> aliasProperties.put(entry.getKey(), (String) entry.getValue())); // way easier than .collect
+ @Override
+ public Set<String> getRequiredParams() {
+ return REQUIRED_ROUTER_PARAMS;
+ }
- return new TimeRoutedAlias(aliasName, aliasProperties); // validates as well
+ @Override
+ public Set<String> getOptionalParams() {
+ return OPTIONAL_ROUTER_PARAMS;
}
/**
@@ -470,8 +468,6 @@ public class TimeRoutedAlias implements RoutedAlias {
}
}
-
-
private void preemptiveAsync(Runnable r, SolrCore core) {
preemptiveCreateOnceAlready = true;
core.runAsync(r);
@@ -558,6 +554,4 @@ public class TimeRoutedAlias implements RoutedAlias {
ASYNC_PREEMPTIVE,
SYNCHRONOUS
}
-
-
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
index 725d2bd..3f39808 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
@@ -52,6 +53,7 @@ import org.apache.solr.cloud.OverseerTaskQueue.QueueEvent;
import org.apache.solr.cloud.ZkController.NotInClusterStateException;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.cloud.ZkShardTerms;
+import org.apache.solr.cloud.api.collections.RoutedAlias;
import org.apache.solr.cloud.overseer.SliceMutator;
import org.apache.solr.cloud.rule.ReplicaAssigner;
import org.apache.solr.cloud.rule.Rule;
@@ -115,9 +117,7 @@ import static org.apache.solr.cloud.api.collections.OverseerCollectionMessageHan
import static org.apache.solr.cloud.api.collections.OverseerCollectionMessageHandler.REQUESTID;
import static org.apache.solr.cloud.api.collections.OverseerCollectionMessageHandler.SHARDS_PROP;
import static org.apache.solr.cloud.api.collections.OverseerCollectionMessageHandler.SHARD_UNIQUE;
-import static org.apache.solr.cloud.api.collections.TimeRoutedAlias.CREATE_COLLECTION_PREFIX;
-import static org.apache.solr.cloud.api.collections.TimeRoutedAlias.OPTIONAL_ROUTER_PARAMS;
-import static org.apache.solr.cloud.api.collections.TimeRoutedAlias.REQUIRED_ROUTER_PARAMS;
+import static org.apache.solr.cloud.api.collections.RoutedAlias.CREATE_COLLECTION_PREFIX;
import static org.apache.solr.common.SolrException.ErrorCode.BAD_REQUEST;
import static org.apache.solr.common.cloud.DocCollection.DOC_ROUTER;
import static org.apache.solr.common.cloud.DocCollection.RULE;
@@ -551,20 +551,39 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
String alias = req.getParams().get(NAME);
SolrIdentifierValidator.validateAliasName(alias);
String collections = req.getParams().get("collections");
- Map<String, Object> result = copy(req.getParams(), null, REQUIRED_ROUTER_PARAMS);
- copy(req.getParams(), result, OPTIONAL_ROUTER_PARAMS);
+ RoutedAlias routedAlias = null;
+ Exception ex = null;
+ try {
+ // note that RA specific validation occurs here.
+ routedAlias = RoutedAlias.fromProps(alias, req.getParams().toMap(new HashMap<>()));
+ } catch (SolrException e) {
+ // we'll throw this later if we are in fact creating a routed alias.
+ ex = e;
+ }
if (collections != null) {
- if (result.size() > 1) { // (NAME should be there, and if it's not we will fail below)
+ if (routedAlias != null) {
throw new SolrException(BAD_REQUEST, "Collections cannot be specified when creating a time routed alias.");
+ } else {
+ //////////////////////////////////////
+ // Regular alias creation indicated //
+ //////////////////////////////////////
+ return copy(req.getParams().required(), null, NAME, "collections");
}
- // regular alias creation...
- return copy(req.getParams().required(), null, NAME, "collections");
}
- // Ok so we are creating a time routed alias from here
+ /////////////////////////////////////////////////
+ // We are creating a routed alias from here on //
+ /////////////////////////////////////////////////
+
+ // If our prior creation attempt had issues expose them now.
+ if (ex != null) {
+ throw ex;
+ }
+
+ // Now filter out just the parameters we care about from the request
+ Map<String, Object> result = copy(req.getParams(), null, routedAlias.getRequiredParams());
+ copy(req.getParams(), result, routedAlias.getOptionalParams());
- // for validation....
- copy(req.getParams().required(), null, REQUIRED_ROUTER_PARAMS);
ModifiableSolrParams createCollParams = new ModifiableSolrParams(); // without prefix
// add to result params that start with "create-collection.".
diff --git a/solr/core/src/java/org/apache/solr/update/processor/RoutedAliasUpdateProcessor.java b/solr/core/src/java/org/apache/solr/update/processor/RoutedAliasUpdateProcessor.java
index 5929e94..ab83e22 100644
--- a/solr/core/src/java/org/apache/solr/update/processor/RoutedAliasUpdateProcessor.java
+++ b/solr/core/src/java/org/apache/solr/update/processor/RoutedAliasUpdateProcessor.java
@@ -25,9 +25,7 @@ import java.util.Map;
import java.util.stream.Collectors;
import org.apache.solr.cloud.ZkController;
-import org.apache.solr.cloud.api.collections.CategoryRoutedAlias;
import org.apache.solr.cloud.api.collections.RoutedAlias;
-import org.apache.solr.cloud.api.collections.TimeRoutedAlias;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.cloud.Aliases;
@@ -95,23 +93,7 @@ public class RoutedAliasUpdateProcessor extends UpdateRequestProcessor {
return next;
} else {
try {
- final Map<String, String> aliasProperties = getAliasProps(req, aliasName);
- String routerType = aliasProperties.get("router.name");
- RoutedAlias alias;
- switch (routerType) {
- case "time": {
- log.debug("Time Routed Alias detected for {}", aliasName );
- alias = new TimeRoutedAlias(aliasName, aliasProperties);
- break;
- }
- case "category":{
- log.debug("Category Routed Alias detected for {}", aliasName );
- alias = new CategoryRoutedAlias(aliasName, aliasProperties);
- break;
- }
- default:
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown router type " + routerType);
- }
+ RoutedAlias alias = RoutedAlias.fromProps(aliasName, getAliasProps(req, aliasName));
return new RoutedAliasUpdateProcessor(req, next, aliasDistribPhase, alias);
} catch (Exception e) { // ensure we throw SERVER_ERROR not BAD_REQUEST at this stage
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Routed alias has invalid properties: " + e, e);
diff --git a/solr/core/src/test/org/apache/solr/cloud/CreateRoutedAliasTest.java b/solr/core/src/test/org/apache/solr/cloud/CreateRoutedAliasTest.java
index 2e18d18..85a755a 100644
--- a/solr/core/src/test/org/apache/solr/cloud/CreateRoutedAliasTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/CreateRoutedAliasTest.java
@@ -282,7 +282,7 @@ public class CreateRoutedAliasTest extends SolrCloudTestCase {
"&router.interval=%2B30MINUTE" +
"&create-collection.collection.configName=_default" +
"&create-collection.numShards=1");
- assertFailure(get, "Only 'time' routed aliases is supported right now");
+ assertFailure(get, " is not in supported types, ");
}
@Test