org.opensearch.cluster.routing.allocation.command.AbstractAllocateAllocationCommand Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opensearch Show documentation
Show all versions of opensearch Show documentation
OpenSearch subproject :server
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/
package org.opensearch.cluster.routing.allocation.command;
import org.opensearch.cluster.node.DiscoveryNode;
import org.opensearch.cluster.routing.RecoverySource;
import org.opensearch.cluster.routing.RoutingNode;
import org.opensearch.cluster.routing.RoutingNodes;
import org.opensearch.cluster.routing.ShardRouting;
import org.opensearch.cluster.routing.UnassignedInfo;
import org.opensearch.cluster.routing.allocation.RerouteExplanation;
import org.opensearch.cluster.routing.allocation.RoutingAllocation;
import org.opensearch.cluster.routing.allocation.decider.Decision;
import org.opensearch.common.Nullable;
import org.opensearch.core.ParseField;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.xcontent.ObjectParser;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;
import java.io.IOException;
import java.util.Objects;
/**
* Abstract base class for allocating an unassigned shard to a node
*
* @opensearch.internal
*/
public abstract class AbstractAllocateAllocationCommand implements AllocationCommand {
private static final String INDEX_FIELD = "index";
private static final String SHARD_FIELD = "shard";
private static final String NODE_FIELD = "node";
protected static > ObjectParser createAllocateParser(String command) {
ObjectParser parser = new ObjectParser<>(command);
parser.declareString(Builder::setIndex, new ParseField(INDEX_FIELD));
parser.declareInt(Builder::setShard, new ParseField(SHARD_FIELD));
parser.declareString(Builder::setNode, new ParseField(NODE_FIELD));
return parser;
}
/**
* Works around ObjectParser not supporting constructor arguments.
*/
protected abstract static class Builder {
protected String index;
protected int shard = -1;
protected String node;
public void setIndex(String index) {
this.index = index;
}
public void setShard(int shard) {
this.shard = shard;
}
public void setNode(String node) {
this.node = node;
}
public abstract Builder parse(XContentParser parser) throws IOException;
public abstract T build();
protected void validate() {
if (index == null) {
throw new IllegalArgumentException("Argument [index] must be defined");
}
if (shard < 0) {
throw new IllegalArgumentException("Argument [shard] must be defined and non-negative");
}
if (node == null) {
throw new IllegalArgumentException("Argument [node] must be defined");
}
}
}
protected final String index;
protected final int shardId;
protected final String node;
protected AbstractAllocateAllocationCommand(String index, int shardId, String node) {
this.index = index;
this.shardId = shardId;
this.node = node;
}
/**
* Read from a stream.
*/
protected AbstractAllocateAllocationCommand(StreamInput in) throws IOException {
index = in.readString();
shardId = in.readVInt();
node = in.readString();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(index);
out.writeVInt(shardId);
out.writeString(node);
}
/**
* Get the index name
*
* @return name of the index
*/
public String index() {
return this.index;
}
/**
* Get the shard id
*
* @return id of the shard
*/
public int shardId() {
return this.shardId;
}
/**
* Get the id of the node
*
* @return id of the node
*/
public String node() {
return this.node;
}
/**
* Handle case where a disco node cannot be found in the routing table. Usually means that it's not a data node.
*/
protected RerouteExplanation explainOrThrowMissingRoutingNode(RoutingAllocation allocation, boolean explain, DiscoveryNode discoNode) {
if (!discoNode.isDataNode()) {
return explainOrThrowRejectedCommand(explain, allocation, "allocation can only be done on data nodes, not [" + node + "]");
} else {
return explainOrThrowRejectedCommand(explain, allocation, "could not find [" + node + "] among the routing nodes");
}
}
/**
* Utility method for rejecting the current allocation command based on provided reason
*/
protected RerouteExplanation explainOrThrowRejectedCommand(boolean explain, RoutingAllocation allocation, String reason) {
if (explain) {
return new RerouteExplanation(this, allocation.decision(Decision.NO, name() + " (allocation command)", reason));
}
throw new IllegalArgumentException("[" + name() + "] " + reason);
}
/**
* Utility method for rejecting the current allocation command based on provided exception
*/
protected RerouteExplanation explainOrThrowRejectedCommand(boolean explain, RoutingAllocation allocation, RuntimeException rte) {
if (explain) {
return new RerouteExplanation(this, allocation.decision(Decision.NO, name() + " (allocation command)", rte.getMessage()));
}
throw rte;
}
/**
* Initializes an unassigned shard on a node and removes it from the unassigned
*
* @param allocation the allocation
* @param routingNodes the routing nodes
* @param routingNode the node to initialize it to
* @param shardRouting the shard routing that is to be matched in unassigned shards
*/
protected void initializeUnassignedShard(
RoutingAllocation allocation,
RoutingNodes routingNodes,
RoutingNode routingNode,
ShardRouting shardRouting
) {
initializeUnassignedShard(allocation, routingNodes, routingNode, shardRouting, null, null);
}
/**
* Initializes an unassigned shard on a node and removes it from the unassigned
*
* @param allocation the allocation
* @param routingNodes the routing nodes
* @param routingNode the node to initialize it to
* @param shardRouting the shard routing that is to be matched in unassigned shards
* @param unassignedInfo unassigned info to override
* @param recoverySource recovery source to override
*/
protected void initializeUnassignedShard(
RoutingAllocation allocation,
RoutingNodes routingNodes,
RoutingNode routingNode,
ShardRouting shardRouting,
@Nullable UnassignedInfo unassignedInfo,
@Nullable RecoverySource recoverySource
) {
for (RoutingNodes.UnassignedShards.UnassignedIterator it = routingNodes.unassigned().iterator(); it.hasNext();) {
ShardRouting unassigned = it.next();
if (!unassigned.equalsIgnoringMetadata(shardRouting)) {
continue;
}
if (unassignedInfo != null || recoverySource != null) {
unassigned = it.updateUnassigned(
unassignedInfo != null ? unassignedInfo : unassigned.unassignedInfo(),
recoverySource != null ? recoverySource : unassigned.recoverySource(),
allocation.changes()
);
}
it.initialize(
routingNode.nodeId(),
null,
allocation.clusterInfo().getShardSize(unassigned, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE),
allocation.changes()
);
return;
}
assert false : "shard to initialize not found in list of unassigned shards";
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject();
builder.field(INDEX_FIELD, index());
builder.field(SHARD_FIELD, shardId());
builder.field(NODE_FIELD, node());
extraXContent(builder);
return builder.endObject();
}
protected void extraXContent(XContentBuilder builder) throws IOException {}
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
AbstractAllocateAllocationCommand other = (AbstractAllocateAllocationCommand) obj;
// Override equals and hashCode for testing
return Objects.equals(index, other.index) && Objects.equals(shardId, other.shardId) && Objects.equals(node, other.node);
}
@Override
public int hashCode() {
// Override equals and hashCode for testing
return Objects.hash(index, shardId, node);
}
}