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

org.elasticsearch.cluster.node.DiscoveryNodes Maven / Gradle / Ivy

There is a newer version: 8.15.1
Show newest version
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

package org.elasticsearch.cluster.node;

import org.elasticsearch.TransportVersions;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.Diff;
import org.elasticsearch.cluster.SimpleDiffable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.core.Booleans;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.IndexVersions;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.elasticsearch.cluster.routing.allocation.DataTier.ALL_DATA_TIERS;

/**
 * This class holds all {@link DiscoveryNode} in the cluster and provides convenience methods to
 * access, modify merge / diff discovery nodes.
 */
public class DiscoveryNodes implements Iterable, SimpleDiffable {

    public static final DiscoveryNodes EMPTY_NODES = builder().build();

    private final long nodeLeftGeneration;
    private final Map nodes;
    private final Map dataNodes;
    private final Map masterNodes;
    private final Map ingestNodes;

    @Nullable
    private final String masterNodeId;
    @Nullable
    private final DiscoveryNode masterNode;
    @Nullable
    private final String localNodeId;
    @Nullable
    private final DiscoveryNode localNode;
    private final Version minNonClientNodeVersion;
    private final Version maxNodeVersion;
    private final Version minNodeVersion;
    private final IndexVersion maxDataNodeCompatibleIndexVersion;
    private final IndexVersion minSupportedIndexVersion;

    private final Map> tiersToNodeIds;

    private DiscoveryNodes(
        long nodeLeftGeneration,
        Map nodes,
        Map dataNodes,
        Map masterNodes,
        Map ingestNodes,
        @Nullable String masterNodeId,
        @Nullable String localNodeId,
        Version minNonClientNodeVersion,
        Version maxNodeVersion,
        Version minNodeVersion,
        IndexVersion maxDataNodeCompatibleIndexVersion,
        IndexVersion minSupportedIndexVersion,
        Map> tiersToNodeIds
    ) {
        this.nodeLeftGeneration = nodeLeftGeneration;
        this.nodes = nodes;
        this.dataNodes = dataNodes;
        this.masterNodes = masterNodes;
        this.ingestNodes = ingestNodes;
        this.masterNodeId = masterNodeId;
        this.masterNode = masterNodeId == null ? null : nodes.get(masterNodeId);
        assert (masterNodeId == null) == (masterNode == null);
        this.localNodeId = localNodeId;
        this.localNode = localNodeId == null ? null : nodes.get(localNodeId);
        this.minNonClientNodeVersion = minNonClientNodeVersion;
        this.minNodeVersion = minNodeVersion;
        this.maxNodeVersion = maxNodeVersion;
        this.maxDataNodeCompatibleIndexVersion = maxDataNodeCompatibleIndexVersion;
        this.minSupportedIndexVersion = minSupportedIndexVersion;
        assert (localNodeId == null) == (localNode == null);
        this.tiersToNodeIds = tiersToNodeIds;
    }

    public DiscoveryNodes withMasterNodeId(@Nullable String masterNodeId) {
        assert masterNodeId == null || nodes.containsKey(masterNodeId) : "unknown node [" + masterNodeId + "]";
        return new DiscoveryNodes(
            nodeLeftGeneration,
            nodes,
            dataNodes,
            masterNodes,
            ingestNodes,
            masterNodeId,
            localNodeId,
            minNonClientNodeVersion,
            maxNodeVersion,
            minNodeVersion,
            maxDataNodeCompatibleIndexVersion,
            minSupportedIndexVersion,
            tiersToNodeIds
        );
    }

    @Override
    public Iterator iterator() {
        return nodes.values().iterator();
    }

    public Stream stream() {
        return nodes.values().stream();
    }

    public Collection getAllNodes() {
        return nodes.values();
    }

    public int size() {
        return nodes.size();
    }

    /**
     * Returns {@code true} if the local node is the elected master node.
     */
    public boolean isLocalNodeElectedMaster() {
        if (localNodeId == null) {
            // we don't know yet the local node id, return false
            return false;
        }
        return localNodeId.equals(masterNodeId);
    }

