Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog/unreleased/SOLR-18370-remove-overseer-roles-api.yml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions dev-docs/overseer/overseer.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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()`.
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,27 @@
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;
import org.apache.solr.common.cloud.ZkStateReader;
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 {

Expand All @@ -66,30 +63,11 @@ public OverseerNodePrioritizer(

public synchronized void prioritizeOverseerNodes(String overseerId) throws Exception {
SolrZkClient zk = zkStateReader.getZkClient();
List<String> overseerDesignates = new ArrayList<>();
if (zk.exists(ZkStateReader.ROLES)) {
Map<?, ?> m = (Map<?, ?>) Utils.fromJSON(zk.getData(ZkStateReader.ROLES, null, new Stat()));
@SuppressWarnings("unchecked")
List<String> l = (List<String>) m.get("overseer");
if (l != null) {
overseerDesignates.addAll(l);
}
}

List<String> preferredOverseers =
List<String> 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;
Expand Down
35 changes: 9 additions & 26 deletions solr/core/src/java/org/apache/solr/cloud/ZkController.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()),
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give me some context on why we had to add this whole class? This looks like a big add, is it because something was missed when we moved to the node roles earlier?


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<Object> 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();
}
}

This file was deleted.

1 change: 0 additions & 1 deletion solr/core/src/java/org/apache/solr/core/CoreContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading