You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by da...@apache.org on 2018/10/23 00:05:59 UTC
[38/52] [abbrv] [partial] lucene-solr:jira/gradle: Add gradle support
for Solr
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0ae21ad0/solr/core/src/java/org/apache/solr/cloud/api/collections/TimeRoutedAlias.java
----------------------------------------------------------------------
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
deleted file mode 100644
index 1fb3d9e..0000000
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/TimeRoutedAlias.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.cloud.api.collections;
-
-import java.text.ParseException;
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeFormatterBuilder;
-import java.time.temporal.ChronoField;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-
-import com.google.common.base.Objects;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.cloud.Aliases;
-import org.apache.solr.common.params.CommonParams;
-import org.apache.solr.common.params.MapSolrParams;
-import org.apache.solr.common.params.RequiredSolrParams;
-import org.apache.solr.util.DateMathParser;
-import org.apache.solr.util.TimeZoneUtils;
-
-import static org.apache.solr.common.SolrException.ErrorCode.BAD_REQUEST;
-import static org.apache.solr.common.params.CommonParams.TZ;
-
-/**
- * Holds configuration for a routed alias, and some common code and constants.
- *
- * @see CreateAliasCmd
- * @see MaintainRoutedAliasCmd
- * @see org.apache.solr.update.processor.TimeRoutedAliasUpdateProcessor
- */
-public class TimeRoutedAlias {
-
- // These are parameter names to routed alias creation, AND are stored as metadata with the alias.
- public static final String ROUTER_PREFIX = "router.";
- public static final String ROUTER_TYPE_NAME = ROUTER_PREFIX + "name";
- public static final String ROUTER_FIELD = ROUTER_PREFIX + "field";
- 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_PREEMPTIVE_CREATE_MATH = ROUTER_PREFIX + "preemptiveCreateMath";
- public static final String ROUTER_AUTO_DELETE_AGE = ROUTER_PREFIX + "autoDeleteAge";
- public static final String CREATE_COLLECTION_PREFIX = "create-collection.";
- // plus TZ and NAME
-
- /**
- * Parameters required for creating a routed alias
- */
- public static final List<String> REQUIRED_ROUTER_PARAMS = Collections.unmodifiableList(Arrays.asList(
- CommonParams.NAME,
- ROUTER_TYPE_NAME,
- ROUTER_FIELD,
- ROUTER_START,
- 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(
- ROUTER_MAX_FUTURE,
- ROUTER_AUTO_DELETE_AGE,
- ROUTER_PREEMPTIVE_CREATE_MATH,
- 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);
-
- public static final String ROUTED_ALIAS_NAME_CORE_PROP = "routedAliasName"; // core prop
-
- // This format must be compatible with collection name limitations
- private static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()
- .append(DateTimeFormatter.ISO_LOCAL_DATE).appendPattern("[_HH[_mm[_ss]]]") //brackets mean optional
- .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
- .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
- .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
- .toFormatter(Locale.ROOT).withZone(ZoneOffset.UTC); // deliberate -- collection names disregard TZ
-
- 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;
- }
-
-
- //
- // Instance data and methods
- //
-
- private final String aliasName;
- private final String routeField;
- private final String intervalMath; // ex: +1DAY
- private final long maxFutureMs;
- private final String preemptiveCreateMath;
- private final String autoDeleteAgeMath; // ex: /DAY-30DAYS *optional*
- private final TimeZone timeZone;
-
- public TimeRoutedAlias(String aliasName, Map<String, String> aliasMetadata) {
- this.aliasName = aliasName;
- final MapSolrParams params = new MapSolrParams(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.");
- }
- routeField = required.get(ROUTER_FIELD);
- intervalMath = required.get(ROUTER_INTERVAL);
-
- //optional:
- maxFutureMs = params.getLong(ROUTER_MAX_FUTURE, TimeUnit.MINUTES.toMillis(10));
- // the date math configured is an interval to be subtracted from the most recent collection's time stamp
- 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));
-
- // More validation:
-
- // check that the date math is valid
- final Date now = new Date();
- try {
- final Date after = new DateMathParser(now, timeZone).parseMath(intervalMath);
- if (!after.after(now)) {
- throw new SolrException(BAD_REQUEST, "duration must add to produce a time in the future");
- }
- } catch (Exception e) {
- throw new SolrException(BAD_REQUEST, "bad " + TimeRoutedAlias.ROUTER_INTERVAL + ", " + e, e);
- }
-
- if (autoDeleteAgeMath != null) {
- try {
- final Date before = new DateMathParser(now, timeZone).parseMath(autoDeleteAgeMath);
- if (now.before(before)) {
- throw new SolrException(BAD_REQUEST, "duration must round or subtract to produce a time in the past");
- }
- } catch (Exception e) {
- throw new SolrException(BAD_REQUEST, "bad " + TimeRoutedAlias.ROUTER_AUTO_DELETE_AGE + ", " + e, e);
- }
- }
- if (preemptiveCreateMath != null) {
- try {
- new DateMathParser().parseMath(preemptiveCreateMath);
- } catch (ParseException e) {
- throw new SolrException(BAD_REQUEST, "Invalid date math for preemptiveCreateMath:" + preemptiveCreateMath);
- }
- }
-
- if (maxFutureMs < 0) {
- throw new SolrException(BAD_REQUEST, ROUTER_MAX_FUTURE + " must be >= 0");
- }
- }
-
- public String getAliasName() {
- return aliasName;
- }
-
- public String getRouteField() {
- return routeField;
- }
-
- public String getIntervalMath() {
- return intervalMath;
- }
-
- public long getMaxFutureMs() {
- return maxFutureMs;
- }
-
- public String getPreemptiveCreateWindow() {
- return preemptiveCreateMath;
- }
-
- public String getAutoDeleteAgeMath() {
- return autoDeleteAgeMath;
- }
-
- public TimeZone getTimeZone() {
- return timeZone;
- }
-
- @Override
- public String toString() {
- return Objects.toStringHelper(this)
- .add("aliasName", aliasName)
- .add("routeField", routeField)
- .add("intervalMath", intervalMath)
- .add("maxFutureMs", maxFutureMs)
- .add("preemptiveCreateMath", preemptiveCreateMath)
- .add("autoDeleteAgeMath", autoDeleteAgeMath)
- .add("timeZone", timeZone)
- .toString();
- }
-
- /** Parses the timestamp from the collection list and returns them in reverse sorted order (most recent 1st) */
- public List<Map.Entry<Instant,String>> parseCollections(Aliases aliases, Supplier<SolrException> aliasNotExist) {
- final List<String> collections = aliases.getCollectionAliasListMap().get(aliasName);
- if (collections == null) {
- throw aliasNotExist.get();
- }
- // note: I considered TreeMap but didn't like the log(N) just to grab the most recent when we use it later
- List<Map.Entry<Instant,String>> result = new ArrayList<>(collections.size());
- for (String collection : collections) {
- Instant colStartTime = parseInstantFromCollectionName(aliasName, collection);
- result.add(new AbstractMap.SimpleImmutableEntry<>(colStartTime, collection));
- }
- result.sort((e1, e2) -> e2.getKey().compareTo(e1.getKey())); // reverse sort by key
- return result;
- }
-
- /** Computes the timestamp of the next collection given the timestamp of the one before. */
- public Instant computeNextCollTimestamp(Instant fromTimestamp) {
- final Instant nextCollTimestamp =
- DateMathParser.parseMath(Date.from(fromTimestamp), "NOW" + intervalMath, timeZone).toInstant();
- assert nextCollTimestamp.isAfter(fromTimestamp);
- return nextCollTimestamp;
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0ae21ad0/solr/core/src/java/org/apache/solr/cloud/api/collections/UtilizeNodeCmd.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/UtilizeNodeCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/UtilizeNodeCmd.java
deleted file mode 100644
index 818b16f..0000000
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/UtilizeNodeCmd.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.cloud.api.collections;
-
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
-import org.apache.solr.client.solrj.cloud.autoscaling.Policy;
-import org.apache.solr.client.solrj.cloud.autoscaling.PolicyHelper;
-import org.apache.solr.client.solrj.cloud.autoscaling.Suggester;
-import org.apache.solr.client.solrj.request.V2Request;
-import org.apache.solr.common.cloud.ClusterState;
-import org.apache.solr.common.cloud.ZkNodeProps;
-import org.apache.solr.common.params.CollectionParams;
-import org.apache.solr.common.util.NamedList;
-import org.apache.solr.common.util.StrUtils;
-import org.apache.solr.common.util.Utils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-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.params.AutoScalingParams.NODE;
-import static org.apache.solr.common.params.CollectionParams.CollectionAction.MOVEREPLICA;
-import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
-
-public class UtilizeNodeCmd implements OverseerCollectionMessageHandler.Cmd {
- private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
- private final OverseerCollectionMessageHandler ocmh;
-
- public UtilizeNodeCmd(OverseerCollectionMessageHandler ocmh) {
- this.ocmh = ocmh;
- }
-
- @Override
- public void call(ClusterState state, ZkNodeProps message, NamedList results) throws Exception {
- ocmh.checkRequired(message, NODE);
- String nodeName = message.getStr(NODE);
- String async = message.getStr(ASYNC);
- AutoScalingConfig autoScalingConfig = ocmh.overseer.getSolrCloudManager().getDistribStateManager().getAutoScalingConfig();
-
- //first look for any violation that may use this replica
- List<ZkNodeProps> requests = new ArrayList<>();
- //first look for suggestions if any
- List<Suggester.SuggestionInfo> suggestions = PolicyHelper.getSuggestions(autoScalingConfig, ocmh.overseer.getSolrCloudManager());
- for (Suggester.SuggestionInfo suggestionInfo : suggestions) {
- log.info("op: " + suggestionInfo.getOperation());
- String coll = null;
- List<String> pieces = StrUtils.splitSmart(suggestionInfo.getOperation().getPath(), '/');
- if (pieces.size() > 1) {
- coll = pieces.get(2);
- } else {
- continue;
- }
- log.info("coll: " + coll);
- if (suggestionInfo.getOperation() instanceof V2Request) {
- String targetNode = (String) Utils.getObjectByPath(suggestionInfo.getOperation(), true, "command/move-replica/targetNode");
- if (Objects.equals(targetNode, nodeName)) {
- String replica = (String) Utils.getObjectByPath(suggestionInfo.getOperation(), true, "command/move-replica/replica");
- requests.add(new ZkNodeProps(COLLECTION_PROP, coll,
- CollectionParams.TARGET_NODE, targetNode,
- ASYNC, async,
- REPLICA_PROP, replica));
- }
- }
- }
- executeAll(requests);
- PolicyHelper.SessionWrapper sessionWrapper = PolicyHelper.getSession(ocmh.overseer.getSolrCloudManager());
- Policy.Session session = sessionWrapper.get();
- Suggester initialsuggester = session.getSuggester(MOVEREPLICA)
- .hint(Suggester.Hint.TARGET_NODE, nodeName);
- Suggester suggester = null;
- for (; ; ) {
- suggester = session.getSuggester(MOVEREPLICA)
- .hint(Suggester.Hint.TARGET_NODE, nodeName);
- SolrRequest request = suggester.getSuggestion();
- if (requests.size() > 10) {
- log.info("too_many_suggestions");
- PolicyHelper.logState(ocmh.overseer.getSolrCloudManager(), initialsuggester);
- break;
- }
- log.info("SUGGESTION: {}", request);
- if (request == null) break;
- session = suggester.getSession();
- requests.add(new ZkNodeProps(COLLECTION_PROP, request.getParams().get(COLLECTION_PROP),
- CollectionParams.TARGET_NODE, request.getParams().get(CollectionParams.TARGET_NODE),
- REPLICA_PROP, request.getParams().get(REPLICA_PROP),
- ASYNC, request.getParams().get(ASYNC)));
- }
- log.info("total_suggestions: {}", requests.size());
- if (requests.size() == 0) {
- PolicyHelper.logState(ocmh.overseer.getSolrCloudManager(), initialsuggester);
- }
- sessionWrapper.returnSession(session);
- try {
- executeAll(requests);
- } finally {
- sessionWrapper.release();
- }
- }
-
- private void executeAll(List<ZkNodeProps> requests) throws Exception {
- if (requests.isEmpty()) return;
- for (ZkNodeProps props : requests) {
- NamedList result = new NamedList();
- ocmh.commandMap.get(MOVEREPLICA)
- .call(ocmh.overseer.getSolrCloudManager().getClusterStateProvider().getClusterState(),
- props,
- result);
- }
- requests.clear();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0ae21ad0/solr/core/src/java/org/apache/solr/cloud/api/collections/package-info.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/package-info.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/package-info.java
deleted file mode 100644
index 651d4fe..0000000
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Package related to internal implementations of the SolrCloud collections api
- */
-package org.apache.solr.cloud.api.collections;
-
-
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0ae21ad0/solr/core/src/java/org/apache/solr/cloud/autoscaling/ActionContext.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ActionContext.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ActionContext.java
deleted file mode 100644
index 8487d3d..0000000
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ActionContext.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.cloud.autoscaling;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.solr.client.solrj.cloud.SolrCloudManager;
-import org.apache.solr.common.MapWriter;
-
-/**
- * Provides additional context for the TriggerAction such as the trigger instance on
- * which the action is being executed as well as helper methods to pass computed information along
- * to the next action
- */
-public class ActionContext implements MapWriter {
-
- private final SolrCloudManager cloudManager;
- private final AutoScaling.Trigger source;
- private final Map<String, Object> properties;
-
- public ActionContext(SolrCloudManager cloudManager, AutoScaling.Trigger source, Map<String, Object> properties) {
- this.cloudManager = cloudManager;
- this.source = source;
- this.properties = properties;
- }
-
- public SolrCloudManager getCloudManager() {
- return cloudManager;
- }
-
- public AutoScaling.Trigger getSource() {
- return source;
- }
-
- public Map<String, Object> getProperties() {
- return properties;
- }
-
- public Object getProperty(String name) {
- return properties != null ? properties.get(name) : null;
- }
-
- @Override
- public void writeMap(EntryWriter ew) throws IOException {
- ew.put("source", source.getName());
- if (properties != null) {
- for (Map.Entry<String, Object> entry : properties.entrySet()) {
- ew.put("properties." + entry.getKey(), entry.getValue());
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0ae21ad0/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoAddReplicasPlanAction.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoAddReplicasPlanAction.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoAddReplicasPlanAction.java
deleted file mode 100644
index fdd3474..0000000
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoAddReplicasPlanAction.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.cloud.autoscaling;
-
-
-import java.io.IOException;
-
-import org.apache.solr.client.solrj.cloud.autoscaling.NoneSuggester;
-import org.apache.solr.client.solrj.cloud.autoscaling.Policy;
-import org.apache.solr.client.solrj.cloud.SolrCloudManager;
-import org.apache.solr.client.solrj.cloud.autoscaling.Suggester;
-import org.apache.solr.client.solrj.impl.ClusterStateProvider;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.cloud.ClusterState;
-import org.apache.solr.common.cloud.DocCollection;
-import org.apache.solr.common.cloud.ZkStateReader;
-
-public class AutoAddReplicasPlanAction extends ComputePlanAction {
-
- @Override
- protected Suggester getSuggester(Policy.Session session, TriggerEvent event, ActionContext context, SolrCloudManager cloudManager) throws IOException {
- // for backward compatibility
- ClusterStateProvider stateProvider = cloudManager.getClusterStateProvider();
- String autoAddReplicas = stateProvider.getClusterProperty(ZkStateReader.AUTO_ADD_REPLICAS, (String) null);
- if (autoAddReplicas != null && autoAddReplicas.equals("false")) {
- return NoneSuggester.get(session);
- }
-
- Suggester suggester = super.getSuggester(session, event, context, cloudManager);
- ClusterState clusterState;
- try {
- clusterState = stateProvider.getClusterState();
- } catch (IOException e) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Exception getting cluster state", e);
- }
-
- boolean anyCollections = false;
- for (DocCollection collection: clusterState.getCollectionsMap().values()) {
- if (collection.getAutoAddReplicas()) {
- anyCollections = true;
- suggester.hint(Suggester.Hint.COLL, collection.getName());
- }
- }
-
- if (!anyCollections) return NoneSuggester.get(session);
- return suggester;
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0ae21ad0/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
deleted file mode 100644
index 93f449a..0000000
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.cloud.autoscaling;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.lucene.store.AlreadyClosedException;
-import org.apache.solr.client.solrj.cloud.SolrCloudManager;
-import org.apache.solr.client.solrj.cloud.autoscaling.TriggerEventType;
-import org.apache.solr.common.util.Utils;
-import org.apache.solr.core.SolrResourceLoader;
-
-public class AutoScaling {
-
- /**
- * Implementation of this interface is used for processing events generated by a trigger.
- */
- public interface TriggerEventProcessor {
-
- /**
- * This method is executed for events produced by {@link Trigger#run()}.
- *
- * @param event a subclass of {@link TriggerEvent}
- * @return true if the processor was ready to perform actions on the event, false
- * otherwise. If false was returned then callers should assume the event was discarded.
- */
- boolean process(TriggerEvent event);
- }
-
- /**
- * Interface for a Solr trigger. Each trigger implements Runnable and Closeable interface. A trigger
- * is scheduled using a {@link java.util.concurrent.ScheduledExecutorService} so it is executed as
- * per a configured schedule to check whether the trigger is ready to fire. The {@link AutoScaling.Trigger#setProcessor(AutoScaling.TriggerEventProcessor)}
- * method should be used to set a processor which is used by implementation of this class whenever
- * ready.
- * <p>
- * As per the guarantees made by the {@link java.util.concurrent.ScheduledExecutorService} a trigger
- * implementation is only ever called sequentially and therefore need not be thread safe. However, it
- * is encouraged that implementations be immutable with the exception of the associated listener
- * which can be get/set by a different thread than the one executing the trigger. Therefore, implementations
- * should use appropriate synchronization around the listener.
- * <p>
- * When a trigger is ready to fire, it calls the {@link TriggerEventProcessor#process(TriggerEvent)} event
- * with the proper trigger event object. If that method returns false then it should be interpreted to mean
- * that Solr is not ready to process this trigger event and therefore we should retain the state and fire
- * at the next invocation of the run() method.
- */
- public interface Trigger extends Closeable, Runnable {
- /**
- * Trigger name.
- */
- String getName();
-
- /**
- * Event type generated by this trigger.
- */
- TriggerEventType getEventType();
-
- /** Returns true if this trigger is enabled. */
- boolean isEnabled();
-
- /** Trigger properties. */
- Map<String, Object> getProperties();
-
- /** Number of seconds to wait between fired events ("waitFor" property). */
- int getWaitForSecond();
-
- /** Actions to execute when event is fired. */
- List<TriggerAction> getActions();
-
- /** Set event processor to call when event is fired. */
- void setProcessor(TriggerEventProcessor processor);
-
- /** Get event processor. */
- TriggerEventProcessor getProcessor();
-
- /** Return true when this trigger is closed and cannot be used. */
- boolean isClosed();
-
- /** Set internal state of this trigger from another instance. */
- void restoreState(Trigger old);
-
- /** Save internal state of this trigger in ZooKeeper. */
- void saveState();
-
- /** Restore internal state of this trigger from ZooKeeper. */
- void restoreState();
-
- /**
- * Called when trigger is created but before it's initialized or scheduled for use.
- * This method should also verify that the trigger configuration parameters are correct. It may
- * be called multiple times.
- * @param properties configuration properties
- * @throws TriggerValidationException contains details of invalid configuration parameters.
- */
- void configure(SolrResourceLoader loader, SolrCloudManager cloudManager, Map<String, Object> properties) throws TriggerValidationException;
-
- /**
- * Called before a trigger is scheduled. Any heavy object creation or initialisation should
- * be done in this method instead of the Trigger's constructor.
- */
- void init() throws Exception;
- }
-
- /**
- * Factory to produce instances of {@link Trigger}.
- */
- public static abstract class TriggerFactory implements Closeable {
- protected boolean isClosed = false;
-
- public abstract Trigger create(TriggerEventType type, String name, Map<String, Object> props) throws TriggerValidationException;
-
- @Override
- public void close() throws IOException {
- synchronized (this) {
- isClosed = true;
- }
- }
- }
-
- /**
- * Default implementation of {@link TriggerFactory}.
- */
- public static class TriggerFactoryImpl extends TriggerFactory {
-
- private final SolrCloudManager cloudManager;
- private final SolrResourceLoader loader;
-
- public TriggerFactoryImpl(SolrResourceLoader loader, SolrCloudManager cloudManager) {
- Objects.requireNonNull(cloudManager);
- Objects.requireNonNull(loader);
- this.cloudManager = cloudManager;
- this.loader = loader;
- }
-
- @Override
- public synchronized Trigger create(TriggerEventType type, String name, Map<String, Object> props) throws TriggerValidationException {
- if (isClosed) {
- throw new AlreadyClosedException("TriggerFactory has already been closed, cannot create new triggers");
- }
- if (type == null) {
- throw new IllegalArgumentException("Trigger type must not be null");
- }
- if (name == null || name.isEmpty()) {
- throw new IllegalArgumentException("Trigger name must not be empty");
- }
- Trigger t;
- switch (type) {
- case NODEADDED:
- t = new NodeAddedTrigger(name);
- break;
- case NODELOST:
- t = new NodeLostTrigger(name);
- break;
- case SEARCHRATE:
- t = new SearchRateTrigger(name);
- break;
- case METRIC:
- t = new MetricTrigger(name);
- break;
- case SCHEDULED:
- t = new ScheduledTrigger(name);
- break;
- case INDEXSIZE:
- t = new IndexSizeTrigger(name);
- break;
- default:
- throw new IllegalArgumentException("Unknown event type: " + type + " in trigger: " + name);
- }
- t.configure(loader, cloudManager, props);
- return t;
- }
-
- }
-
- public static final String AUTO_ADD_REPLICAS_TRIGGER_NAME = ".auto_add_replicas";
-
- public static final String AUTO_ADD_REPLICAS_TRIGGER_DSL =
- " {" +
- " 'name' : '" + AUTO_ADD_REPLICAS_TRIGGER_NAME + "'," +
- " 'event' : 'nodeLost'," +
- " 'waitFor' : -1," +
- " 'enabled' : true," +
- " 'actions' : [" +
- " {" +
- " 'name':'auto_add_replicas_plan'," +
- " 'class':'solr.AutoAddReplicasPlanAction'" +
- " }," +
- " {" +
- " 'name':'execute_plan'," +
- " 'class':'solr.ExecutePlanAction'" +
- " }" +
- " ]" +
- " }";
-
- public static final Map<String, Object> AUTO_ADD_REPLICAS_TRIGGER_PROPS = (Map) Utils.fromJSONString(AUTO_ADD_REPLICAS_TRIGGER_DSL);
-
- public static final String SCHEDULED_MAINTENANCE_TRIGGER_NAME = ".scheduled_maintenance";
-
- public static final String SCHEDULED_MAINTENANCE_TRIGGER_DSL =
- " {" +
- " 'name' : '" + SCHEDULED_MAINTENANCE_TRIGGER_NAME + "'," +
- " 'event' : 'scheduled'," +
- " 'startTime' : 'NOW'," +
- " 'every' : '+1DAY'," +
- " 'enabled' : true," +
- " 'actions' : [" +
- " {" +
- " 'name':'inactive_shard_plan'," +
- " 'class':'solr.InactiveShardPlanAction'" +
- " }," +
- " {" +
- " 'name':'execute_plan'," +
- " 'class':'solr.ExecutePlanAction'" +
- " }" +
- " ]" +
- " }";
-
- public static final Map<String, Object> SCHEDULED_MAINTENANCE_TRIGGER_PROPS = (Map) Utils.fromJSONString(SCHEDULED_MAINTENANCE_TRIGGER_DSL);
-
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0ae21ad0/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
deleted file mode 100644
index 899c5cd..0000000
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.cloud.autoscaling;
-
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.solr.api.Api;
-import org.apache.solr.api.ApiBag;
-import org.apache.solr.client.solrj.cloud.SolrCloudManager;
-import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
-import org.apache.solr.client.solrj.cloud.autoscaling.BadVersionException;
-import org.apache.solr.client.solrj.cloud.autoscaling.Clause;
-import org.apache.solr.client.solrj.cloud.autoscaling.Policy;
-import org.apache.solr.client.solrj.cloud.autoscaling.PolicyHelper;
-import org.apache.solr.client.solrj.cloud.autoscaling.Preference;
-import org.apache.solr.client.solrj.cloud.autoscaling.TriggerEventProcessorStage;
-import org.apache.solr.client.solrj.cloud.autoscaling.TriggerEventType;
-import org.apache.solr.common.MapWriter;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.params.AutoScalingParams;
-import org.apache.solr.common.params.CollectionAdminParams;
-import org.apache.solr.common.util.CommandOperation;
-import org.apache.solr.common.util.IOUtils;
-import org.apache.solr.common.util.StrUtils;
-import org.apache.solr.common.util.TimeSource;
-import org.apache.solr.common.util.Utils;
-import org.apache.solr.core.SolrResourceLoader;
-import org.apache.solr.handler.RequestHandlerBase;
-import org.apache.solr.handler.RequestHandlerUtils;
-import org.apache.solr.request.SolrQueryRequest;
-import org.apache.solr.request.SolrRequestHandler;
-import org.apache.solr.response.SolrQueryResponse;
-import org.apache.solr.security.AuthorizationContext;
-import org.apache.solr.security.PermissionNameProvider;
-import org.apache.zookeeper.KeeperException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static java.util.stream.Collectors.collectingAndThen;
-import static java.util.stream.Collectors.toSet;
-import static org.apache.solr.common.cloud.ZkStateReader.SOLR_AUTOSCALING_CONF_PATH;
-import static org.apache.solr.common.params.AutoScalingParams.*;
-import static org.apache.solr.common.params.CommonParams.JSON;
-
-/**
- * Handler for /cluster/autoscaling
- */
-public class AutoScalingHandler extends RequestHandlerBase implements PermissionNameProvider {
- public static final String HANDLER_PATH = "/admin/autoscaling";
- private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
- protected final SolrCloudManager cloudManager;
- protected final SolrResourceLoader loader;
- protected final AutoScaling.TriggerFactory triggerFactory;
- private final List<Map<String, String>> DEFAULT_ACTIONS = new ArrayList<>(3);
- private static Set<String> singletonCommands = Stream.of("set-cluster-preferences", "set-cluster-policy")
- .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));
-
- private final TimeSource timeSource;
-
- public AutoScalingHandler(SolrCloudManager cloudManager, SolrResourceLoader loader) {
- this.cloudManager = cloudManager;
- this.loader = loader;
- this.triggerFactory = new AutoScaling.TriggerFactoryImpl(loader, cloudManager);
- this.timeSource = cloudManager.getTimeSource();
- Map<String, String> map = new HashMap<>(2);
- map.put(NAME, "compute_plan");
- map.put(CLASS, "solr.ComputePlanAction");
- DEFAULT_ACTIONS.add(map);
- map = new HashMap<>(2);
- map.put(NAME, "execute_plan");
- map.put(CLASS, "solr.ExecutePlanAction");
- DEFAULT_ACTIONS.add(map);
- }
-
- @Override
- public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
- try {
- String httpMethod = (String) req.getContext().get("httpMethod");
- RequestHandlerUtils.setWt(req, JSON);
-
- if ("GET".equals(httpMethod)) {
- String path = (String) req.getContext().get("path");
- if (path == null) path = "/cluster/autoscaling";
- List<String> parts = StrUtils.splitSmart(path, '/');
- if (parts.get(0).isEmpty()) parts.remove(0);
-
- if (parts.size() < 2 || parts.size() > 3) {
- // invalid
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown path: " + path);
- }
-
- AutoScalingConfig autoScalingConf = cloudManager.getDistribStateManager().getAutoScalingConfig();
- if (parts.size() == 2) {
- autoScalingConf.writeMap(new MapWriter.EntryWriter() {
-
- @Override
- public MapWriter.EntryWriter put(String k, Object v) throws IOException {
- rsp.getValues().add(k, v);
- return this;
- }
- });
- } else if (parts.size() == 3) {
- if (DIAGNOSTICS.equals(parts.get(2))) {
- handleDiagnostics(rsp, autoScalingConf);
- } else if (SUGGESTIONS.equals(parts.get(2))) {
- handleSuggestions(rsp, autoScalingConf);
- }
- }
- } else {
- if (req.getContentStreams() == null) {
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No commands specified for autoscaling");
- }
- List<CommandOperation> ops = CommandOperation.readCommands(req.getContentStreams(), rsp.getValues(), singletonCommands);
- if (ops == null) {
- // errors have already been added to the response so there's nothing left to do
- return;
- }
- processOps(req, rsp, ops);
- }
- } catch (Exception e) {
- rsp.getValues().add("result", "failure");
- throw e;
- } finally {
- RequestHandlerUtils.addExperimentalFormatWarning(rsp);
- }
- }
-
-
- private void handleSuggestions(SolrQueryResponse rsp, AutoScalingConfig autoScalingConf) throws IOException {
- rsp.getValues().add("suggestions",
- PolicyHelper.getSuggestions(autoScalingConf, cloudManager));
- }
-
- public void processOps(SolrQueryRequest req, SolrQueryResponse rsp, List<CommandOperation> ops)
- throws KeeperException, InterruptedException, IOException {
- while (true) {
- AutoScalingConfig initialConfig = cloudManager.getDistribStateManager().getAutoScalingConfig();
- AutoScalingConfig currentConfig = initialConfig;
- for (CommandOperation op : ops) {
- switch (op.name) {
- case CMD_SET_TRIGGER:
- currentConfig = handleSetTrigger(req, rsp, op, currentConfig);
- break;
- case CMD_REMOVE_TRIGGER:
- currentConfig = handleRemoveTrigger(req, rsp, op, currentConfig);
- break;
- case CMD_SET_LISTENER:
- currentConfig = handleSetListener(req, rsp, op, currentConfig);
- break;
- case CMD_REMOVE_LISTENER:
- currentConfig = handleRemoveListener(req, rsp, op, currentConfig);
- break;
- case CMD_SUSPEND_TRIGGER:
- currentConfig = handleSuspendTrigger(req, rsp, op, currentConfig);
- break;
- case CMD_RESUME_TRIGGER:
- currentConfig = handleResumeTrigger(req, rsp, op, currentConfig);
- break;
- case CMD_SET_POLICY:
- currentConfig = handleSetPolicies(req, rsp, op, currentConfig);
- break;
- case CMD_REMOVE_POLICY:
- currentConfig = handleRemovePolicy(req, rsp, op, currentConfig);
- break;
- case CMD_SET_CLUSTER_PREFERENCES:
- currentConfig = handleSetClusterPreferences(req, rsp, op, currentConfig);
- break;
- case CMD_SET_CLUSTER_POLICY:
- currentConfig = handleSetClusterPolicy(req, rsp, op, currentConfig);
- break;
- case CMD_SET_PROPERTIES:
- currentConfig = handleSetProperties(req, rsp, op, currentConfig);
- break;
- default:
- op.addError("Unknown command: " + op.name);
- }
- }
- List errs = CommandOperation.captureErrors(ops);
- if (!errs.isEmpty()) {
- throw new ApiBag.ExceptionWithErrObject(SolrException.ErrorCode.BAD_REQUEST, "Error in command payload", errs);
- }
-
- if (!currentConfig.equals(initialConfig)) {
- // update in ZK
- if (setAutoScalingConfig(currentConfig)) {
- break;
- } else {
- // someone else updated the config, get the latest one and re-apply our ops
- rsp.getValues().add("retry", "initialVersion=" + initialConfig.getZkVersion());
- continue;
- }
- } else {
- // no changes
- break;
- }
- }
- rsp.getValues().add("result", "success");
- }
-
- private AutoScalingConfig handleSetProperties(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op, AutoScalingConfig currentConfig) {
- Map<String, Object> map = op.getDataMap() == null ? Collections.emptyMap() : op.getDataMap();
- Map<String, Object> configProps = new HashMap<>(currentConfig.getProperties());
- configProps.putAll(map);
- // remove a key which is set to null
- map.forEach((k, v) -> {
- if (v == null) configProps.remove(k);
- });
- return currentConfig.withProperties(configProps);
- }
-
- private void handleDiagnostics(SolrQueryResponse rsp, AutoScalingConfig autoScalingConf) throws IOException {
- Policy policy = autoScalingConf.getPolicy();
- rsp.getValues().add("diagnostics", PolicyHelper.getDiagnostics(policy, cloudManager));
- }
-
- private AutoScalingConfig handleSetClusterPolicy(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException, IOException {
- List<Map<String, Object>> clusterPolicy = (List<Map<String, Object>>) op.getCommandData();
- if (clusterPolicy == null || !(clusterPolicy instanceof List)) {
- op.addError("set-cluster-policy expects an array of objects");
- return currentConfig;
- }
- List<Clause> cp = null;
- try {
- cp = clusterPolicy.stream().map(Clause::create).collect(Collectors.toList());
- } catch (Exception e) {
- op.addError(e.getMessage());
- return currentConfig;
- }
- Policy p = currentConfig.getPolicy().withClusterPolicy(cp);
- currentConfig = currentConfig.withPolicy(p);
- return currentConfig;
- }
-
- private AutoScalingConfig handleSetClusterPreferences(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException, IOException {
- List<Map<String, Object>> preferences = (List<Map<String, Object>>) op.getCommandData();
- if (preferences == null || !(preferences instanceof List)) {
- op.addError("A list of cluster preferences not found");
- return currentConfig;
- }
- List<Preference> prefs = null;
- try {
- prefs = preferences.stream().map(Preference::new).collect(Collectors.toList());
- } catch (Exception e) {
- op.addError(e.getMessage());
- return currentConfig;
- }
- Policy p = currentConfig.getPolicy().withClusterPreferences(prefs);
- currentConfig = currentConfig.withPolicy(p);
- return currentConfig;
- }
-
- private AutoScalingConfig handleRemovePolicy(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException, IOException {
- String policyName = (String) op.getVal("");
-
- if (op.hasError()) return currentConfig;
-
- Map<String, List<Clause>> policies = currentConfig.getPolicy().getPolicies();
- if (policies == null || !policies.containsKey(policyName)) {
- op.addError("No policy exists with name: " + policyName);
- return currentConfig;
- }
-
- cloudManager.getClusterStateProvider().getClusterState().forEachCollection(coll -> {
- if (policyName.equals(coll.getPolicyName()))
- op.addError(StrUtils.formatString("policy : {0} is being used by collection {1}", policyName, coll.getName()));
- });
- if (op.hasError()) return currentConfig;
- policies = new HashMap<>(policies);
- policies.remove(policyName);
- Policy p = currentConfig.getPolicy().withPolicies(policies);
- currentConfig = currentConfig.withPolicy(p);
- return currentConfig;
- }
-
- private AutoScalingConfig handleSetPolicies(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException, IOException {
- Map<String, Object> policiesMap = op.getDataMap();
- for (Map.Entry<String, Object> policy : policiesMap.entrySet()) {
- String policyName = policy.getKey();
- if (policyName == null || policyName.trim().length() == 0) {
- op.addError("The policy name cannot be null or empty");
- return currentConfig;
- }
- }
- Map<String, List<Clause>> currentClauses = new HashMap<>(currentConfig.getPolicy().getPolicies());
- Map<String, List<Clause>> newClauses = null;
- try {
- newClauses = Policy.clausesFromMap((Map<String, List<Map<String, Object>>>) op.getCommandData(),
- new ArrayList<>() );
- } catch (Exception e) {
- op.addError(e.getMessage());
- return currentConfig;
- }
- currentClauses.putAll(newClauses);
- Policy p = currentConfig.getPolicy().withPolicies(currentClauses);
- currentConfig = currentConfig.withPolicy(p);
- return currentConfig;
- }
-
- private AutoScalingConfig handleResumeTrigger(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException {
- String triggerName = op.getStr(NAME);
- if (op.hasError()) return currentConfig;
- Map<String, AutoScalingConfig.TriggerConfig> triggers = currentConfig.getTriggerConfigs();
- Set<String> changed = new HashSet<>();
- if (!Policy.EACH.equals(triggerName) && !triggers.containsKey(triggerName)) {
- op.addError("No trigger exists with name: " + triggerName);
- return currentConfig;
- }
- Map<String, AutoScalingConfig.TriggerConfig> newTriggers = new HashMap<>();
- for (Map.Entry<String, AutoScalingConfig.TriggerConfig> entry : triggers.entrySet()) {
- if (Policy.EACH.equals(triggerName) || triggerName.equals(entry.getKey())) {
- AutoScalingConfig.TriggerConfig trigger = entry.getValue();
- if (!trigger.enabled) {
- trigger = trigger.withEnabled(true);
- newTriggers.put(entry.getKey(), trigger);
- changed.add(entry.getKey());
- } else {
- newTriggers.put(entry.getKey(), entry.getValue());
- }
- } else {
- newTriggers.put(entry.getKey(), entry.getValue());
- }
- }
- rsp.getValues().add("changed", changed);
- if (!changed.isEmpty()) {
- currentConfig = currentConfig.withTriggerConfigs(newTriggers);
- }
- return currentConfig;
- }
-
- private AutoScalingConfig handleSuspendTrigger(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException {
- String triggerName = op.getStr(NAME);
- if (op.hasError()) return currentConfig;
- String timeout = op.getStr(TIMEOUT, null);
- Date resumeTime = null;
- if (timeout != null) {
- try {
- int timeoutSeconds = parseHumanTime(timeout);
- resumeTime = new Date(TimeUnit.MILLISECONDS.convert(timeSource.getTimeNs(), TimeUnit.NANOSECONDS)
- + TimeUnit.MILLISECONDS.convert(timeoutSeconds, TimeUnit.SECONDS));
- } catch (IllegalArgumentException e) {
- op.addError("Invalid 'timeout' value for suspend trigger: " + triggerName);
- return currentConfig;
- }
- }
-
- Map<String, AutoScalingConfig.TriggerConfig> triggers = currentConfig.getTriggerConfigs();
- Set<String> changed = new HashSet<>();
-
- if (!Policy.EACH.equals(triggerName) && !triggers.containsKey(triggerName)) {
- op.addError("No trigger exists with name: " + triggerName);
- return currentConfig;
- }
- Map<String, AutoScalingConfig.TriggerConfig> newTriggers = new HashMap<>();
- for (Map.Entry<String, AutoScalingConfig.TriggerConfig> entry : triggers.entrySet()) {
- if (Policy.EACH.equals(triggerName) || triggerName.equals(entry.getKey())) {
- AutoScalingConfig.TriggerConfig trigger = entry.getValue();
- if (trigger.enabled) {
- trigger = trigger.withEnabled(false);
- if (resumeTime != null) {
- trigger = trigger.withProperty(RESUME_AT, resumeTime.getTime());
- }
- newTriggers.put(entry.getKey(), trigger);
- changed.add(trigger.name);
- } else {
- newTriggers.put(entry.getKey(), entry.getValue());
- }
- } else {
- newTriggers.put(entry.getKey(), entry.getValue());
- }
- }
- rsp.getValues().add("changed", changed);
- if (!changed.isEmpty()) {
- currentConfig = currentConfig.withTriggerConfigs(newTriggers);
- }
- return currentConfig;
- }
-
- private AutoScalingConfig handleRemoveListener(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException {
- String listenerName = op.getStr(NAME);
-
- if (op.hasError()) return currentConfig;
- Map<String, AutoScalingConfig.TriggerListenerConfig> listeners = currentConfig.getTriggerListenerConfigs();
- if (listeners == null || !listeners.containsKey(listenerName)) {
- op.addError("No listener exists with name: " + listenerName);
- return currentConfig;
- }
- currentConfig = currentConfig.withoutTriggerListenerConfig(listenerName);
- return currentConfig;
- }
-
- private AutoScalingConfig handleSetListener(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException {
- String listenerName = op.getStr(NAME);
- String triggerName = op.getStr(TRIGGER);
- List<String> stageNames = op.getStrs(STAGE, Collections.emptyList());
- String listenerClass = op.getStr(CLASS);
- List<String> beforeActions = op.getStrs(BEFORE_ACTION, Collections.emptyList());
- List<String> afterActions = op.getStrs(AFTER_ACTION, Collections.emptyList());
-
- if (op.hasError()) return currentConfig;
-
- Map<String, AutoScalingConfig.TriggerConfig> triggers = currentConfig.getTriggerConfigs();
- if (triggers == null || !triggers.containsKey(triggerName)) {
- op.addError("A trigger with the name " + triggerName + " does not exist");
- return currentConfig;
- }
- AutoScalingConfig.TriggerConfig triggerConfig = triggers.get(triggerName);
-
- if (stageNames.isEmpty() && beforeActions.isEmpty() && afterActions.isEmpty()) {
- op.addError("Either 'stage' or 'beforeAction' or 'afterAction' must be specified");
- return currentConfig;
- }
-
- for (String stage : stageNames) {
- try {
- TriggerEventProcessorStage.valueOf(stage);
- } catch (IllegalArgumentException e) {
- op.addError("Invalid stage name: " + stage);
- }
- }
- if (op.hasError()) return currentConfig;
-
- AutoScalingConfig.TriggerListenerConfig listenerConfig = new AutoScalingConfig.TriggerListenerConfig(listenerName, op.getValuesExcluding("name"));
-
- // validate that we can load the listener class
- // todo allow creation from blobstore
- TriggerListener listener = null;
- try {
- listener = loader.newInstance(listenerClass, TriggerListener.class);
- listener.configure(loader, cloudManager, listenerConfig);
- } catch (TriggerValidationException e) {
- log.warn("invalid listener configuration", e);
- op.addError("invalid listener configuration: " + e.toString());
- return currentConfig;
- } catch (Exception e) {
- log.warn("error loading listener class ", e);
- op.addError("Listener not found: " + listenerClass + ". error message:" + e.getMessage());
- return currentConfig;
- } finally {
- if (listener != null) {
- IOUtils.closeQuietly(listener);
- }
- }
-
- Set<String> actionNames = new HashSet<>();
- actionNames.addAll(beforeActions);
- actionNames.addAll(afterActions);
- for (AutoScalingConfig.ActionConfig action : triggerConfig.actions) {
- actionNames.remove(action.name);
- }
- if (!actionNames.isEmpty()) {
- op.addError("The trigger '" + triggerName + "' does not have actions named: " + actionNames);
- return currentConfig;
- }
- // todo - handle races between competing set-trigger and set-listener invocations
- currentConfig = currentConfig.withTriggerListenerConfig(listenerConfig);
- return currentConfig;
- }
-
- private AutoScalingConfig handleSetTrigger(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException {
- // we're going to modify the op - use a copy
- String triggerName = op.getStr(NAME);
- String eventTypeStr = op.getStr(EVENT);
-
- if (op.hasError()) return currentConfig;
- TriggerEventType eventType = TriggerEventType.valueOf(eventTypeStr.trim().toUpperCase(Locale.ROOT));
-
- String waitForStr = op.getStr(WAIT_FOR, null);
-
- CommandOperation opCopy = new CommandOperation(op.name, Utils.getDeepCopy((Map) op.getCommandData(), 10));
-
- if (waitForStr != null) {
- int seconds = 0;
- try {
- seconds = parseHumanTime(waitForStr);
- } catch (IllegalArgumentException e) {
- op.addError("Invalid 'waitFor' value '" + waitForStr + "' in trigger: " + triggerName);
- return currentConfig;
- }
- opCopy.getDataMap().put(WAIT_FOR, seconds);
- }
-
- Integer lowerBound = op.getInt(LOWER_BOUND, null);
- Integer upperBound = op.getInt(UPPER_BOUND, null);
-
- List<Map<String, String>> actions = (List<Map<String, String>>) op.getVal(ACTIONS);
- if (actions == null) {
- actions = DEFAULT_ACTIONS;
- opCopy.getDataMap().put(ACTIONS, actions);
- }
-
- // validate that we can load all the actions
- // todo allow creation from blobstore
- for (Map<String, String> action : actions) {
- if (!action.containsKey(NAME) || !action.containsKey(CLASS)) {
- op.addError("No 'name' or 'class' specified for action: " + action);
- return currentConfig;
- }
- String klass = action.get(CLASS);
- try {
- loader.findClass(klass, TriggerAction.class);
- } catch (Exception e) {
- log.warn("Could not load class : ", e);
- op.addError("Action not found: " + klass + " " + e.getMessage());
- return currentConfig;
- }
- }
- AutoScalingConfig.TriggerConfig trigger = new AutoScalingConfig.TriggerConfig(triggerName, opCopy.getValuesExcluding("name"));
- // validate trigger config
- AutoScaling.Trigger t = null;
- try {
- t = triggerFactory.create(trigger.event, trigger.name, trigger.properties);
- } catch (Exception e) {
- op.addError("Error validating trigger config " + trigger.name + ": " + e.toString());
- return currentConfig;
- } finally {
- if (t != null) {
- IOUtils.closeQuietly(t);
- }
- }
- currentConfig = currentConfig.withTriggerConfig(trigger);
- // check that there's a default SystemLogListener, unless user specified another one
- return withSystemLogListener(currentConfig, triggerName);
- }
-
- private static String fullName = SystemLogListener.class.getName();
- private static String solrName = "solr." + SystemLogListener.class.getSimpleName();
-
- static AutoScalingConfig withSystemLogListener(AutoScalingConfig autoScalingConfig, String triggerName) {
- Map<String, AutoScalingConfig.TriggerListenerConfig> configs = autoScalingConfig.getTriggerListenerConfigs();
- for (AutoScalingConfig.TriggerListenerConfig cfg : configs.values()) {
- if (triggerName.equals(cfg.trigger)) {
- // already has some listener config
- return autoScalingConfig;
- }
- }
- // need to add
- Map<String, Object> properties = new HashMap<>();
- properties.put(AutoScalingParams.CLASS, SystemLogListener.class.getName());
- properties.put(AutoScalingParams.TRIGGER, triggerName);
- properties.put(AutoScalingParams.STAGE, EnumSet.allOf(TriggerEventProcessorStage.class));
- AutoScalingConfig.TriggerListenerConfig listener =
- new AutoScalingConfig.TriggerListenerConfig(triggerName + CollectionAdminParams.SYSTEM_COLL, properties);
- autoScalingConfig = autoScalingConfig.withTriggerListenerConfig(listener);
- return autoScalingConfig;
- }
-
- private int parseHumanTime(String timeStr) {
- char c = timeStr.charAt(timeStr.length() - 1);
- long timeValue = Long.parseLong(timeStr.substring(0, timeStr.length() - 1));
- int seconds;
- switch (c) {
- case 'h':
- seconds = (int) TimeUnit.HOURS.toSeconds(timeValue);
- break;
- case 'm':
- seconds = (int) TimeUnit.MINUTES.toSeconds(timeValue);
- break;
- case 's':
- seconds = (int) timeValue;
- break;
- default:
- throw new IllegalArgumentException("Invalid time value");
- }
- return seconds;
- }
-
- private AutoScalingConfig handleRemoveTrigger(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
- AutoScalingConfig currentConfig) throws KeeperException, InterruptedException {
- String triggerName = op.getStr(NAME);
- boolean removeListeners = op.getBoolean(REMOVE_LISTENERS, false);
-
- if (op.hasError()) return currentConfig;
- Map<String, AutoScalingConfig.TriggerConfig> triggerConfigs = currentConfig.getTriggerConfigs();
- if (!triggerConfigs.containsKey(triggerName)) {
- op.addError("No trigger exists with name: " + triggerName);
- return currentConfig;
- }
- triggerConfigs = new HashMap<>(triggerConfigs);
- Set<String> activeListeners = new HashSet<>();
- Map<String, AutoScalingConfig.TriggerListenerConfig> listeners = currentConfig.getTriggerListenerConfigs();
- for (AutoScalingConfig.TriggerListenerConfig listener : listeners.values()) {
- if (triggerName.equals(listener.trigger)) {
- activeListeners.add(listener.name);
- }
- }
- if (!activeListeners.isEmpty()) {
- boolean onlySystemLog = false;
- if (activeListeners.size() == 1) {
- AutoScalingConfig.TriggerListenerConfig cfg = listeners.get(activeListeners.iterator().next());
- if (SystemLogListener.class.getName().equals(cfg.listenerClass) ||
- ("solr." + SystemLogListener.class.getSimpleName()).equals(cfg.listenerClass)) {
- onlySystemLog = true;
- }
- }
- if (removeListeners || onlySystemLog) {
- listeners = new HashMap<>(listeners);
- listeners.keySet().removeAll(activeListeners);
- } else {
- op.addError("Cannot remove trigger: " + triggerName + " because it has active listeners: " + activeListeners);
- return currentConfig;
- }
- }
- triggerConfigs.remove(triggerName);
- currentConfig = currentConfig.withTriggerConfigs(triggerConfigs).withTriggerListenerConfigs(listeners);
- return currentConfig;
- }
-
-
- private boolean setAutoScalingConfig(AutoScalingConfig currentConfig) throws KeeperException, InterruptedException, IOException {
- verifyAutoScalingConf(currentConfig);
- try {
- cloudManager.getDistribStateManager().setData(SOLR_AUTOSCALING_CONF_PATH, Utils.toJSON(currentConfig), currentConfig.getZkVersion());
- } catch (BadVersionException bve) {
- // somebody else has changed the configuration so we must retry
- return false;
- }
- //log.debug("-- saved version " + currentConfig.getZkVersion() + ": " + currentConfig);
- return true;
- }
-
- private void verifyAutoScalingConf(AutoScalingConfig autoScalingConf) throws IOException {
- Policy.Session session = autoScalingConf.getPolicy()
- .createSession(cloudManager);
- log.debug("Verified autoscaling configuration");
- }
-
- @Override
- public String getDescription() {
- return "A handler for autoscaling configuration";
- }
-
- @Override
- public Name getPermissionName(AuthorizationContext request) {
- switch (request.getHttpMethod()) {
- case "GET":
- return Name.AUTOSCALING_READ_PERM;
- case "POST":
- return Name.AUTOSCALING_WRITE_PERM;
- default:
- return null;
- }
- }
-
- @Override
- public Collection<Api> getApis() {
- return ApiBag.wrapRequestHandlers(this, "autoscaling.Commands");
- }
-
- @Override
- public Boolean registerV2() {
- return Boolean.TRUE;
- }
-
- @Override
- public SolrRequestHandler getSubHandler(String path) {
- if (path.equals("/diagnostics") || path.equals("/suggestions")) return this;
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0ae21ad0/solr/core/src/java/org/apache/solr/cloud/autoscaling/ComputePlanAction.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ComputePlanAction.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ComputePlanAction.java
deleted file mode 100644
index 7103bf5..0000000
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ComputePlanAction.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.cloud.autoscaling;
-
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.cloud.SolrCloudManager;
-import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
-import org.apache.solr.client.solrj.cloud.autoscaling.NoneSuggester;
-import org.apache.solr.client.solrj.cloud.autoscaling.Policy;
-import org.apache.solr.client.solrj.cloud.autoscaling.PolicyHelper;
-import org.apache.solr.client.solrj.cloud.autoscaling.Suggester;
-import org.apache.solr.client.solrj.cloud.autoscaling.UnsupportedSuggester;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.cloud.ClusterState;
-import org.apache.solr.common.cloud.DocCollection;
-import org.apache.solr.common.params.AutoScalingParams;
-import org.apache.solr.common.params.CollectionParams;
-import org.apache.solr.common.params.CoreAdminParams;
-import org.apache.solr.common.util.Pair;
-import org.apache.solr.common.util.StrUtils;
-import org.apache.solr.core.SolrResourceLoader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.solr.cloud.autoscaling.TriggerEvent.NODE_NAMES;
-
-/**
- * This class is responsible for using the configured policy and preferences
- * with the hints provided by the trigger event to compute the required cluster operations.
- * <p>
- * The cluster operations computed here are put into the {@link ActionContext}'s properties
- * with the key name "operations". The value is a List of SolrRequest objects.
- */
-public class ComputePlanAction extends TriggerActionBase {
- private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
- Set<String> collections = new HashSet<>();
-
- public ComputePlanAction() {
- super();
- TriggerUtils.validProperties(validProperties, "collections");
- }
-
-
- @Override
- public void configure(SolrResourceLoader loader, SolrCloudManager cloudManager, Map<String, Object> properties) throws TriggerValidationException {
- super.configure(loader, cloudManager, properties);
- String colString = (String) properties.get("collections");
- if (colString != null && !colString.isEmpty()) {
- collections.addAll(StrUtils.splitSmart(colString, ','));
- }
- }
-
- @Override
- public void process(TriggerEvent event, ActionContext context) throws Exception {
- log.debug("-- processing event: {} with context properties: {}", event, context.getProperties());
- SolrCloudManager cloudManager = context.getCloudManager();
- try {
- AutoScalingConfig autoScalingConf = cloudManager.getDistribStateManager().getAutoScalingConfig();
- if (autoScalingConf.isEmpty()) {
- throw new Exception("Action: " + getName() + " executed but no policy is configured");
- }
- PolicyHelper.SessionWrapper sessionWrapper = PolicyHelper.getSession(cloudManager);
- Policy.Session session = sessionWrapper.get();
- ClusterState clusterState = cloudManager.getClusterStateProvider().getClusterState();
- if (log.isTraceEnabled()) {
- log.trace("-- session: {}", session);
- log.trace("-- state: {}", clusterState);
- }
- try {
- Suggester suggester = getSuggester(session, event, context, cloudManager);
- int maxOperations = getMaxNumOps(event, autoScalingConf, clusterState);
- int requestedOperations = getRequestedNumOps(event);
- if (requestedOperations > maxOperations) {
- log.warn("Requested number of operations {} higher than maximum {}, adjusting...",
- requestedOperations, maxOperations);
- }
- int opCount = 0;
- int opLimit = maxOperations;
- if (requestedOperations > 0) {
- opLimit = requestedOperations;
- }
- do {
- // computing changes in large clusters may take a long time
- if (Thread.currentThread().isInterrupted()) {
- throw new InterruptedException("stopping - thread was interrupted");
- }
- SolrRequest operation = suggester.getSuggestion();
- opCount++;
- // prepare suggester for the next iteration
- if (suggester.getSession() != null) {
- session = suggester.getSession();
- }
- suggester = getSuggester(session, event, context, cloudManager);
-
- // break on first null op
- // unless a specific number of ops was requested
- // uncomment the following to log too many operations
- /*if (opCount > 10) {
- PolicyHelper.logState(cloudManager, initialSuggester);
- }*/
-
- if (operation == null) {
- if (requestedOperations < 0) {
- //uncomment the following to log zero operations
-// PolicyHelper.logState(cloudManager, initialSuggester);
- break;
- } else {
- log.info("Computed plan empty, remained " + (opCount - opLimit) + " requested ops to try.");
- continue;
- }
- }
- log.debug("Computed Plan: {}", operation.getParams());
- if (!collections.isEmpty()) {
- String coll = operation.getParams().get(CoreAdminParams.COLLECTION);
- if (coll != null && !collections.contains(coll)) {
- // discard an op that doesn't affect our collections
- log.debug("-- discarding due to collection={} not in {}", coll, collections);
- continue;
- }
- }
- Map<String, Object> props = context.getProperties();
- props.compute("operations", (k, v) -> {
- List<SolrRequest> operations = (List<SolrRequest>) v;
- if (operations == null) operations = new ArrayList<>();
- operations.add(operation);
- return operations;
- });
- } while (opCount < opLimit);
- } finally {
- releasePolicySession(sessionWrapper, session);
- }
- } catch (Exception e) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
- "Unexpected exception while processing event: " + event, e);
- }
- }
-
- private void releasePolicySession(PolicyHelper.SessionWrapper sessionWrapper, Policy.Session session) {
- sessionWrapper.returnSession(session);
- sessionWrapper.release();
-
- }
-
- protected int getMaxNumOps(TriggerEvent event, AutoScalingConfig autoScalingConfig, ClusterState clusterState) {
- // estimate a maximum default limit that should be sufficient for most purposes:
- // number of nodes * total number of replicas * 3
- AtomicInteger totalRF = new AtomicInteger();
- clusterState.forEachCollection(coll -> {
- Integer rf = coll.getReplicationFactor();
- if (rf == null) {
- if (coll.getSlices().isEmpty()) {
- rf = 1; // ???
- } else {
- rf = coll.getReplicas().size() / coll.getSlices().size();
- }
- }
- totalRF.addAndGet(rf * coll.getSlices().size());
- });
- int totalMax = clusterState.getLiveNodes().size() * totalRF.get() * 3;
- int maxOp = (Integer) autoScalingConfig.getProperties().getOrDefault(AutoScalingParams.MAX_COMPUTE_OPERATIONS, totalMax);
- Object o = event.getProperty(AutoScalingParams.MAX_COMPUTE_OPERATIONS, maxOp);
- try {
- return Integer.parseInt(String.valueOf(o));
- } catch (Exception e) {
- log.warn("Invalid '" + AutoScalingParams.MAX_COMPUTE_OPERATIONS + "' event property: " + o + ", using default " + maxOp);
- return maxOp;
- }
- }
-
- protected int getRequestedNumOps(TriggerEvent event) {
- Collection<TriggerEvent.Op> ops = (Collection<TriggerEvent.Op>) event.getProperty(TriggerEvent.REQUESTED_OPS, Collections.emptyList());
- if (ops.isEmpty()) {
- return -1;
- } else {
- return ops.size();
- }
- }
-
- private static final String START = "__start__";
-
- protected Suggester getSuggester(Policy.Session session, TriggerEvent event, ActionContext context, SolrCloudManager cloudManager) throws IOException {
- Suggester suggester;
- switch (event.getEventType()) {
- case NODEADDED:
- suggester = getNodeAddedSuggester(cloudManager, session, event);
- break;
- case NODELOST:
- String preferredOp = (String) event.getProperty(AutoScalingParams.PREFERRED_OP, CollectionParams.CollectionAction.MOVEREPLICA.toLower());
- CollectionParams.CollectionAction action = CollectionParams.CollectionAction.get(preferredOp);
- switch (action) {
- case MOVEREPLICA:
- suggester = session.getSuggester(action)
- .hint(Suggester.Hint.SRC_NODE, event.getProperty(NODE_NAMES));
- break;
- case DELETENODE:
- int start = (Integer)event.getProperty(START, 0);
- List<String> srcNodes = (List<String>) event.getProperty(NODE_NAMES);
- if (srcNodes.isEmpty() || start >= srcNodes.size()) {
- return NoneSuggester.get(session);
- }
- String sourceNode = srcNodes.get(start);
- suggester = session.getSuggester(action)
- .hint(Suggester.Hint.SRC_NODE, Collections.singletonList(sourceNode));
- event.getProperties().put(START, ++start);
- break;
- case NONE:
- return NoneSuggester.get(session);
- default:
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unsupported preferredOperation: " + action.toLower() + " specified for node lost trigger");
- }
- break;
- case SEARCHRATE:
- case METRIC:
- case INDEXSIZE:
- List<TriggerEvent.Op> ops = (List<TriggerEvent.Op>)event.getProperty(TriggerEvent.REQUESTED_OPS, Collections.emptyList());
- int start = (Integer)event.getProperty(START, 0);
- if (ops.isEmpty() || start >= ops.size()) {
- return NoneSuggester.get(session);
- }
- TriggerEvent.Op op = ops.get(start);
- suggester = session.getSuggester(op.getAction());
- if (suggester instanceof UnsupportedSuggester) {
- List<TriggerEvent.Op> unsupportedOps = (List<TriggerEvent.Op>)context.getProperties().computeIfAbsent(TriggerEvent.UNSUPPORTED_OPS, k -> new ArrayList<TriggerEvent.Op>());
- unsupportedOps.add(op);
- }
- for (Map.Entry<Suggester.Hint, Object> e : op.getHints().entrySet()) {
- suggester = suggester.hint(e.getKey(), e.getValue());
- }
- suggester = suggester.forceOperation(true);
- event.getProperties().put(START, ++start);
- break;
- case SCHEDULED:
- preferredOp = (String) event.getProperty(AutoScalingParams.PREFERRED_OP, CollectionParams.CollectionAction.MOVEREPLICA.toLower());
- action = CollectionParams.CollectionAction.get(preferredOp);
- suggester = session.getSuggester(action);
- break;
- default:
- throw new UnsupportedOperationException("No support for events other than nodeAdded, nodeLost, searchRate, metric, scheduled and indexSize. Received: " + event.getEventType());
- }
- return suggester;
- }
-
- private Suggester getNodeAddedSuggester(SolrCloudManager cloudManager, Policy.Session session, TriggerEvent event) throws IOException {
- String preferredOp = (String) event.getProperty(AutoScalingParams.PREFERRED_OP, CollectionParams.CollectionAction.MOVEREPLICA.toLower());
- CollectionParams.CollectionAction action = CollectionParams.CollectionAction.get(preferredOp);
-
- Suggester suggester = session.getSuggester(action)
- .hint(Suggester.Hint.TARGET_NODE, event.getProperty(NODE_NAMES));
- switch (action) {
- case ADDREPLICA:
- // add all collection/shard pairs and let policy engine figure out which one
- // to place on the target node
- // todo in future we can prune ineligible collection/shard pairs
- ClusterState clusterState = cloudManager.getClusterStateProvider().getClusterState();
- Set<Pair<String, String>> collShards = new HashSet<>();
- clusterState.getCollectionStates().forEach((collectionName, collectionRef) -> {
- DocCollection docCollection = collectionRef.get();
- if (docCollection != null) {
- docCollection.getActiveSlices().stream()
- .map(slice -> new Pair<>(collectionName, slice.getName()))
- .forEach(collShards::add);
- }
- });
- suggester.hint(Suggester.Hint.COLL_SHARD, collShards);
- break;
- case MOVEREPLICA:
- case NONE:
- break;
- default:
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
- "Unsupported preferredOperation=" + preferredOp + " for node added event");
- }
- return suggester;
- }
-}