    /**
     * Gets a {@link Map} of node roles to node IDs which have those roles.
     *
     * @return {@link Map} of node roles to node IDs which have those roles.
     */
    public Map> getTiersToNodeIds() {
        return tiersToNodeIds;
    }

    /**
     * Get the number of known nodes
     *
     * @return number of nodes
     */
    public int getSize() {
        return nodes.size();
    }

    /**
     * Get a {@link Map} of the discovered nodes arranged by their ids
     *
     * @return {@link Map} of the discovered nodes arranged by their ids
     */
    public Map getNodes() {
        return this.nodes;
    }

    /**
     * Get a {@link Map} of the discovered data nodes arranged by their ids
     *
     * @return {@link Map} of the discovered data nodes arranged by their ids
     */
    public Map getDataNodes() {
        return this.dataNodes;
    }

    /**
     * Get a {@link Map} of the discovered master nodes arranged by their ids
     *
     * @return {@link Map} of the discovered master nodes arranged by their ids
     */
    public Map getMasterNodes() {
        return this.masterNodes;
    }

    /**
     * @return All the ingest nodes arranged by their ids
     */
    public Map getIngestNodes() {
        return ingestNodes;
    }

    /**
     * Get a {@link Map} of the discovered master and data nodes arranged by their ids
     *
     * @return {@link Map} of the discovered master and data nodes arranged by their ids
     */
    public Map getMasterAndDataNodes() {
        return filteredNodes(nodes, n -> n.canContainData() || n.isMasterNode());
    }

    /**
     * Get a {@link Map} of the coordinating only nodes (nodes which are neither master, nor data, nor ingest nodes) arranged by their ids
     *
     * @return {@link Map} of the coordinating only nodes arranged by their ids
     */
    public Map getCoordinatingOnlyNodes() {
        return filteredNodes(nodes, n -> n.canContainData() == false && n.isMasterNode() == false && n.isIngestNode() == false);
    }

    private static final Comparator MASTERS_FIRST_COMPARATOR
    // Ugly hack: when https://github.com/elastic/elasticsearch/issues/94946 is fixed, remove the sorting by ephemeral ID here
        = Comparator.comparingInt(n -> n.isMasterNode() ? 0 : 1).thenComparing(DiscoveryNode::getEphemeralId);

    /**
     * Returns a stream of all nodes, with master nodes at the front
     */
    public Stream mastersFirstStream() {
        return nodes.values().stream().sorted(MASTERS_FIRST_COMPARATOR);
    }

    /**
     * Get a node by its id
     *
     * @param nodeId id of the wanted node
     * @return wanted node if it exists. Otherwise null
     */
    public DiscoveryNode get(String nodeId) {
        return nodes.get(nodeId);
    }

    /**
     * Determine if a given node id exists
     *
     * @param nodeId id of the node which existence should be verified
     * @return true if the node exists. Otherwise false
     */
    public boolean nodeExists(String nodeId) {
        return nodes.containsKey(nodeId);
    }

    /**
     * Determine if a given node exists
     *
     * @param node of the node which existence should be verified
     * @return true if the node exists. Otherwise false
     */
    public boolean nodeExists(DiscoveryNode node) {
        DiscoveryNode existing = nodes.get(node.getId());
        return existing != null && existing.equals(node);
    }

    /**
     * Determine if the given node exists and has the right roles. Supported roles vary by version, and our local cluster state might
     * have come via an older master, so the roles may differ even if the node is otherwise identical.
     */
    public boolean nodeExistsWithSameRoles(DiscoveryNode discoveryNode) {
        final DiscoveryNode existing = nodes.get(discoveryNode.getId());
        return existing != null && existing.equals(discoveryNode) && existing.getRoles().equals(discoveryNode.getRoles());
    }

    /**
     * Get the id of the master node
     *
     * @return id of the master
     */
    public String getMasterNodeId() {
        return this.masterNodeId;
    }

