Java tutorial
/* * 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.slider.server.appmaster.state; import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.NodeReport; import org.apache.hadoop.yarn.api.records.NodeState; import org.apache.slider.api.types.NodeInformation; import org.apache.slider.common.tools.Comparators; import org.apache.slider.common.tools.SliderUtils; import java.io.Serializable; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; /** * A node instance -stores information about a node in the cluster. * <p> * Operations on the array/set of roles are synchronized. */ public class NodeInstance { public final String hostname; /** * last state of node. Starts off as {@link NodeState#RUNNING}, * on the assumption that it is live. */ private NodeState nodeState = NodeState.RUNNING; /** * Last node report. If null: none */ private NodeReport nodeReport = null; /** * time of state update */ private long nodeStateUpdateTime = 0; /** * Node labels. * * IMPORTANT: we assume that there is one label/node, which is the policy * for Hadoop as of November 2015 */ private String nodeLabels = ""; /** * An unordered list of node entries of specific roles. There's nothing * indexed so as to support sparser datastructures. */ private final List<NodeEntry> nodeEntries; /** * Create an instance and the (empty) array of nodes * @param roles role count -the no. of roles */ public NodeInstance(String hostname, int roles) { this.hostname = hostname; nodeEntries = new ArrayList<>(roles); } /** * Update the node status. * The return code is true if the node state changed enough to * trigger a re-evaluation of pending requests. That is, either a node * became available when it was previously not, or the label changed * on an available node. * * Transitions of a node from live to dead aren't treated as significant, * nor label changes on a dead node. * * @param report latest node report * @return true if the node state changed enough for a request evaluation. */ public synchronized boolean updateNode(NodeReport report) { nodeStateUpdateTime = report.getLastHealthReportTime(); nodeReport = report; NodeState oldState = nodeState; boolean oldStateUnusable = oldState.isUnusable(); nodeState = report.getNodeState(); boolean newUsable = !nodeState.isUnusable(); boolean nodeNowAvailable = oldStateUnusable && newUsable; String labels = this.nodeLabels; nodeLabels = SliderUtils.extractNodeLabel(report); return nodeNowAvailable || newUsable && !this.nodeLabels.equals(labels); } public String getNodeLabels() { return nodeLabels; } /** * Get the entry for a role -if present * @param role role index * @return the entry * null if the role is out of range */ public synchronized NodeEntry get(int role) { for (NodeEntry nodeEntry : nodeEntries) { if (nodeEntry.rolePriority == role) { return nodeEntry; } } return null; } /** * Get the entry for a role -if present * @param role role index * @return the entry * @throws ArrayIndexOutOfBoundsException if the role is out of range */ public synchronized NodeEntry getOrCreate(int role) { NodeEntry entry = get(role); if (entry == null) { entry = new NodeEntry(role); nodeEntries.add(entry); } return entry; } /** * Get the node entry matching a container on this node * @param container container * @return matching node instance for the role */ public NodeEntry getOrCreate(Container container) { return getOrCreate(ContainerPriority.extractRole(container)); } /** * Count the number of active role instances on this node * @param role role index * @return 0 if there are none, otherwise the #of nodes that are running and * not being released already. */ public int getActiveRoleInstances(int role) { NodeEntry nodeEntry = get(role); return (nodeEntry != null) ? nodeEntry.getActive() : 0; } /** * Count the number of live role instances on this node * @param role role index * @return 0 if there are none, otherwise the #of nodes that are running */ public int getLiveRoleInstances(int role) { NodeEntry nodeEntry = get(role); return (nodeEntry != null) ? nodeEntry.getLive() : 0; } /** * Is the node considered online * @return the node */ public boolean isOnline() { return !nodeState.isUnusable(); } /** * Query for a node being considered unreliable * @param role role key * @param threshold threshold above which a node is considered unreliable * @return true if the node is considered unreliable */ public boolean isConsideredUnreliable(int role, int threshold) { NodeEntry entry = get(role); return entry != null && entry.getFailedRecently() > threshold; } /** * Get the entry for a role -and remove it if present * @param role the role index * @return the entry that WAS there */ public synchronized NodeEntry remove(int role) { NodeEntry nodeEntry = get(role); if (nodeEntry != null) { nodeEntries.remove(nodeEntry); } return nodeEntry; } public synchronized void set(int role, NodeEntry nodeEntry) { remove(role); nodeEntries.add(nodeEntry); } /** * run through each entry; gc'ing & removing old ones that don't have * a recent failure count (we care about those) * @param absoluteTime age in millis * @return true if there are still entries left */ public synchronized boolean purgeUnusedEntries(long absoluteTime) { boolean active = false; ListIterator<NodeEntry> entries = nodeEntries.listIterator(); while (entries.hasNext()) { NodeEntry entry = entries.next(); if (entry.notUsedSince(absoluteTime) && entry.getFailedRecently() == 0) { entries.remove(); } else { active = true; } } return active; } /** * run through each entry resetting the failure count */ public synchronized void resetFailedRecently() { for (NodeEntry entry : nodeEntries) { entry.resetFailedRecently(); } } @Override public String toString() { return hostname; } /** * Full dump of entry including children * @return a multi-line description fo the node */ public String toFullString() { final StringBuilder sb = new StringBuilder(toString()); sb.append("{ "); for (NodeEntry entry : nodeEntries) { sb.append(String.format("\n [%02d] ", entry.rolePriority)); sb.append(entry.toString()); } sb.append("} "); return sb.toString(); } /** * Equality test is purely on the hostname of the node address * @param o other * @return true if the hostnames are equal */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } NodeInstance that = (NodeInstance) o; return hostname.equals(that.hostname); } @Override public int hashCode() { return hostname.hashCode(); } /** * Predicate to query if the number of recent failures of a role * on this node exceeds that role's failure threshold. * If there is no record of a deployment of that role on this * node, the failure count is taken as "0". * @param role role to look up * @return true if the failure rate is above the threshold. */ public boolean exceedsFailureThreshold(RoleStatus role) { NodeEntry entry = get(role.getKey()); int numFailuresOnLastHost = entry != null ? entry.getFailedRecently() : 0; int failureThreshold = role.getNodeFailureThreshold(); return failureThreshold < 0 || numFailuresOnLastHost > failureThreshold; } /** * Produced a serialized form which can be served up as JSON * @param naming map of priority -> value for naming entries * @return a summary of the current role status. */ public synchronized NodeInformation serialize(Map<Integer, String> naming) { NodeInformation info = new NodeInformation(); info.hostname = hostname; // null-handling state constructor info.state = "" + nodeState; info.lastUpdated = nodeStateUpdateTime; info.labels = nodeLabels; if (nodeReport != null) { info.httpAddress = nodeReport.getHttpAddress(); info.rackName = nodeReport.getRackName(); info.healthReport = nodeReport.getHealthReport(); } info.entries = new HashMap<>(nodeEntries.size()); for (NodeEntry nodeEntry : nodeEntries) { String name = naming.get(nodeEntry.rolePriority); if (name == null) { name = Integer.toString(nodeEntry.rolePriority); } info.entries.put(name, nodeEntry.serialize()); } return info; } /** * Is this node instance a suitable candidate for the specific role? * @param role role ID * @param label label which must match, or "" for no label checks * @return true if the node has space for this role, is running and the labels * match. */ public boolean canHost(int role, String label) { return isOnline() && (SliderUtils.isUnset(label) || label.equals(nodeLabels)) // label match && getOrCreate(role).isAvailable(); // no live role } /** * A comparator for sorting entries where the node is preferred over another. * * The exact algorithm may change: current policy is "most recent first", so sorted * on the lastUsed * * the comparision is a positive int if left is preferred to right; * negative if right over left, 0 for equal */ public static class Preferred implements Comparator<NodeInstance>, Serializable { private static final Comparators.InvertedLongComparator comparator = new Comparators.InvertedLongComparator(); private final int role; public Preferred(int role) { this.role = role; } @Override public int compare(NodeInstance o1, NodeInstance o2) { NodeEntry left = o1.get(role); NodeEntry right = o2.get(role); long ageL = left != null ? left.getLastUsed() : -1; long ageR = right != null ? right.getLastUsed() : -1; return comparator.compare(ageL, ageR); } } /** * A comparator for sorting entries where the role is newer than * the other. * This sort only compares the lastUsed field, not whether the * node is in use or not */ public static class MoreActiveThan implements Comparator<NodeInstance>, Serializable { private final int role; public MoreActiveThan(int role) { this.role = role; } @Override public int compare(NodeInstance left, NodeInstance right) { int activeLeft = left.getActiveRoleInstances(role); int activeRight = right.getActiveRoleInstances(role); return activeRight - activeLeft; } } /** * A comparator for sorting entries alphabetically */ public static class CompareNames implements Comparator<NodeInstance>, Serializable { public CompareNames() { } @Override public int compare(NodeInstance left, NodeInstance right) { return left.hostname.compareTo(right.hostname); } } }