diff --git a/changelog/unreleased/SOLR-18370-remove-overseer-roles-api.yml b/changelog/unreleased/SOLR-18370-remove-overseer-roles-api.yml new file mode 100644 index 000000000000..827ebc550f7f --- /dev/null +++ b/changelog/unreleased/SOLR-18370-remove-overseer-roles-api.yml @@ -0,0 +1,8 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc +title: Remove the ADDROLE and REMOVEROLE Collections API commands, their V2 add-role and remove-role counterparts, the CollectionAdminRequest.addRole and removeRole SolrJ methods, and the roles section of CLUSTERSTATUS. Overseer designates are now declared only at startup with node roles, for example -Dsolr.node.roles=data:on,overseer:preferred. +type: removed +authors: + - name: Serhiy Bzhezytskyy +links: + - name: SOLR-18370 + url: https://issues.apache.org/jira/browse/SOLR-18370 diff --git a/dev-docs/overseer/overseer.adoc b/dev-docs/overseer/overseer.adoc index a782e5a0bc8e..dab352310b2d 100644 --- a/dev-docs/overseer/overseer.adoc +++ b/dev-docs/overseer/overseer.adoc @@ -662,12 +662,12 @@ This class is a collection of methods allowing whole cluster level Zookeeper ope === org.apache.solr.cloud.OverseerNodePrioritizer |=== -|Javadoc: _Responsible for prioritization of Overseer nodes, for example with the ADDROLE collection command_ +|Javadoc: _Responsible for prioritization of Overseer nodes: a node declared as `overseer:preferred` through node roles (`-Dsolr.node.roles`) is moved to the front of the Overseer election queue._ |=== -This class was introduced in https://issues.apache.org/jira/browse/SOLR-5476[SOLR-5476]. It allows listing in Zookeeper’s `/roles.json` nodes that are preferred for becoming Overseer (for example nodes with more powerful hardware). +This class was introduced in https://issues.apache.org/jira/browse/SOLR-5476[SOLR-5476]. It reads which nodes are preferred for becoming Overseer (for example nodes with more powerful hardware) from Node Roles, declared at startup with `-Dsolr.node.roles`; the legacy `roles.json`-based ADDROLE/REMOVEROLE API was removed in https://issues.apache.org/jira/browse/SOLR-18370[SOLR-18370]. -Method `prioritizeOverseerNodes()` can be called independently on any thread and works by its side effects. Apparently it is only called by the elected overseer as it starts (from `OverseerTaskProcessor.run()`). It checks if another node is better suited to become overseer. When `/roles.json` is not empty, a preferred (designate) node explicitly configured is strongly encouraged to take over the Overseer role if that’s not already the case. + +Method `prioritizeOverseerNodes()` can be called independently on any thread and works by its side effects. Apparently it is only called by the elected overseer as it starts (from `OverseerTaskProcessor.run()`). It checks if another node is better suited to become overseer. When a preferred (designate) node is explicitly configured, it is strongly encouraged to take over the Overseer role if that’s not already the case. + This is done by sending the designate node a `CoreAdminOperation.OVERSEEROP_OP` with the actual operation `"op"` param equal to `"rejoinAtHead"`. The old first in line node (in the overseer election) is asked to rejoin (by passing `"rejoin"` in `"op"` but any string different than `rejoinAtHead` would do), then the actual leader (Overseer node) is asked to submit its resignation to new elections take place. Processing of `OVERSEEROP_OP` calls into `ZkController.rejoinOverseerElection()` that essentially delegates to `LeaderElector.retryElection()`. diff --git a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java index 0e1a0acd58e7..50acc241bc72 100644 --- a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java +++ b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java @@ -17,9 +17,7 @@ package org.apache.solr.cloud; import java.lang.invoke.MethodHandles; -import java.util.ArrayList; import java.util.List; -import java.util.Map; import org.apache.solr.client.solrj.impl.ZkDistribStateManager; import org.apache.solr.common.SolrException; import org.apache.solr.common.cloud.SolrZkClient; @@ -27,20 +25,19 @@ import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.CoreAdminParams.CoreAdminAction; import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.util.Utils; import org.apache.solr.core.NodeRoles; import org.apache.solr.handler.ClusterAPI; import org.apache.solr.handler.component.ShardHandler; import org.apache.solr.handler.component.ShardHandlerFactory; import org.apache.solr.handler.component.ShardRequest; import org.apache.solr.handler.component.ShardResponse; -import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Responsible for prioritization of Overseer nodes, for example with the ADDROLE collection - * command. + * Responsible for prioritization of Overseer nodes: a node declared as {@code overseer:preferred} + * through node roles ({@code -Dsolr.node.roles}) is moved to the front of the Overseer election + * queue. */ public class OverseerNodePrioritizer { @@ -66,30 +63,11 @@ public OverseerNodePrioritizer( public synchronized void prioritizeOverseerNodes(String overseerId) throws Exception { SolrZkClient zk = zkStateReader.getZkClient(); - List overseerDesignates = new ArrayList<>(); - if (zk.exists(ZkStateReader.ROLES)) { - Map m = (Map) Utils.fromJSON(zk.getData(ZkStateReader.ROLES, null, new Stat())); - @SuppressWarnings("unchecked") - List l = (List) m.get("overseer"); - if (l != null) { - overseerDesignates.addAll(l); - } - } - - List preferredOverseers = + List overseerDesignates = ClusterAPI.getNodesByRole( NodeRoles.Role.OVERSEER, NodeRoles.MODE_PREFERRED, new ZkDistribStateManager(zkStateReader.getZkClient())); - for (String preferred : preferredOverseers) { - if (overseerDesignates.contains(preferred)) { - log.warn( - "Node {} has been configured to be a preferred overseer using both ADDROLE API command " - + "as well as using Node Roles (i.e. -Dsolr.node.roles start up property). Only the latter is recommended.", - preferred); - } - } - overseerDesignates.addAll(preferredOverseers); if (overseerDesignates.isEmpty()) return; String ldr = OverseerTaskProcessor.getLeaderNode(zk); if (overseerDesignates.contains(ldr)) return; diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java index 3ab82e68b1d6..5addde410de1 100644 --- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java +++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java @@ -24,7 +24,7 @@ import static org.apache.solr.common.cloud.ZkStateReader.LIVE_NODE_SOLR_VERSION; import static org.apache.solr.common.cloud.ZkStateReader.REJOIN_AT_HEAD_PROP; import static org.apache.solr.common.cloud.ZkStateReader.UNSUPPORTED_SOLR_XML; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.REPRIORITIZE_OVERSEER; import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE; import io.opentelemetry.api.internal.StringUtils; @@ -2542,34 +2542,17 @@ public void rejoinShardLeaderElection(SolrParams params) { } } - public void checkOverseerDesignate() { - try { - byte[] data = zkClient.getData(ZkStateReader.ROLES, null, new Stat()); - if (data == null) return; - Map roles = (Map) Utils.fromJSON(data); - if (roles == null) return; - List nodeList = (List) roles.get("overseer"); - if (nodeList == null) return; - if (nodeList.contains(getNodeName())) { - setPreferredOverseer(); - } - } catch (NoNodeException nne) { - return; - } catch (Exception e) { - log.warn("could not read the overseer designate ", e); - } - } - public void setPreferredOverseer() throws KeeperException, InterruptedException { MapWriter props = ew -> - ew.put(Overseer.QUEUE_OPERATION, ADDROLE.toString().toLowerCase(Locale.ROOT)) - .put(getNodeName(), getNodeName()) - .put("role", "overseer") - .put("persist", "false"); - log.warn( - "Going to add role {}. It is deprecated to use ADDROLE and consider using Node Roles instead.", - props.jsonStr()); + ew.put( + Overseer.QUEUE_OPERATION, + REPRIORITIZE_OVERSEER.toString().toLowerCase(Locale.ROOT)); + if (log.isInfoEnabled()) { + log.info( + "Asking the Overseer to re-run node prioritization for this preferred overseer: {}", + props.jsonStr()); + } getOverseerCollectionQueue().offer(props); } diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/CollApiCmds.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/CollApiCmds.java index ab8a8c58e4c2..1b6f083a940b 100644 --- a/solr/core/src/java/org/apache/solr/cloud/api/collections/CollApiCmds.java +++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/CollApiCmds.java @@ -30,7 +30,6 @@ import static org.apache.solr.common.params.CollectionAdminParams.COLL_CONF; import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA; import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICAPROP; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.ALIASPROP; import static org.apache.solr.common.params.CollectionParams.CollectionAction.BACKUP; import static org.apache.solr.common.params.CollectionParams.CollectionAction.BALANCESHARDUNIQUE; @@ -60,9 +59,9 @@ import static org.apache.solr.common.params.CollectionParams.CollectionAction.REBALANCELEADERS; import static org.apache.solr.common.params.CollectionParams.CollectionAction.REINDEXCOLLECTION; import static org.apache.solr.common.params.CollectionParams.CollectionAction.RELOAD; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.REMOVEROLE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.RENAME; import static org.apache.solr.common.params.CollectionParams.CollectionAction.REPLACENODE; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.REPRIORITIZE_OVERSEER; import static org.apache.solr.common.params.CollectionParams.CollectionAction.RESTORE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.SPLITSHARD; import static org.apache.solr.common.params.CommonParams.NAME; @@ -164,8 +163,8 @@ private CommandMap(OverseerNodePrioritizer overseerPrioritizer, CollectionComman Map.entry(CREATESNAPSHOT, new CreateSnapshotCmd(ccc)), Map.entry(DELETESNAPSHOT, new DeleteSnapshotCmd(ccc)), Map.entry(SPLITSHARD, new SplitShardCmd(ccc)), - Map.entry(ADDROLE, new OverseerRoleCmd(ccc, ADDROLE, overseerPrioritizer)), - Map.entry(REMOVEROLE, new OverseerRoleCmd(ccc, REMOVEROLE, overseerPrioritizer)), + Map.entry( + REPRIORITIZE_OVERSEER, new OverseerPrioritizationCmd(ccc, overseerPrioritizer)), Map.entry(MOCK_COLL_TASK, new CollApiCmds.MockOperationCmd()), Map.entry(MOCK_SHARD_TASK, new CollApiCmds.MockOperationCmd()), Map.entry(MOCK_REPLICA_TASK, new CollApiCmds.MockOperationCmd()), diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerPrioritizationCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerPrioritizationCmd.java new file mode 100644 index 000000000000..b556deeeffe0 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerPrioritizationCmd.java @@ -0,0 +1,64 @@ +/* + * 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 org.apache.solr.cloud.OverseerNodePrioritizer; +import org.apache.solr.cloud.api.collections.CollApiCmds.CollectionApiCommand; +import org.apache.solr.common.cloud.ZkNodeProps; +import org.apache.solr.common.util.NamedList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** Internal message asking the Overseer to re-run overseer-node prioritization. */ +public class OverseerPrioritizationCmd implements CollectionApiCommand { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private final CollectionCommandContext ccc; + private final OverseerNodePrioritizer overseerPrioritizer; + + public OverseerPrioritizationCmd( + CollectionCommandContext ccc, OverseerNodePrioritizer prioritizer) { + this.ccc = ccc; + this.overseerPrioritizer = prioritizer; + } + + @Override + public void call(AdminCmdContext context, ZkNodeProps message, NamedList results) + throws Exception { + if (ccc.isDistributedCollectionAPI()) { + // No Overseer (not accessible from Collection API command execution in any case) so this + // command can't be run... + log.error( + "Cluster is running with distributed Collection API execution. Ignoring internal overseer" + + " prioritization request."); + return; + } + // if there are too many nodes this may time out, and dedicated overseers are most likely + // configured when there are many nodes, so do it in a separate thread + new Thread( + () -> { + try { + overseerPrioritizer.prioritizeOverseerNodes(ccc.getOverseerId()); + } catch (Exception e) { + log.error("Error in prioritizing Overseer", e); + } + }, + "OverseerPrioritizationThread") + .start(); + } +} diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java deleted file mode 100644 index f789455c0d6c..000000000000 --- a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java +++ /dev/null @@ -1,128 +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 static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.REMOVEROLE; - -import java.lang.invoke.MethodHandles; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import org.apache.solr.cloud.OverseerNodePrioritizer; -import org.apache.solr.common.cloud.SolrZkClient; -import org.apache.solr.common.cloud.ZkNodeProps; -import org.apache.solr.common.cloud.ZkStateReader; -import org.apache.solr.common.params.CollectionParams.CollectionAction; -import org.apache.solr.common.util.CollectionUtil; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.Utils; -import org.apache.solr.logging.DeprecationLog; -import org.apache.zookeeper.data.Stat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class OverseerRoleCmd implements CollApiCmds.CollectionApiCommand { - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - private final CollectionCommandContext ccc; - private final CollectionAction operation; - private final OverseerNodePrioritizer overseerPrioritizer; - - public OverseerRoleCmd( - CollectionCommandContext ccc, - CollectionAction operation, - OverseerNodePrioritizer prioritizer) { - this.ccc = ccc; - this.operation = operation; - this.overseerPrioritizer = prioritizer; - } - - @Override - public void call(AdminCmdContext adminCmdContext, ZkNodeProps message, NamedList results) - throws Exception { - if (ccc.isDistributedCollectionAPI()) { - // No Overseer (not accessible from Collection API command execution in any case) so this - // command can't be run... - log.error( - "Cluster is running with distributed Collection API execution. Ignoring Collection API operation {}", - operation); - return; - } - ZkStateReader zkStateReader = ccc.getZkStateReader(); - SolrZkClient zkClient = zkStateReader.getZkClient(); - Map> roles = null; - String node = message.getStr("node"); - if ("false".equals(message.getStr("persist"))) { // no need to persist to roles.json - runPrioritizer(); - return; - } - - String roleName = message.getStr("role"); - boolean nodeExists = zkClient.exists(ZkStateReader.ROLES); - if (nodeExists) { - @SuppressWarnings("unchecked") - Map> tmp = - (Map>) - Utils.fromJSON(zkClient.getData(ZkStateReader.ROLES, null, new Stat())); - roles = tmp; - } else { - roles = CollectionUtil.newLinkedHashMap(1); - } - - List nodeList = roles.computeIfAbsent(roleName, k -> new ArrayList<>()); - if (ADDROLE == operation) { - DeprecationLog.log( - "CollectionAPI-" + operation, - "The " - + operation - + " API is deprecated and will be removed in Solr 11. " - + "Please transition to using Node Roles (-Dsolr.node.roles) at startup instead."); - log.info("Overseer role added to {}", node); - if (!nodeList.contains(node)) nodeList.add(node); - } else if (REMOVEROLE == operation) { - DeprecationLog.log( - "CollectionAPI-" + operation, - "The " - + operation - + " API is deprecated and will be removed in Solr 11. " - + "Please transition to using Node Roles (-Dsolr.node.roles) at startup instead."); - log.info("Overseer role removed from {}", node); - nodeList.remove(node); - } - - zkClient.makePath(ZkStateReader.ROLES, Utils.toJSON(roles), false); - runPrioritizer(); - } - - private void runPrioritizer() { - // if there are too many nodes this command may time out. And most likely dedicated - // overseers are created when there are too many nodes . So , do this operation in a separate - // thread - new Thread( - () -> { - try { - overseerPrioritizer.prioritizeOverseerNodes(ccc.getOverseerId()); - } catch (Exception e) { - log.error("Error in prioritizing Overseer", e); - } - }, - "OverseerPrioritizationThread") - .start(); - } -} diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index 2afae17b49ec..820a96e9bbd5 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -1107,7 +1107,6 @@ protected void configure() { throw new SolrException(ErrorCode.SERVER_ERROR, e); } } - zkSys.getZkController().checkOverseerDesignate(); } // This is a bit redundant but these are two distinct concepts for all they're accomplished at diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java index 13b2cb8a6828..4e3eb35e3bca 100644 --- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java @@ -22,10 +22,8 @@ import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST; import static org.apache.solr.cloud.api.collections.CollectionHandlingUtils.REQUESTID; import static org.apache.solr.common.params.CollectionParams.ACTION; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETESTATUS; import static org.apache.solr.common.params.CollectionParams.CollectionAction.OVERSEERSTATUS; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.REMOVEROLE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.REQUESTSTATUS; import static org.apache.solr.core.RateLimiterConfig.RL_CONFIG_KEY; import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM; @@ -43,15 +41,12 @@ import org.apache.solr.client.solrj.cloud.DistribStateManager; import org.apache.solr.client.solrj.request.beans.RateLimiterPayload; import org.apache.solr.common.SolrException; -import org.apache.solr.common.annotation.JsonProperty; import org.apache.solr.common.cloud.ClusterProperties; import org.apache.solr.common.cloud.ZkStateReader; import org.apache.solr.common.params.CollectionParams.CollectionAction; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.DefaultSolrParams; import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.common.util.ReflectMapWriter; -import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.Utils; import org.apache.solr.core.CoreContainer; import org.apache.solr.core.NodeRoles; @@ -257,24 +252,6 @@ private CoreContainer getCoreContainer() { @EndPoint(method = POST, path = "/cluster", permission = COLL_EDIT_PERM) public class Commands { - @Command(name = "add-role") - @Deprecated(since = "10.1") - public void addRole(PayloadObj obj) throws Exception { - RoleInfo info = obj.get(); - Map m = new SimpleOrderedMap<>(info); - m.put("action", ADDROLE.toString()); - collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse()); - } - - @Command(name = "remove-role") - @Deprecated(since = "10.1") - public void removeRole(PayloadObj obj) throws Exception { - RoleInfo info = obj.get(); - Map m = new SimpleOrderedMap<>(info); - m.put("action", REMOVEROLE.toString()); - collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse()); - } - @Command(name = "set-ratelimiter") public void setRateLimiters(PayloadObj payLoad) { RateLimiterPayload rateLimiterConfig = payLoad.get(); @@ -288,12 +265,4 @@ public void setRateLimiters(PayloadObj payLoad) { } } } - - public static class RoleInfo implements ReflectMapWriter { - @JsonProperty(required = true) - public String node; - - @JsonProperty(required = true) - public String role; - } } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ClusterStatus.java b/solr/core/src/java/org/apache/solr/handler/admin/ClusterStatus.java index cfd2377ebe6d..f7f206625066 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/ClusterStatus.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/ClusterStatus.java @@ -51,9 +51,6 @@ public class ClusterStatus { public static final String LIVENODES_PROP = "liveNodes"; public static final String CLUSTER_PROP = "clusterProperties"; - @Deprecated(since = "10.1") - public static final String ROLES_PROP = "roles"; - public static final String ALIASES_PROP = "aliases"; /** Shard / collection health state. */ @@ -111,7 +108,6 @@ public void getClusterStatus(NamedList results, SolrVersion solrVersion) boolean includeAll = solrParams.getBool(INCLUDE_ALL, true); boolean withLiveNodes = solrParams.getBool(LIVENODES_PROP, includeAll); boolean withClusterProperties = solrParams.getBool(CLUSTER_PROP, includeAll); - boolean withRoles = solrParams.getBool(ROLES_PROP, includeAll); boolean withCollection = includeAll || (collection != null); boolean withAliases = solrParams.getBool(ALIASES_PROP, includeAll); @@ -144,18 +140,6 @@ public void getClusterStatus(NamedList results, SolrVersion solrVersion) clusterStatus.add("properties", clusterProps); } - // add the roles map - if (withRoles) { - Map roles = Map.of(); - if (zkStateReader.getZkClient().exists(ZkStateReader.ROLES)) { - roles = - (Map) - Utils.fromJSON( - zkStateReader.getZkClient().getData(ZkStateReader.ROLES, null, null)); - } - clusterStatus.add("roles", roles); - } - results.add("cluster", clusterStatus); } 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 6e3c9e453d78..ad7d16345522 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 @@ -44,7 +44,6 @@ import static org.apache.solr.common.params.CollectionAdminParams.SHARD; import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA; import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICAPROP; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.ALIASPROP; import static org.apache.solr.common.params.CollectionParams.CollectionAction.BACKUP; import static org.apache.solr.common.params.CollectionParams.CollectionAction.BALANCESHARDUNIQUE; @@ -80,7 +79,6 @@ import static org.apache.solr.common.params.CollectionParams.CollectionAction.REBALANCELEADERS; import static org.apache.solr.common.params.CollectionParams.CollectionAction.REINDEXCOLLECTION; import static org.apache.solr.common.params.CollectionParams.CollectionAction.RELOAD; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.REMOVEROLE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.RENAME; import static org.apache.solr.common.params.CollectionParams.CollectionAction.REPLACENODE; import static org.apache.solr.common.params.CollectionParams.CollectionAction.REQUESTSTATUS; @@ -114,7 +112,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.solr.api.AnnotatedApi; @@ -336,8 +333,6 @@ void invokeAction( } } - static final Set KNOWN_ROLES = Set.of("overseer"); - public static long DEFAULT_COLLECTION_OP_TIMEOUT = 180 * 1000; public SolrResponse submitCollectionApiCommand(AdminCmdContext adminCmdContext, ZkNodeProps m) @@ -740,24 +735,6 @@ public enum CollectionOperation implements CollectionOp { "target.collection"); return copy(req.getParams(), map, "forward.timeout", FOLLOW_ALIASES); }), - ADDROLE_OP( - ADDROLE, - (req, rsp, h) -> { - Map map = copy(req.getParams().required(), null, "role", "node"); - if (!KNOWN_ROLES.contains(map.get("role"))) - throw new SolrException( - ErrorCode.BAD_REQUEST, "Unknown role. Supported roles are ," + KNOWN_ROLES); - return map; - }), - REMOVEROLE_OP( - REMOVEROLE, - (req, rsp, h) -> { - Map map = copy(req.getParams().required(), null, "role", "node"); - if (!KNOWN_ROLES.contains(map.get("role"))) - throw new SolrException( - ErrorCode.BAD_REQUEST, "Unknown role. Supported roles are ," + KNOWN_ROLES); - return map; - }), CLUSTERPROP_OP( CLUSTERPROP, (req, rsp, h) -> { diff --git a/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java index 3da6970e1f6e..3f8c3f44b143 100644 --- a/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java @@ -21,13 +21,12 @@ import java.lang.invoke.MethodHandles; import java.net.URI; -import java.util.Collections; -import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.common.util.TimeSource; +import org.apache.solr.core.NodeRoles; import org.apache.solr.embedded.JettySolrRunner; import org.apache.solr.util.TimeOut; import org.apache.zookeeper.KeeperException; @@ -42,7 +41,12 @@ public class OverseerRolesTest extends SolrCloudTestCase { @BeforeClass public static void setupCluster() throws Exception { - configureCluster(4).addConfig("conf", configset("cloud-minimal")).configure(); + // SolrCloudTestCase randomises solr.cloud.overseer.enabled; this test is about the Overseer + // election, so pin it on rather than skipping half the runs. + configureCluster(4) + .withOverseer(true) + .addConfig("conf", configset("cloud-minimal")) + .configure(); } public static void waitForNewOverseer( @@ -97,131 +101,49 @@ private void logOverseerState() throws KeeperException, InterruptedException { } } + /** + * A node started with {@code -Dsolr.node.roles=overseer:preferred} must become the Overseer + * without waiting for the current Overseer to restart. + */ @Test - public void testOverseerRole() throws Exception { - if (new CollectionAdminRequest.RequestApiDistributedProcessing() - .process(cluster.getSolrClient()) - .getIsCollectionApiDistributed()) { - log.info("Skipping test because Collection API is distributed"); - return; - } - + public void testPreferredOverseerNodeRoleTakesOver() throws Exception { + assertFalse( + "the Overseer must be enabled for this test", + new CollectionAdminRequest.RequestApiDistributedProcessing() + .process(cluster.getSolrClient()) + .getIsCollectionApiDistributed()); logOverseerState(); - List nodes = - OverseerCollectionConfigSetProcessor.getSortedOverseerNodeNames(zkClient()); - // Remove the OVERSEER role, in case it was already assigned by another test in this suite - for (String node : nodes) { - CollectionAdminRequest.removeRole(node, "overseer").process(cluster.getSolrClient()); + final String overseerBefore = getLeaderNode(zkClient()); + assertNotNull("no Overseer to start from", overseerBefore); + + final JettySolrRunner preferred; + System.setProperty(NodeRoles.NODE_ROLES_PROP, "data:on,overseer:preferred"); + try { + preferred = cluster.startJettySolrRunner(); + } finally { + System.clearProperty(NodeRoles.NODE_ROLES_PROP); } - String overseer1 = OverseerCollectionConfigSetProcessor.getLeaderNode(zkClient()); - nodes.remove(overseer1); - - Collections.shuffle(nodes, random()); - String overseer2 = nodes.get(0); - log.info("### Setting overseer designate {}", overseer2); - - CollectionAdminRequest.addRole(overseer2, "overseer").process(cluster.getSolrClient()); + final String preferredNodeName = preferred.getNodeName(); + log.info("Started {} as a preferred overseer", preferredNodeName); - waitForNewOverseer(15, overseer2, false); + assertEquals( + "the new node did not take the preferred overseer role", + NodeRoles.MODE_PREFERRED, + preferred.getCoreContainer().nodeRoles.getRoleMode(NodeRoles.Role.OVERSEER)); - // add another node as overseer - nodes.remove(overseer2); - Collections.shuffle(nodes, random()); - - String overseer3 = nodes.get(0); - log.info("### Adding another overseer designate {}", overseer3); - CollectionAdminRequest.addRole(overseer3, "overseer").process(cluster.getSolrClient()); - - // kill the current overseer, and check that the new designate becomes the new overseer - JettySolrRunner leaderJetty = getOverseerJetty(); + // the node published its role and nudged the Overseer, so it must take over + waitForNewOverseer(30, preferredNodeName, false); logOverseerState(); + assertEquals( + "the preferred node should be the Overseer", preferredNodeName, getLeaderNode(zkClient())); - leaderJetty.stop(); - waitForNewOverseer(10, overseer3, false); - - // add another node as overseer - nodes.remove(overseer3); - Collections.shuffle(nodes, random()); - String overseer4 = nodes.get(0); - log.info("### Adding last overseer designate {}", overseer4); - CollectionAdminRequest.addRole(overseer4, "overseer").process(cluster.getSolrClient()); - logOverseerState(); - - // remove the overseer role from the current overseer - CollectionAdminRequest.removeRole(overseer3, "overseer").process(cluster.getSolrClient()); - waitForNewOverseer(15, overseer4, false); - - // Add it back again - we now have two delegates, 4 and 3 - CollectionAdminRequest.addRole(overseer3, "overseer").process(cluster.getSolrClient()); - - // explicitly tell the overseer to quit - String leaderId = OverseerCollectionConfigSetProcessor.getLeaderId(zkClient()); - String leader = OverseerCollectionConfigSetProcessor.getLeaderNode(zkClient()); - log.info("### Sending QUIT to overseer {}", leader); - getOverseerJetty() - .getCoreContainer() - .getZkController() - .getOverseer() - .sendQuitToOverseer(leaderId); - - waitForNewOverseer(15, s -> Objects.equals(leader, s) == false, false); - - Thread.sleep(1000); - - logOverseerState(); + // and it must still be reachable as an ordinary node assertTrue( - "The old leader should have rejoined election", - OverseerCollectionConfigSetProcessor.getSortedOverseerNodeNames(zkClient()) - .contains(leader)); - - leaderJetty.start(); // starting this back, just for good measure - } - - @Test - public void testDesignatedOverseerRestarts() throws Exception { - if (new CollectionAdminRequest.RequestApiDistributedProcessing() - .process(cluster.getSolrClient()) - .getIsCollectionApiDistributed()) { - log.info("Skipping test because Collection API is distributed"); - return; - } - logOverseerState(); - // Remove the OVERSEER role, in case it was already assigned by another test in this suite - List nodes = - OverseerCollectionConfigSetProcessor.getSortedOverseerNodeNames(zkClient()); - // We want to remove from the last (in election order) to the first. - // This way the current overseer will have its role removed last, - // so there will not be any elections. - Collections.reverse(nodes); - for (String node : nodes) { - CollectionAdminRequest.removeRole(node, "overseer").process(cluster.getSolrClient()); - } - String overseer1 = OverseerCollectionConfigSetProcessor.getLeaderNode(zkClient()); - int counter = 0; - while (overseer1 == null && counter < 10) { - overseer1 = OverseerCollectionConfigSetProcessor.getLeaderNode(zkClient()); - Thread.sleep(1000); - } - - // Setting overseer role to the current overseer - CollectionAdminRequest.addRole(overseer1, "overseer").process(cluster.getSolrClient()); - waitForNewOverseer(15, overseer1, false); - JettySolrRunner leaderJetty = getOverseerJetty(); + cluster.getZkStateReader().getClusterState().getLiveNodes().contains(preferredNodeName)); + cluster.stopJettySolrRunner(preferred); + cluster.waitForJettyToStop(preferred); + waitForNewOverseer(30, s -> s != null && !s.equals(preferredNodeName), false); logOverseerState(); - // kill the current overseer, and check that the next node in the election queue assumes - // leadership - leaderJetty.stop(); - log.info("Killing designated overseer: {}", overseer1); - - // after 5 seconds, bring back dead designated overseer and assert that it assumes leadership - // "right away", i.e. without any other node assuming leadership before this node becomes - // leader. - Thread.sleep(5); - logOverseerState(); - log.info("Starting back the prioritized overseer.."); - leaderJetty.start(); - // assert that there is just a single leadership transition - waitForNewOverseer(15, overseer1, true); } } diff --git a/solr/core/src/test/org/apache/solr/cloud/RollingRestartTest.java b/solr/core/src/test/org/apache/solr/cloud/RollingRestartTest.java deleted file mode 100644 index caea6e59015e..000000000000 --- a/solr/core/src/test/org/apache/solr/cloud/RollingRestartTest.java +++ /dev/null @@ -1,177 +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; - -import java.lang.invoke.MethodHandles; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import org.apache.lucene.tests.util.LuceneTestCase.Nightly; -import org.apache.solr.client.solrj.request.CollectionAdminRequest; -import org.apache.solr.common.cloud.SolrZkClient; -import org.apache.solr.common.cloud.ZkStateReader; -import org.apache.zookeeper.KeeperException; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Nightly -public class RollingRestartTest extends AbstractFullDistribZkTestBase { - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - private static final long MAX_WAIT_TIME = TimeUnit.NANOSECONDS.convert(300, TimeUnit.SECONDS); - - public RollingRestartTest() { - sliceCount = 2; - fixShardCount(TEST_NIGHTLY ? 16 : 2); - } - - @Override - public void distribSetUp() throws Exception { - super.distribSetUp(); - useFactory("solr.StandardDirectoryFactory"); - } - - @Test - public void test() throws Exception { - if (new CollectionAdminRequest.RequestApiDistributedProcessing() - .process(cloudClient) - .getIsCollectionApiDistributed()) { - log.info("Skipping test because Collection API is distributed"); - return; - } - - waitForRecoveriesToFinish(false); - - restartWithRolesTest(); - - waitForRecoveriesToFinish(false); - } - - public void restartWithRolesTest() throws Exception { - ZkStateReader zkStateReader = ZkStateReader.from(cloudClient); - String leader = OverseerCollectionConfigSetProcessor.getLeaderNode(zkStateReader.getZkClient()); - assertNotNull(leader); - log.info("Current overseer leader = {}", leader); - - zkStateReader.getZkClient().printLayoutToStream(System.out); - - int numDesignateOverseers = TEST_NIGHTLY ? 16 : 2; - numDesignateOverseers = Math.max(getShardCount(), numDesignateOverseers); - List designates = new ArrayList<>(); - List designateJettys = new ArrayList<>(); - for (int i = 0; i < numDesignateOverseers; i++) { - int n = random().nextInt(getShardCount()); - String nodeName = cloudJettys.get(n).nodeName; - log.info("Chose {} as overseer designate", nodeName); - CollectionAdminRequest.addRole(nodeName, "overseer").process(cloudClient); - designates.add(nodeName); - designateJettys.add(cloudJettys.get(n)); - } - - waitUntilOverseerDesignateIsLeader(zkStateReader.getZkClient(), designates); - - zkStateReader.getZkClient().printLayoutToStream(System.out); - - boolean sawLiveDesignate = false; - int numRestarts = 1 + random().nextInt(TEST_NIGHTLY ? 12 : 2); - for (int i = 0; i < numRestarts; i++) { - log.info("Rolling restart #{}", i + 1); // nowarn - for (CloudJettyRunner cloudJetty : designateJettys) { - log.info("Restarting {}", cloudJetty); - chaosMonkey.stopJetty(cloudJetty); - zkStateReader.updateLiveNodes(); - boolean liveDesignates = - zkStateReader.getClusterState().getLiveNodes().stream().anyMatch(designates::contains); - if (liveDesignates) { - boolean success = - waitUntilOverseerDesignateIsLeader(zkStateReader.getZkClient(), designates); - if (!success) { - leader = - OverseerCollectionConfigSetProcessor.getLeaderNode(zkStateReader.getZkClient()); - if (leader == null) - log.error( - "NOOVERSEER election queue is : {}", - OverseerCollectionConfigSetProcessor.getSortedElectionNodes( - zkStateReader.getZkClient(), "/overseer_elect/election")); - fail("No overseer designate as leader found after restart #" + (i + 1) + ": " + leader); - } - } - cloudJetty.jetty.start(); - boolean success = - waitUntilOverseerDesignateIsLeader(zkStateReader.getZkClient(), designates); - if (!success) { - leader = OverseerCollectionConfigSetProcessor.getLeaderNode(zkStateReader.getZkClient()); - if (leader == null) - log.error( - "NOOVERSEER election queue is :{}", - OverseerCollectionConfigSetProcessor.getSortedElectionNodes( - zkStateReader.getZkClient(), "/overseer_elect/election")); - fail("No overseer leader found after restart #" + (i + 1) + ": " + leader); - } - - zkStateReader.updateLiveNodes(); - sawLiveDesignate = - zkStateReader.getClusterState().getLiveNodes().stream().anyMatch(designates::contains); - } - } - - assertTrue("Test may not be working if we never saw a live designate", sawLiveDesignate); - - leader = OverseerCollectionConfigSetProcessor.getLeaderNode(zkStateReader.getZkClient()); - assertNotNull(leader); - log.info("Current overseer leader (after restart) = {}", leader); - - zkStateReader.getZkClient().printLayoutToStream(System.out); - } - - static boolean waitUntilOverseerDesignateIsLeader( - SolrZkClient testZkClient, List overseerDesignates) - throws KeeperException, InterruptedException { - long now = System.nanoTime(); - // the maximum amount of time we're willing to wait to see the designate as leader - long maxTimeout = now + RollingRestartTest.MAX_WAIT_TIME; - long timeout = now + TimeUnit.NANOSECONDS.convert(60, TimeUnit.SECONDS); - boolean firstTime = true; - int stableCheckTimeout = 2000; - String oldleader = null; - while (System.nanoTime() < timeout && System.nanoTime() < maxTimeout) { - String newLeader = OverseerCollectionConfigSetProcessor.getLeaderNode(testZkClient); - if (newLeader != null && !newLeader.equals(oldleader)) { - // the leaders have changed, let's move the timeout further - timeout = System.nanoTime() + TimeUnit.NANOSECONDS.convert(60, TimeUnit.SECONDS); - log.info( - "oldLeader={} newLeader={} - Advancing timeout to: {}", oldleader, newLeader, timeout); - oldleader = newLeader; - } - if (!overseerDesignates.contains(newLeader)) { - Thread.sleep(500); - } else { - if (firstTime) { - firstTime = false; - Thread.sleep(stableCheckTimeout); - } else { - return true; - } - } - } - if (System.nanoTime() < maxTimeout) { - log.error("Max wait time exceeded"); - } - return false; - } -} diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java index d0de765bd69d..c7f784490206 100644 --- a/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java +++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java @@ -70,12 +70,7 @@ public TestCollectionAPI() { @Test @ShardsFixed(num = 2) public void test() throws Exception { - final boolean isDistributedCollectionApi; try (CloudSolrClient client = createCloudClient(null)) { - isDistributedCollectionApi = - new CollectionAdminRequest.RequestApiDistributedProcessing() - .process(client) - .getIsCollectionApiDistributed(); CollectionAdminRequest.Create req; if (useTlogReplicas()) { req = CollectionAdminRequest.createCollection(COLLECTION_NAME, "conf1", 2, 0, 2, 1); @@ -99,9 +94,6 @@ public void test() throws Exception { clusterStatusWithCollectionHealthState(); clusterStatusWithRouteKey(); clusterStatusAliasTest(); - if (!isDistributedCollectionApi) { - clusterStatusRolesTest(); - } clusterStatusBadCollectionTest(); replicaPropTest(); clusterStatusZNodeVersion(); @@ -685,38 +677,6 @@ private void clusterStatusWithCollectionAndShardJSON() throws IOException, SolrS } } - private void clusterStatusRolesTest() throws Exception { - try (CloudSolrClient client = createCloudClient(null)) { - client.connect(); - Replica replica = ZkStateReader.from(client).getLeaderRetry(DEFAULT_COLLECTION, SHARD1); - - ModifiableSolrParams params = new ModifiableSolrParams(); - params.set("action", CollectionParams.CollectionAction.ADDROLE.toString()); - params.set("node", replica.getNodeName()); - params.set("role", "overseer"); - var request = - new GenericSolrRequest(METHOD.GET, "/admin/collections", SolrRequestType.ADMIN, params); - client.request(request); - - params = new ModifiableSolrParams(); - params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString()); - params.set("collection", DEFAULT_COLLECTION); - request = - new GenericSolrRequest(METHOD.GET, "/admin/collections", SolrRequestType.ADMIN, params); - - NamedList rsp = client.request(request); - NamedList cluster = (NamedList) rsp.get("cluster"); - assertNotNull("Cluster state should not be null", cluster); - @SuppressWarnings({"unchecked"}) - Map roles = (Map) cluster.get("roles"); - assertNotNull("Role information should not be null", roles); - List overseer = (List) roles.get("overseer"); - assertNotNull(overseer); - assertEquals(1, overseer.size()); - assertTrue(overseer.contains(replica.getNodeName())); - } - } - private void clusterStatusBadCollectionTest() throws Exception { try (CloudSolrClient client = createCloudClient(null)) { ModifiableSolrParams params = new ModifiableSolrParams(); diff --git a/solr/core/src/test/org/apache/solr/handler/V2ClusterAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/V2ClusterAPIMappingTest.java index b41640f15341..bfc80bd4787b 100644 --- a/solr/core/src/test/org/apache/solr/handler/V2ClusterAPIMappingTest.java +++ b/solr/core/src/test/org/apache/solr/handler/V2ClusterAPIMappingTest.java @@ -101,32 +101,6 @@ public void testDeleteCommandStatusAllParams() throws Exception { assertEquals("someId", v1Params.get(REQUESTID)); } - @Test - public void testAddRoleAllParams() throws Exception { - final SolrParams v1Params = - captureConvertedV1Params( - "/cluster", - "POST", - "{'add-role': {" + "'node': 'some_node_name', " + "'role':'some_role'}}"); - - assertEquals(CollectionParams.CollectionAction.ADDROLE.toString(), v1Params.get(ACTION)); - assertEquals("some_node_name", v1Params.get("node")); - assertEquals("some_role", v1Params.get("role")); - } - - @Test - public void testRemoveRoleAllParams() throws Exception { - final SolrParams v1Params = - captureConvertedV1Params( - "/cluster", - "POST", - "{'remove-role': {" + "'node': 'some_node_name', " + "'role':'some_role'}}"); - - assertEquals(CollectionParams.CollectionAction.REMOVEROLE.toString(), v1Params.get(ACTION)); - assertEquals("some_node_name", v1Params.get("node")); - assertEquals("some_role", v1Params.get("role")); - } - private SolrParams captureConvertedV1Params(String path, String method, String v2RequestBody) throws Exception { return doCaptureParams(path, method, v2RequestBody, mockCollectionsHandler); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/TestCollectionAPIs.java b/solr/core/src/test/org/apache/solr/handler/admin/TestCollectionAPIs.java index eaa40be34e85..99828d971a0c 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/TestCollectionAPIs.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/TestCollectionAPIs.java @@ -101,20 +101,6 @@ public void testCommands() throws Exception { "{split:{ splitKey:id12345, coreProperties : {prop1:prop1Val, prop2:prop2Val} }}", "{collection: collName , split.key : id12345 , operation : splitshard, property.prop1:prop1Val, property.prop2: prop2Val}"); - compareOutput( - apiBag, - "/cluster", - POST, - "{add-role : {role : overseer, node : 'localhost_8978'} }", - "{operation : addrole ,role : overseer, node : 'localhost_8978'}"); - - compareOutput( - apiBag, - "/cluster", - POST, - "{remove-role : {role : overseer, node : 'localhost_8978'} }", - "{operation : removerole ,role : overseer, node : 'localhost_8978'}"); - compareOutput( apiBag, "/collections/coll1", diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cluster-node-management.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/cluster-node-management.adoc index 46517de459d0..0c06f73b92b9 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/cluster-node-management.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cluster-node-management.adoc @@ -125,15 +125,6 @@ If set to true, returns the status of live nodes in the cluster. + If set to true, returns the properties of the cluster. -`roles`:: -+ -[%autowidth,frame=none] -|=== -|Optional |Default: will default to the default value of `includeAll` parameter specified below -|=== -+ -If set to true, returns the roles within the cluster. - `includeAll`:: + [%autowidth,frame=none] @@ -141,7 +132,7 @@ If set to true, returns the roles within the cluster. |Optional |Default: true |=== + -If set to `true`, returns all information pertaining to live nodes, collections, aliases, cluster properties, roles, etc. +If set to `true`, returns all information pertaining to live nodes, collections, aliases, cluster properties, etc. If set to `false`, the information returned is based on the other specified parameters. === CLUSTERSTATUS Response @@ -212,11 +203,6 @@ http://localhost:8983/solr/admin/collections?action=CLUSTERSTATUS "collection2":{"key": "value"} }, "aliases":{ "both_collections":"collection1,collection2" }, - "roles":{ - "overseer":[ - "127.0.1.1:8983_solr", - "127.0.1.1:7574_solr"] - }, "live_nodes":[ "127.0.1.1:7574_solr", "127.0.1.1:7500_solr", @@ -915,178 +901,6 @@ The node to be removed. + Request ID to track this action which will be xref:configuration-guide:collections-api.adoc#asynchronous-calls[processed asynchronously]. -[[addrole]] -== ADDROLE: Add a Role - -Assigns a role to a given node in the cluster. -The only supported role is `overseer`. - -Use this command to dedicate a particular node as Overseer. -Invoke it multiple times to add more nodes. -This is useful in large clusters where an Overseer is likely to get overloaded. -If available, one among the list of nodes which are assigned the 'overseer' role would become the overseer. -The system would assign the role to any other node if none of the designated nodes are up and running. - -[tabs#addrole-request] -====== -V1 API:: -+ -==== -[source,bash] ----- -http://localhost:8983/solr/admin/collections?action=ADDROLE&role=overseer&node=localhost:8983_solr - ----- -==== - -V2 API:: -+ -==== -[source,bash] ----- -curl -X POST http://localhost:8983/api/cluster -H 'Content-Type: application/json' -d ' - { - "add-role": { - "role": "overseer", - "node": "localhost:8983_solr" - } - } -' ----- -==== -====== - -=== ADDROLE Parameters - -`role`:: -+ -[%autowidth,frame=none] -|=== -s|Required |Default: none -|=== -+ -The name of the role. -The only supported role as of now is `overseer`. - -`node`:: -+ -[%autowidth,frame=none] -|=== -s|Required |Default: none -|=== -+ -The name of the node that will be assigned the role. -It is possible to assign a role even before that node is started. - -=== ADDROLE Response - -The response will include the status of the request and the properties that were updated or removed. -If the status is anything other than "0", an error message will explain why the request failed. - -=== Examples using ADDROLE - -*Input* - -[source,text] ----- -http://localhost:8983/solr/admin/collections?action=ADDROLE&role=overseer&node=192.167.1.2:8983_solr&wt=xml ----- - -*Output* - -[source,xml] ----- - - - 0 - 0 - - ----- - -[[removerole]] -== REMOVEROLE: Remove Role - -Remove an assigned role. -This API is used to undo the roles assigned using ADDROLE operation - -[tabs#removerole-request] -====== -V1 API:: -+ -==== -[source,bash] ----- -http://localhost:8983/solr/admin/collections?action=REMOVEROLE&role=overseer&node=localhost:8983_solr - ----- -==== - -V2 API:: -+ -==== -[source,bash] ----- -curl -X POST http://localhost:8983/api/cluster -H 'Content-Type: application/json' -d ' - { - "remove-role": { - "role": "overseer", - "node": "localhost:8983_solr" - } - } -' ----- -==== -====== - -=== REMOVEROLE Parameters - -`role`:: -+ -[%autowidth,frame=none] -|=== -s|Required |Default: none -|=== -+ -The name of the role. -The only supported role as of now is `overseer`. - -`node`:: -+ -[%autowidth,frame=none] -|=== -s|Required |Default: none -|=== -+ -The name of the node where the role should be removed. - - -=== REMOVEROLE Response - -The response will include the status of the request and the properties that were updated or removed. -If the status is anything other than "0", an error message will explain why the request failed. - -=== Examples using REMOVEROLE - -*Input* - -[source,text] ----- -http://localhost:8983/solr/admin/collections?action=REMOVEROLE&role=overseer&node=192.167.1.2:8983_solr&wt=xml ----- - -*Output* - -[source,xml] ----- - - - 0 - 0 - - ----- - [[overseerstatus]] == OVERSEERSTATUS: Overseer Status and Statistics diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/rule-based-authorization-plugin.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/rule-based-authorization-plugin.adoc index 6996a7277f4a..c243186a9280 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/rule-based-authorization-plugin.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/rule-based-authorization-plugin.adoc @@ -419,8 +419,6 @@ Specifically, the following actions of the Collections API would be allowed: | ADDREPLICA | CLUSTERPROP | MIGRATE -| ADDROLE -| REMOVEROLE | ADDREPLICAPROP | DELETEREPLICAPROP | BALANCESHARDUNIQUE diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc new file mode 100644 index 000000000000..ac2de0f81376 --- /dev/null +++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-11.adoc @@ -0,0 +1,43 @@ += Major Changes in Solr 11 +// 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. + +Solr 11.0 is a major new release of Solr. + +This page highlights the most important changes including new features and changes in default behavior as well as previously deprecated features that have now been removed. + +== Solr 11 Upgrade Planning + +Before starting an upgrade to this version of Solr, please be sure to review all information about changes from the version you are currently on up to this one, to include the minor version number changes as well. +For example, if you are currently using Solr 10.1, you should review changes made in all subsequent 10.x releases in addition to the 11.0-specific changes on this page. + +== Removed Features + +=== Overseer Roles API + +The `ADDROLE` and `REMOVEROLE` Collections API commands have been removed, together with their V2 `add-role` and `remove-role` counterparts, the `CollectionAdminRequest.addRole` and `CollectionAdminRequest.removeRole` SolrJ methods, and the `roles` section of the `CLUSTERSTATUS` response. +The `/roles.json` node in ZooKeeper is no longer read or written; an existing one is simply ignored and may be deleted. + +Overseer designates are now declared at startup with node roles, for example: + +[source,bash] +---- +bin/solr start -Dsolr.node.roles=data:on,overseer:preferred +---- + +A node started this way asks the Overseer to re-run its node prioritization, so a preferred node takes over without waiting for the current Overseer to restart. +Note that node roles are fixed for the lifetime of a node: unlike `ADDROLE`, they cannot be changed on a running node. diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/solr-upgrade-notes.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/solr-upgrade-notes.adoc index 061aea238e4a..2d51b6c54859 100644 --- a/solr/solr-ref-guide/modules/upgrade-notes/pages/solr-upgrade-notes.adoc +++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/solr-upgrade-notes.adoc @@ -1,5 +1,6 @@ = Solr Upgrade Notes -:page-children: major-changes-in-solr-10, \ +:page-children: major-changes-in-solr-11, \ + major-changes-in-solr-10, \ major-changes-in-solr-9, \ major-changes-in-solr-8, \ major-changes-in-solr-7, \ diff --git a/solr/solr-ref-guide/modules/upgrade-notes/upgrade-nav.adoc b/solr/solr-ref-guide/modules/upgrade-notes/upgrade-nav.adoc index 455fd81b6a72..5b253dbf57c7 100644 --- a/solr/solr-ref-guide/modules/upgrade-notes/upgrade-nav.adoc +++ b/solr/solr-ref-guide/modules/upgrade-notes/upgrade-nav.adoc @@ -16,6 +16,7 @@ // under the License. * xref:solr-upgrade-notes.adoc[] +** xref:major-changes-in-solr-11.adoc[] ** xref:major-changes-in-solr-10.adoc[] ** xref:major-changes-in-solr-9.adoc[] ** xref:major-changes-in-solr-8.adoc[] diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java index 4f0c3bb38366..c1a404d08433 100644 --- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java +++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/ZkStateReader.java @@ -105,16 +105,9 @@ public class ZkStateReader implements SolrCloseable { public static final String COLLECTIONS_ZKNODE = "/collections"; public static final String LIVE_NODES_ZKNODE = "/live_nodes"; - // TODO: Deprecate and remove support for roles.json in an upcoming release. - /** - * The following, node_roles and roles.json are for assigning roles to nodes. The node_roles is - * the preferred way (using -Dsolr.node.roles param), and roles.json is used by legacy ADDROLE API - * command. - */ + /** Node roles are assigned at startup with the {@code -Dsolr.node.roles} property. */ public static final String NODE_ROLES = "/node_roles"; - public static final String ROLES = "/roles.json"; - public static final String ALIASES = "/aliases.json"; /** diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/CollectionAdminRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/CollectionAdminRequest.java index 0ac88ee39228..b94f1e479429 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/CollectionAdminRequest.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/CollectionAdminRequest.java @@ -325,34 +325,6 @@ protected CollectionAdminResponse createResponse(NamedList namedList) { // // --------------------------------------------------------------------------------------- - protected abstract static class CollectionAdminRoleRequest extends AsyncCollectionAdminRequest { - - protected String node; - protected String role; - - public CollectionAdminRoleRequest(CollectionAction action, String node, String role) { - super(action); - this.role = checkNotNull(CollectionAdminParams.ROLE, role); - this.node = checkNotNull(CoreAdminParams.NODE, node); - } - - public String getNode() { - return this.node; - } - - public String getRole() { - return this.role; - } - - @Override - public SolrParams getParams() { - ModifiableSolrParams params = new ModifiableSolrParams(super.getParams()); - params.set(CollectionAdminParams.ROLE, this.role); - params.set(CoreAdminParams.NODE, this.node); - return params; - } - } - /** Specific Collection API call implementations * */ /** @@ -2861,40 +2833,6 @@ public SolrParams getParams() { } } - /** - * Returns a SolrRequest to add a role to a node - * - * @deprecated Use Node Roles ({@code -Dsolr.node.roles}) at startup instead. - */ - @Deprecated(since = "10.1") - public static AddRole addRole(String node, String role) { - return new AddRole(node, role); - } - - // ADDROLE request - public static class AddRole extends CollectionAdminRoleRequest { - private AddRole(String node, String role) { - super(CollectionAction.ADDROLE, node, role); - } - } - - /** - * Returns a SolrRequest to remove a role from a node - * - * @deprecated Use Node Roles ({@code -Dsolr.node.roles}) at startup instead. - */ - @Deprecated(since = "10.1") - public static RemoveRole removeRole(String node, String role) { - return new RemoveRole(node, role); - } - - // REMOVEROLE request - public static class RemoveRole extends CollectionAdminRoleRequest { - private RemoveRole(String node, String role) { - super(CollectionAction.REMOVEROLE, node, role); - } - } - /** Return a SolrRequest to get the Overseer status */ public static OverseerStatus getOverseerStatus() { return new OverseerStatus(); diff --git a/solr/solrj/src/java/org/apache/solr/common/params/CollectionAdminParams.java b/solr/solrj/src/java/org/apache/solr/common/params/CollectionAdminParams.java index 050e32bb977a..26e8d1ff3a3f 100644 --- a/solr/solrj/src/java/org/apache/solr/common/params/CollectionAdminParams.java +++ b/solr/solrj/src/java/org/apache/solr/common/params/CollectionAdminParams.java @@ -29,8 +29,6 @@ public interface CollectionAdminParams { String COUNT_PROP = "count"; - String ROLE = "role"; - /** * A parameter to specify list of Solr nodes to be used (e.g. for collection creation or restore * operation). diff --git a/solr/solrj/src/java/org/apache/solr/common/params/CollectionParams.java b/solr/solrj/src/java/org/apache/solr/common/params/CollectionParams.java index 3e1d9745c93e..584441816b22 100644 --- a/solr/solrj/src/java/org/apache/solr/common/params/CollectionParams.java +++ b/solr/solrj/src/java/org/apache/solr/common/params/CollectionParams.java @@ -102,10 +102,6 @@ enum CollectionAction { DELETEREPLICA(true, LockLevel.SHARD), FORCELEADER(true, LockLevel.SHARD), MIGRATE(true, LockLevel.COLLECTION), - @Deprecated(since = "10.1") - ADDROLE(true, LockLevel.NONE), - @Deprecated(since = "10.1") - REMOVEROLE(true, LockLevel.NONE), CLUSTERPROP(true, LockLevel.NONE), COLLECTIONPROP(true, LockLevel.NONE), // atomic; no lock REQUESTSTATUS(false, LockLevel.NONE), @@ -130,9 +126,10 @@ enum CollectionAction { CREATESNAPSHOT(true, LockLevel.COLLECTION), DELETESNAPSHOT(true, LockLevel.COLLECTION), LISTSNAPSHOTS(false, LockLevel.NONE), - // only for testing. it just waits for specified time // these are not exposed via collection API commands // but the overseer is aware of these tasks + REPRIORITIZE_OVERSEER(true, LockLevel.NONE), + // only for testing. it just waits for specified time MOCK_COLL_TASK(false, LockLevel.COLLECTION), MOCK_SHARD_TASK(false, LockLevel.SHARD), // TODO when we have a node level lock use it here diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/CollectionAdminRequestRequiredParamsTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/CollectionAdminRequestRequiredParamsTest.java index 942b0dbea48a..5296659793a2 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/CollectionAdminRequestRequiredParamsTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/CollectionAdminRequestRequiredParamsTest.java @@ -75,16 +75,6 @@ public void testCollectionProp() { CollectionAdminParams.PROPERTY_VALUE); } - public void testAddRole() { - CollectionAdminRequest.AddRole request = CollectionAdminRequest.addRole("node", "role"); - assertContainsParams(request.getParams(), ACTION, "node", "role"); - } - - public void testRemoveRole() { - CollectionAdminRequest.RemoveRole request = CollectionAdminRequest.removeRole("node", "role"); - assertContainsParams(request.getParams(), ACTION, "node", "role"); - } - public void testAddReplica() { // with shard parameter and "client side" implicit type param CollectionAdminRequest.AddReplica request =