All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.cluster.routing.allocation.AllocateUnassignedDecision Maven / Gradle / Ivy

There is a newer version: 8.14.1
Show newest version
/*
 * 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.
 */

package org.elasticsearch.cluster.routing.allocation;

import org.elasticsearch.cluster.routing.UnassignedInfo.AllocationStatus;
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
import org.elasticsearch.cluster.routing.allocation.decider.Decision.Type;
import org.elasticsearch.common.Nullable;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * Represents the allocation decision by an allocator for an unassigned shard.
 */
public class AllocateUnassignedDecision {
    /** a constant representing a shard decision where no decision was taken */
    public static final AllocateUnassignedDecision NOT_TAKEN =
        new AllocateUnassignedDecision(null, null, null, null, null, null, null);
    /**
     * a map of cached common no/throttle decisions that don't need explanations,
     * this helps prevent unnecessary object allocations for the non-explain API case
     */
    private static final Map CACHED_DECISIONS;
    static {
        Map cachedDecisions = new HashMap<>();
        cachedDecisions.put(AllocationStatus.FETCHING_SHARD_DATA,
            new AllocateUnassignedDecision(Type.NO, AllocationStatus.FETCHING_SHARD_DATA, null, null, null, null, null));
        cachedDecisions.put(AllocationStatus.NO_VALID_SHARD_COPY,
            new AllocateUnassignedDecision(Type.NO, AllocationStatus.NO_VALID_SHARD_COPY, null, null, null, null, null));
        cachedDecisions.put(AllocationStatus.DECIDERS_NO,
            new AllocateUnassignedDecision(Type.NO, AllocationStatus.DECIDERS_NO, null, null, null, null, null));
        cachedDecisions.put(AllocationStatus.DECIDERS_THROTTLED,
            new AllocateUnassignedDecision(Type.THROTTLE, AllocationStatus.DECIDERS_THROTTLED, null, null, null, null, null));
        cachedDecisions.put(AllocationStatus.DELAYED_ALLOCATION,
            new AllocateUnassignedDecision(Type.NO, AllocationStatus.DELAYED_ALLOCATION, null, null, null, null, null));
        CACHED_DECISIONS = Collections.unmodifiableMap(cachedDecisions);
    }

    @Nullable
    private final Type finalDecision;
    @Nullable
    private final AllocationStatus allocationStatus;
    @Nullable
    private final String finalExplanation;
    @Nullable
    private final String assignedNodeId;
    @Nullable
    private final String allocationId;
    @Nullable
    private final Map nodeDecisions;
    @Nullable
    private final Decision shardDecision;

    private AllocateUnassignedDecision(Type finalDecision,
                                       AllocationStatus allocationStatus,
                                       String finalExplanation,
                                       String assignedNodeId,
                                       String allocationId,
                                       Map nodeDecisions,
                                       Decision shardDecision) {
        assert assignedNodeId != null || finalDecision == null || finalDecision != Type.YES :
            "a yes decision must have a node to assign the shard to";
        assert allocationStatus != null || finalDecision == null || finalDecision == Type.YES :
            "only a yes decision should not have an allocation status";
        assert allocationId == null || assignedNodeId != null :
            "allocation id can only be null if the assigned node is null";
        this.finalDecision = finalDecision;
        this.allocationStatus = allocationStatus;
        this.finalExplanation = finalExplanation;
        this.assignedNodeId = assignedNodeId;
        this.allocationId = allocationId;
        this.nodeDecisions = nodeDecisions != null ? Collections.unmodifiableMap(nodeDecisions) : null;
        this.shardDecision = shardDecision;
    }

    /**
     * Returns a NO decision with the given shard-level decision and explanation (if in explain mode).
     */
    public static AllocateUnassignedDecision no(Decision shardDecision, @Nullable String explanation) {
        if (explanation != null) {
            return new AllocateUnassignedDecision(Type.NO, AllocationStatus.DECIDERS_NO, explanation, null, null, null, shardDecision);
        } else {
            return getCachedDecision(AllocationStatus.DECIDERS_NO);
        }
    }

    /**
     * Returns a NO decision with the given {@link AllocationStatus} and explanation for the NO decision, if in explain mode.
     */
    public static AllocateUnassignedDecision no(AllocationStatus allocationStatus, @Nullable String explanation) {
        return no(allocationStatus, explanation, null);
    }

    /**
     * Returns a NO decision with the given {@link AllocationStatus}, and the explanation for the NO decision
     * as well as the individual node-level decisions that comprised the final NO decision if in explain mode.
     */
    public static AllocateUnassignedDecision no(AllocationStatus allocationStatus, @Nullable String explanation,
                                                @Nullable Map nodeDecisions) {
        Objects.requireNonNull(allocationStatus, "allocationStatus must not be null");
        if (explanation != null) {
            return new AllocateUnassignedDecision(Type.NO, allocationStatus, explanation, null, null, asExplanations(nodeDecisions), null);
        } else {
            return getCachedDecision(allocationStatus);
        }
    }

    /**
     * Returns a THROTTLE decision, with the given explanation and individual node-level decisions that
     * comprised the final THROTTLE decision if in explain mode.
     */
    public static AllocateUnassignedDecision throttle(@Nullable String explanation, @Nullable Map nodeDecisions) {
        if (explanation != null) {
            return new AllocateUnassignedDecision(Type.THROTTLE, AllocationStatus.DECIDERS_THROTTLED, explanation, null, null,
                                               asExplanations(nodeDecisions), null);
        } else {
            return getCachedDecision(AllocationStatus.DECIDERS_THROTTLED);
        }
    }