    /**
     * Get the id of the local node
     *
     * @return id of the local node
     */
    public String getLocalNodeId() {
        return this.localNodeId;
    }

    /**
     * Get the local node
     *
     * @return local node
     */
    public DiscoveryNode getLocalNode() {
        return localNode;
    }

    /**
     * Returns the master node, or {@code null} if there is no master node
     */
    @Nullable
    public DiscoveryNode getMasterNode() {
        return masterNode;
    }

    /**
     * Get a node by its address
     *
     * @param address {@link TransportAddress} of the wanted node
     * @return node identified by the given address or null if no such node exists
     */
    public DiscoveryNode findByAddress(TransportAddress address) {
        for (DiscoveryNode node : nodes.values()) {
            if (node.getAddress().equals(address)) {
                return node;
            }
        }
        return null;
    }

    /**
     * Check if a node with provided name exists
     *
     * @return {@code true} node identified with provided name exists or {@code false} otherwise
     */
    public boolean hasByName(String name) {
        for (DiscoveryNode node : nodes.values()) {
            if (node.getName().equals(name)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the version of the node with the oldest version in the cluster that is not a client node
     *
     * If there are no non-client nodes, Version.CURRENT will be returned.
     *
     * @return the oldest version in the cluster
     */
    public Version getSmallestNonClientNodeVersion() {
        return minNonClientNodeVersion;
    }

    /**
     * Returns the highest index version supported by all data nodes in the cluster
     */
    public IndexVersion getMaxDataNodeCompatibleIndexVersion() {
        return maxDataNodeCompatibleIndexVersion;
    }

    /**
     * Returns the version of the node with the oldest version in the cluster.
     *
     * @return the oldest version in the cluster
     */
    public Version getMinNodeVersion() {
        return minNodeVersion;
    }

    /**
     * Returns the version of the node with the youngest version in the cluster
     *
     * @return the youngest version in the cluster
     */
    public Version getMaxNodeVersion() {
        return maxNodeVersion;
    }

    /**
     * Returns the minimum index version supported by all nodes in the cluster
     */
    public IndexVersion getMinSupportedIndexVersion() {
        return minSupportedIndexVersion;
    }

    /**
     * Return the node-left generation, which is the number of times the cluster membership has been updated by removing one or more nodes.
     * 

* Since node-left events are rare, nodes can use the fact that this value has not changed to very efficiently verify that they have not * been removed from the cluster. If the node-left generation changes then that indicates some node has left the cluster, which * triggers some more expensive checks to determine the new cluster membership. *

* Not tracked if the cluster has any nodes older than v8.9.0, in which case this method returns zero. */ public long getNodeLeftGeneration() { return nodeLeftGeneration; } /** * Resolve a node with a given id * * @param node id of the node to discover * @return discovered node matching the given id * @throws IllegalArgumentException if more than one node matches the request or no nodes have been resolved */ public DiscoveryNode resolveNode(String node) { String[] resolvedNodeIds = resolveNodes(node); if (resolvedNodeIds.length > 1) { throw new IllegalArgumentException( "resolved [" + node + "] into [" + resolvedNodeIds.length + "] nodes, where expected to be resolved to a single node" ); } if (resolvedNodeIds.length == 0) { throw new IllegalArgumentException("failed to resolve [" + node + "], no matching nodes"); } return nodes.get(resolvedNodeIds[0]); } /** * Resolves a set of nodes according to the given sequence of node specifications. Implements the logic in various APIs that allow the * user to run the action on a subset of the nodes in the cluster. See [Node specification] in the reference manual for full details. * * Works by tracking the current set of nodes and applying each node specification in sequence. The set starts out empty and each node * specification may either add or remove nodes. For instance: * * - _local, _master and _all respectively add to the subset the local node, the currently-elected master, and all the nodes * - node IDs, names, hostnames and IP addresses all add to the subset any nodes which match * - a wildcard-based pattern of the form "attr*:value*" adds to the subset all nodes with a matching attribute with a matching value * - role:true adds to the subset all nodes with a matching role * - role:false removes from the subset all nodes with a matching role. * * An empty sequence of node specifications returns all nodes, since the corresponding actions run on all nodes by default. */ public String[] resolveNodes(String... nodes) { if (nodes == null || nodes.length == 0) { return stream().map(DiscoveryNode::getId).toArray(String[]::new); } else { Set resolvedNodesIds = Sets.newHashSetWithExpectedSize(nodes.length); for (String nodeId : nodes) { if (nodeId == null) { // don't silence the underlying issue, it is a bug, so lets fail if assertions are enabled assert nodeId != null : "nodeId should not be null"; continue; } else if (nodeId.equals("_local")) { String localNodeId = getLocalNodeId(); if (localNodeId != null) { resolvedNodesIds.add(localNodeId); } } else if (nodeId.equals("_master")) { String masterNodeId = getMasterNodeId(); if (masterNodeId != null) { resolvedNodesIds.add(masterNodeId); } } else if (nodeExists(nodeId)) { resolvedNodesIds.add(nodeId); } else { for (DiscoveryNode node : this) { if ("_all".equals(nodeId) || Regex.simpleMatch(nodeId, node.getName()) || Regex.simpleMatch(nodeId, node.getHostAddress()) || Regex.simpleMatch(nodeId, node.getHostName())) { resolvedNodesIds.add(node.getId()); } } int index = nodeId.indexOf(':'); if (index != -1) { String matchAttrName = nodeId.substring(0, index); String matchAttrValue = nodeId.substring(index + 1); if (DiscoveryNodeRole.roles().stream().map(DiscoveryNodeRole::roleName).anyMatch(s -> s.equals(matchAttrName))) { final DiscoveryNodeRole role = DiscoveryNodeRole.getRoleFromRoleName(matchAttrName); final Predicate> predicate; if (role.equals(DiscoveryNodeRole.DATA_ROLE)) { // if the node has *any* role that can contain data, then it matches the data attribute predicate = s -> s.stream().anyMatch(DiscoveryNodeRole::canContainData); } else if (role.canContainData()) { // if the node has the matching data_ role, or the generic data role, then it matches the data_ attribute predicate = s -> s.stream().anyMatch(r -> r.equals(role) || r.equals(DiscoveryNodeRole.DATA_ROLE)); } else { // the role is not a data role, we require an exact match (e.g., ingest) predicate = s -> s.contains(role); } final Consumer mutation; if (Booleans.parseBoolean(matchAttrValue, true)) { mutation = resolvedNodesIds::add; } else { mutation = resolvedNodesIds::remove; } for (final DiscoveryNode node : this) { if (predicate.test(node.getRoles())) { mutation.accept(node.getId()); } } } else if (DiscoveryNode.COORDINATING_ONLY.equals(matchAttrName)) { if (Booleans.parseBoolean(matchAttrValue, true)) { resolvedNodesIds.addAll(getCoordinatingOnlyNodes().keySet()); } else { resolvedNodesIds.removeAll(getCoordinatingOnlyNodes().keySet()); } } else { for (DiscoveryNode node : this) { for (DiscoveryNodeRole role : Sets.difference(node.getRoles(), DiscoveryNodeRole.roles())) { if (role.roleName().equals(matchAttrName)) { if (Booleans.parseBoolean(matchAttrValue, true)) { resolvedNodesIds.add(node.getId()); } else { resolvedNodesIds.remove(node.getId()); } } } } for (DiscoveryNode node : this) { for (Map.Entry entry : node.getAttributes().entrySet()) { String attrName = entry.getKey(); String attrValue = entry.getValue(); if (Regex.simpleMatch(matchAttrName, attrName) && Regex.simpleMatch(matchAttrValue, attrValue)) { resolvedNodesIds.add(node.getId()); } } } } } } } return resolvedNodesIds.toArray(Strings.EMPTY_ARRAY); } } /** * Returns the changes comparing this nodes to the provided nodes. */ public Delta delta(DiscoveryNodes other) { final List removed = new ArrayList<>(); final List added = new ArrayList<>(); for (DiscoveryNode node : other) { if (this.nodeExists(node) == false) { removed.add(node); } } for (DiscoveryNode node : this) { if (other.nodeExists(node) == false) { added.add(node); } } return new Delta( other.getMasterNode(), getMasterNode(), localNodeId, Collections.unmodifiableList(removed), Collections.unmodifiableList(added) ); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("nodes (node-left generation: ").append(nodeLeftGeneration).append("):\n"); for (DiscoveryNode node : this) { sb.append(" ").append(node); if (node == getLocalNode()) { sb.append(", local"); } if (node == getMasterNode()) { sb.append(", master"); } sb.append("\n"); } return sb.toString(); } public static class Delta { private final String localNodeId; @Nullable private final DiscoveryNode previousMasterNode; @Nullable private final DiscoveryNode newMasterNode; private final List removed; private final List added; private Delta( @Nullable DiscoveryNode previousMasterNode, @Nullable DiscoveryNode newMasterNode, String localNodeId, List removed, List added ) { this.previousMasterNode = previousMasterNode; this.newMasterNode = newMasterNode; this.localNodeId = localNodeId; this.removed = removed; this.added = added; } public boolean hasChanges() { return masterNodeChanged() || removed.isEmpty() == false || added.isEmpty() == false; } public boolean masterNodeChanged() { return Objects.equals(newMasterNode, previousMasterNode) == false; } @Nullable public DiscoveryNode previousMasterNode() { return previousMasterNode; } @Nullable public DiscoveryNode newMasterNode() { return newMasterNode; } public boolean removed() { return removed.isEmpty() == false; } public List removedNodes() { return removed; } public boolean added() { return added.isEmpty() == false; } public List addedNodes() { return added; } public String shortSummary() { final StringBuilder summary = new StringBuilder(); if (masterNodeChanged()) { summary.append("master node changed {previous ["); if (previousMasterNode != null) { previousMasterNode.appendDescriptionWithoutAttributes(summary); } summary.append("], current ["); if (newMasterNode != null) { newMasterNode.appendDescriptionWithoutAttributes(summary); } summary.append("]}"); } if (removed()) { if (summary.length() > 0) { summary.append(", "); } summary.append("removed {"); addCommaSeparatedNodesWithoutAttributes(removedNodes().iterator(), summary); summary.append('}'); } if (added()) { // ignore ourselves when reporting on nodes being added final Iterator addedNodesIterator = addedNodes().stream() .filter(node -> node.getId().equals(localNodeId) == false) .iterator(); if (addedNodesIterator.hasNext()) { if (summary.length() > 0) { summary.append(", "); } summary.append("added {"); addCommaSeparatedNodesWithoutAttributes(addedNodesIterator, summary); summary.append('}'); } } return summary.toString(); } } @Override public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(masterNodeId); if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_9_X)) { out.writeVLong(nodeLeftGeneration); } // else nodeLeftGeneration is zero, or we're sending this to a remote cluster which does not care about the nodeLeftGeneration out.writeCollection(nodes.values()); } public static DiscoveryNodes readFrom(StreamInput in, DiscoveryNode localNode) throws IOException { Builder builder = new Builder(); if (in.readBoolean()) { builder.masterNodeId(in.readString()); } if (localNode != null) { builder.localNodeId(localNode.getId()); } if (in.getTransportVersion().onOrAfter(TransportVersions.V_8_9_X)) { builder.nodeLeftGeneration(in.readVLong()); } // else nodeLeftGeneration is zero, or we're receiving this from a remote cluster so the nodeLeftGeneration does not matter to us int size = in.readVInt(); for (int i = 0; i < size; i++) { DiscoveryNode node = new DiscoveryNode(in); if (localNode != null && node.getId().equals(localNode.getId())) { // reuse the same instance of our address and local node id for faster equality node = localNode; } // some one already built this and validated it's OK, skip the n2 scans assert builder.validateAdd(node) == null : "building disco nodes from network doesn't pass preflight: " + builder.validateAdd(node); builder.putUnsafe(node); } return builder.build(); } public static Diff readDiffFrom(StreamInput in, DiscoveryNode localNode) throws IOException { return SimpleDiffable.readDiffFrom(in1 -> readFrom(in1, localNode), in); } public static Builder builder() { return new Builder(); } public static Builder builder(DiscoveryNodes nodes) { return new Builder(nodes); } public static class Builder { private final Map nodes; private String masterNodeId; private String localNodeId; private boolean removedNode; private final long oldNodeLeftGeneration; @Nullable // if not specified private Long nodeLeftGeneration; private boolean resetNodeLeftGeneration; public Builder() { nodes = new HashMap<>(); oldNodeLeftGeneration = 0L; } public Builder(DiscoveryNodes nodes) { this.masterNodeId = nodes.getMasterNodeId(); this.localNodeId = nodes.getLocalNodeId(); this.nodes = new HashMap<>(nodes.getNodes()); this.oldNodeLeftGeneration = nodes.nodeLeftGeneration; } /** * adds a disco node to the builder. Will throw an {@link IllegalArgumentException} if * the supplied node doesn't pass the pre-flight checks performed by {@link #validateAdd(DiscoveryNode)} */ public Builder add(DiscoveryNode node) { final String preflight = validateAdd(node); if (preflight != null) { throw new IllegalArgumentException(preflight); } putUnsafe(node); return this; } /** * Get a node by its id * * @param nodeId id of the wanted node * @return wanted node if it exists. Otherwise null */ @Nullable public DiscoveryNode get(String nodeId) { return nodes.get(nodeId); } private void putUnsafe(DiscoveryNode node) { nodes.put(node.getId(), node); } public Builder remove(String nodeId) { if (nodes.remove(nodeId) != null) { removedNode = true; } return this; } public Builder remove(DiscoveryNode node) { if (node.equals(nodes.get(node.getId()))) { nodes.remove(node.getId()); removedNode = true; } return this; } public Builder masterNodeId(String masterNodeId) { this.masterNodeId = masterNodeId; return this; } public Builder localNodeId(String localNodeId) { this.localNodeId = localNodeId; return this; } /** * Checks that a node can be safely added to this node collection. * * @return null if all is OK or an error message explaining why a node can not be added. * * Note: if this method returns a non-null value, calling {@link #add(DiscoveryNode)} will fail with an * exception */ private String validateAdd(DiscoveryNode node) { for (DiscoveryNode existingNode : nodes.values()) { if (node.getAddress().equals(existingNode.getAddress()) && node.getId().equals(existingNode.getId()) == false) { return "can't add node " + node + ", found existing node " + existingNode + " with same address"; } if (node.getId().equals(existingNode.getId()) && node.equals(existingNode) == false) { return "can't add node " + node + ", found existing node " + existingNode + " with the same id but is a different node instance"; } } return null; } private static > T min(T t1, T t2) { assert t2 != null; return t1 == null || t1.compareTo(t2) > 0 ? t2 : t1; } private static > T max(T t1, T t2) { assert t2 != null; return t1 == null || t1.compareTo(t2) < 0 ? t2 : t1; } public DiscoveryNodes build() { /* * Calculate all the version info we need. This is: * - The highest and lowest node versions in the cluster * - The lowest node version amongst data or master nodes * - The highest index version that is supported by all data/master nodes in the cluster * - The lowest index version that is supported by all nodes in the cluster * * Any of these values may be null (for example, if there are no data or master nodes), * in which case they are replaced with the defaults for this node */ Version minNodeVersion = null; Version maxNodeVersion = null; Version minNonClientNodeVersion = null; IndexVersion maxDataNodeCompatibleIndexVersion = null; IndexVersion minSupportedIndexVersion = null; for (Map.Entry nodeEntry : nodes.entrySet()) { DiscoveryNode discoNode = nodeEntry.getValue(); Version version = discoNode.getVersion(); if (discoNode.canContainData() || discoNode.isMasterNode()) { minNonClientNodeVersion = min(minNonClientNodeVersion, version); maxDataNodeCompatibleIndexVersion = min(maxDataNodeCompatibleIndexVersion, discoNode.getMaxIndexVersion()); } minNodeVersion = min(minNodeVersion, version); maxNodeVersion = max(maxNodeVersion, version); minSupportedIndexVersion = max(minSupportedIndexVersion, discoNode.getMinIndexVersion()); } final long newNodeLeftGeneration; if (minNodeVersion == null || minNodeVersion.before(Version.V_8_9_0)) { assert this.nodeLeftGeneration == null || this.nodeLeftGeneration == 0L; newNodeLeftGeneration = 0L; } else if (this.nodeLeftGeneration != null) { // only happens during deserialization assert removedNode == false; assert resetNodeLeftGeneration == false; newNodeLeftGeneration = nodeLeftGeneration; } else if (resetNodeLeftGeneration) { newNodeLeftGeneration = 0L; } else if (removedNode) { newNodeLeftGeneration = oldNodeLeftGeneration + 1L; } else { newNodeLeftGeneration = oldNodeLeftGeneration; } var dataNodes = filteredNodes(nodes, DiscoveryNode::canContainData); return new DiscoveryNodes( newNodeLeftGeneration, Map.copyOf(nodes), dataNodes, filteredNodes(nodes, DiscoveryNode::isMasterNode), filteredNodes(nodes, DiscoveryNode::isIngestNode), masterNodeId, localNodeId, Objects.requireNonNullElse(minNonClientNodeVersion, Version.CURRENT), Objects.requireNonNullElse(maxNodeVersion, Version.CURRENT), Objects.requireNonNullElse(minNodeVersion, Version.CURRENT.minimumCompatibilityVersion()), Objects.requireNonNullElse(maxDataNodeCompatibleIndexVersion, IndexVersion.current()), Objects.requireNonNullElse(minSupportedIndexVersion, IndexVersions.MINIMUM_COMPATIBLE), computeTiersToNodesMap(dataNodes) ); } private static Map> computeTiersToNodesMap(final Map dataNodes) { Map> tiersToNodes = new HashMap<>(ALL_DATA_TIERS.size() + 1); for (var node : dataNodes.values()) { if (node.hasRole(DiscoveryNodeRole.DATA_ROLE.roleName())) { tiersToNodes.computeIfAbsent(DiscoveryNodeRole.DATA_ROLE.roleName(), (key) -> new HashSet<>()).add(node.getId()); } for (var role : ALL_DATA_TIERS) { if (node.hasRole(role)) { tiersToNodes.computeIfAbsent(role, (key) -> new HashSet<>()).add(node.getId()); } } } for (var entry : tiersToNodes.entrySet()) { entry.setValue(Collections.unmodifiableSet(entry.getValue())); } return Collections.unmodifiableMap(tiersToNodes); } public boolean isLocalNodeElectedMaster() { return masterNodeId != null && masterNodeId.equals(localNodeId); } void nodeLeftGeneration(long nodeLeftGeneration) { // only for deserialization assert this.nodeLeftGeneration == null : nodeLeftGeneration + " vs " + this.nodeLeftGeneration; this.nodeLeftGeneration = nodeLeftGeneration; } public void resetNodeLeftGeneration() { assert this.resetNodeLeftGeneration == false; this.resetNodeLeftGeneration = true; } } private static Map filteredNodes(Map nodes, Predicate predicate) { return nodes.entrySet() .stream() .filter(e -> predicate.test(e.getValue())) .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); } public static void addCommaSeparatedNodesWithoutAttributes(Iterator iterator, StringBuilder stringBuilder) { while (iterator.hasNext()) { iterator.next().appendDescriptionWithoutAttributes(stringBuilder); if (iterator.hasNext()) { stringBuilder.append(", "); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy