aboutsummaryrefslogtreecommitdiffstats
path: root/clustercontroller-core/src/main/java/com/yahoo/vespa/clustercontroller/core/ClusterInfo.java
blob: 0f119d8de50a18478ce79502101041f1076e54f7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.clustercontroller.core;

import com.yahoo.vdslib.distribution.ConfiguredNode;
import com.yahoo.vdslib.distribution.Distribution;
import com.yahoo.vdslib.state.Node;
import com.yahoo.vespa.clustercontroller.core.listeners.NodeListener;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * Detailed information about the current state of all the distributor and storage nodes of the cluster.
 *
 * @author hakonhall
 * @author bratseth
 */
public class ClusterInfo {

    /** The configured nodes of this cluster, indexed by node index */
    private final Map<Integer, ConfiguredNode> nodes = new HashMap<>();

    /** Information about the current state of distributors */
    private final Map<Integer, DistributorNodeInfo> distributorNodeInfo = new TreeMap<>();
    /** Information about the current state of storage nodes */
    private final Map<Integer, StorageNodeInfo> storageNodeInfo = new TreeMap<>();
    /** Information about the current state of all nodes - always consists of both sets of nodes in the two maps above */
    private final Map<Node, NodeInfo> allNodeInfo = new TreeMap<>(); // TODO: Remove

    /** Returns non-null iff index is a configured nodes (except perhaps in tests). */
    DistributorNodeInfo getDistributorNodeInfo(int index) { return distributorNodeInfo.get(index); }

    /** Returns non-null iff index is a configured nodes (except perhaps in tests). */
    StorageNodeInfo getStorageNodeInfo(int index) { return storageNodeInfo.get(index); }

    /** Returns information about the given node id, or null if this node does not exist */
    public NodeInfo getNodeInfo(Node node) { return allNodeInfo.get(node); }

    Collection<DistributorNodeInfo> getDistributorNodeInfos() { return Collections.unmodifiableCollection(distributorNodeInfo.values()); }

    Collection<StorageNodeInfo> getStorageNodeInfos() { return Collections.unmodifiableCollection(storageNodeInfo.values()); }

    Collection<NodeInfo> getAllNodeInfos() { return Collections.unmodifiableCollection(allNodeInfo.values()); }

    /** Returns the configured nodes of this as a read-only map indexed on node index (distribution key) */
    Map<Integer, ConfiguredNode> getConfiguredNodes() { return Collections.unmodifiableMap(nodes); }

    boolean hasConfiguredNode(int index) { return nodes.containsKey(index); }

    /** Sets the nodes which belongs to this cluster */
    void setNodes(Collection<ConfiguredNode> newNodes, ContentCluster owner,
                  Distribution distribution, NodeListener nodeListener) {
        // Remove info for removed nodes
        Set<ConfiguredNode> newNodesSet = new HashSet<>(newNodes);
        for (ConfiguredNode existingNode : this.nodes.values()) {
            if ( ! newNodesSet.contains(existingNode)) {
                {
                    Node existingStorageNode = storageNodeInfo.remove(existingNode.index()).getNode();
                    allNodeInfo.remove(existingStorageNode);
                    nodeListener.handleRemovedNode(existingStorageNode);
                }

                {
                    Node existingDistributorNode = distributorNodeInfo.remove(existingNode.index()).getNode();
                    allNodeInfo.remove(existingDistributorNode);
                    nodeListener.handleRemovedNode(existingDistributorNode);
                }
            }
        }

        // Add and update new nodes info
        for (ConfiguredNode node : newNodes) {
            if ( ! nodes.containsKey(node.index())) { // add new node info
                addNodeInfo(new DistributorNodeInfo(owner, node.index(), null, distribution));
                addNodeInfo(new StorageNodeInfo(owner, node.index(), node.retired(), null, distribution));
            }
            else {
                getStorageNodeInfo(node.index()).setConfiguredRetired(node.retired());
            }
        }

        // Update node set
        nodes.clear();
        for (ConfiguredNode node : newNodes) {
            this.nodes.put(node.index(), node);
        }
    }

    private void addNodeInfo(NodeInfo nodeInfo) {
        if (nodeInfo instanceof DistributorNodeInfo) {
            distributorNodeInfo.put(nodeInfo.getNodeIndex(), (DistributorNodeInfo) nodeInfo);
        } else {
            storageNodeInfo.put(nodeInfo.getNodeIndex(), (StorageNodeInfo) nodeInfo);
        }
        allNodeInfo.put(nodeInfo.getNode(), nodeInfo);
        nodeInfo.setReportedState(nodeInfo.getReportedState().setDescription("Node not seen in slobrok."), 0);
    }

    /** Returns true if no nodes are down or unknown */
    boolean allStatesReported() {
        if (nodes.isEmpty()) return false;
        for (ConfiguredNode node : nodes.values()) {
            if (getDistributorNodeInfo(node.index()).getReportedState().getState().oneOf("d-")) return false;
            if (getStorageNodeInfo(node.index()).getReportedState().getState().oneOf("d-")) return false;
        }
        return true;
    }

    /**
     * Sets the rpc address of a node. If the node does not exist this does nothing.
     *
     * @return the info to which an rpc address is set, or null if none
     */
    public NodeInfo setRpcAddress(Node node, String rpcAddress) {
        NodeInfo nodeInfo = getInfo(node);
        if (nodeInfo != null) {
            nodeInfo.setRpcAddress(rpcAddress);
        }
        return nodeInfo;
    }
    // TODO: Do all mutation of node info through setters in this

    /** Returns the node info object for a given node identifier */
    private NodeInfo getInfo(Node node) {
        return switch (node.getType()) {
            case DISTRIBUTOR -> getDistributorNodeInfo(node.getIndex());
            case STORAGE -> getStorageNodeInfo(node.getIndex());
        };
    }

}