    /**
     * Creates a YES decision with the given explanation and individual node-level decisions that
     * comprised the final YES decision, along with the node id to which the shard is assigned and
     * the allocation id for the shard, if available.
     */
    public static AllocateUnassignedDecision yes(String assignedNodeId, @Nullable String explanation, @Nullable String allocationId,
                                                 @Nullable Map nodeDecisions) {
        Objects.requireNonNull(assignedNodeId, "assignedNodeId must not be null");
        return new AllocateUnassignedDecision(Type.YES, null, explanation, assignedNodeId, allocationId,
                                                 asExplanations(nodeDecisions), null);
    }

    /**
     * Creates a {@link AllocateUnassignedDecision} from the given {@link Decision} and the assigned node, if any.
     */
    public static AllocateUnassignedDecision fromDecision(Decision decision, @Nullable String assignedNodeId, boolean explain,
                                                          @Nullable Map nodeDecisions) {
        final Type decisionType = decision.type();
        AllocationStatus allocationStatus = decisionType != Type.YES ? AllocationStatus.fromDecision(decisionType) : null;
        String explanation = null;
        if (explain) {
            if (decision.type() == Type.YES) {
                assert assignedNodeId != null;
                explanation = "shard assigned to node [" + assignedNodeId + "]";
            } else if (decision.type() == Type.THROTTLE) {
                assert assignedNodeId != null;
                explanation = "shard assignment throttled on node [" + assignedNodeId + "]";
            } else {
                explanation = "shard cannot be assigned to any node in the cluster";
            }
        }
        return new AllocateUnassignedDecision(decisionType, allocationStatus, explanation, assignedNodeId, null, nodeDecisions, null);
    }

    private static AllocateUnassignedDecision getCachedDecision(AllocationStatus allocationStatus) {
        AllocateUnassignedDecision decision = CACHED_DECISIONS.get(allocationStatus);
        return Objects.requireNonNull(decision, "precomputed decision not found for " + allocationStatus);
    }

    private static Map asExplanations(Map decisionMap) {
        if (decisionMap != null) {
            Map explanationMap = new HashMap<>();
            for (Map.Entry entry : decisionMap.entrySet()) {
                explanationMap.put(entry.getKey(), new NodeAllocationResult(entry.getValue(), Float.POSITIVE_INFINITY));
            }
            return explanationMap;
        }
        return null;
    }

    /**
     * Returns true if a decision was taken by the allocator, {@code false} otherwise.
     * If no decision was taken, then the rest of the fields in this object are meaningless and return {@code null}.
     */
    public boolean isDecisionTaken() {
        return finalDecision != null;
    }

    /**
     * Returns the final decision made by the allocator on whether to assign the shard.
     * This value can only be {@code null} if {@link #isDecisionTaken()} returns {@code false}.
     */
    @Nullable
    public Type getFinalDecisionType() {
        return finalDecision;
    }

    /**
     * Returns the final decision made by the allocator on whether to assign the shard.
     * Only call this method if {@link #isDecisionTaken()} returns {@code true}, otherwise it will
     * throw an {@code IllegalArgumentException}.
     */
    public Type getFinalDecisionSafe() {
        if (isDecisionTaken() == false) {
            throw new IllegalArgumentException("decision must have been taken in order to return the final decision");
        }
        return finalDecision;
    }

    /**
     * Returns the status of an unsuccessful allocation attempt.  This value will be {@code null} if
     * no decision was taken or if the decision was {@link Decision.Type#YES}.
     */
    @Nullable
    public AllocationStatus getAllocationStatus() {
        return allocationStatus;
    }

    /**
     * Returns the free-text explanation for the reason behind the decision taken in {@link #getFinalDecisionType()}.
     */
    @Nullable
    public String getFinalExplanation() {
        return finalExplanation;
    }

    /**
     * Get the node id that the allocator will assign the shard to, unless {@link #getFinalDecisionType()} returns
     * a value other than {@link Decision.Type#YES}, in which case this returns {@code null}.
     */
    @Nullable
    public String getAssignedNodeId() {
        return assignedNodeId;
    }

    /**
     * Gets the allocation id for the existing shard copy that the allocator is assigning the shard to.
     * This method returns a non-null value iff {@link #getAssignedNodeId()} returns a non-null value
     * and the node on which the shard is assigned already has a shard copy with an in-sync allocation id
     * that we can re-use.
     */
    @Nullable
    public String getAllocationId() {
        return allocationId;
    }

    /**
     * Gets the individual node-level decisions that went into making the final decision as represented by
     * {@link #getFinalDecisionType()}.  The map that is returned has the node id as the key and a {@link Decision}
     * as the decision for the given node.
     */
    @Nullable
    public Map getNodeDecisions() {
        return nodeDecisions;
    }

    /**
     * Gets the decision on allocating a shard, without examining any specific nodes to allocate to
     * (e.g. a replica can never be allocated if the primary is not allocated, so this is a shard-level
     * decision, not having taken any node into account).
     */
    @Nullable
    public Decision getShardDecision() {
        return shardDecision;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy