private static int compareForRelocation(Node a, Node b) { // Choose smallest node int capacity = ResourceCapacity.of(a).compare(ResourceCapacity.of(b)); if (capacity != 0) return capacity; // Choose unallocated over allocated (this case is when we have ready docker nodes) if (!a.allocation().isPresent() && b.allocation().isPresent()) return -1; if (a.allocation().isPresent() && !b.allocation().isPresent()) return 1; // Choose container over content nodes if (a.allocation().isPresent() && b.allocation().isPresent()) { if (a.allocation().get().membership().cluster().type().equals(ClusterSpec.Type.container) && !b.allocation().get().membership().cluster().type().equals(ClusterSpec.Type.container)) return -1; if (!a.allocation().get().membership().cluster().type().equals(ClusterSpec.Type.container) && b.allocation().get().membership().cluster().type().equals(ClusterSpec.Type.container)) return 1; } // To get a stable algorithm - choose lexicographical from hostname return a.hostname().compareTo(b.hostname()); }
protected String toStringValue() { return cluster.type().name() + "/" + cluster.id().value() + (cluster.group().isPresent() ? "/" + cluster.group().get().index() : "") + "/" + index + ( cluster.isExclusive() ? "/exclusive" : "") + ( retired ? "/retired" : "") + ( !cluster.rotations().isEmpty() ? "/" + rotationsAsString(cluster.rotations()) : ""); }
private ClusterMembership(String stringValue, Version vespaVersion) { String[] components = stringValue.split("/"); if (components.length < 4 || components.length > 7) throw new RuntimeException("Could not parse '" + stringValue + "' to a cluster membership. " + "Expected 'clusterType/clusterId/groupId/index[/retired][/exclusive][/rotationId,...]'"); boolean exclusive = false; Set<RotationName> rotations = Collections.emptySet(); if (components.length > 4) { for (int i = 4; i < components.length; i++) { String component = components[i]; switch (component) { case "exclusive": exclusive = true; break; case "retired": retired = true; break; default: rotations = rotationsFrom(component); break; } } } this.cluster = ClusterSpec.from(ClusterSpec.Type.valueOf(components[0]), ClusterSpec.Id.from(components[1]), ClusterSpec.Group.from(Integer.valueOf(components[2])), vespaVersion, exclusive, rotations); this.index = Integer.parseInt(components[3]); this.stringValue = toStringValue(); }
"applicationId", applicationId.serializedForm().replace(':', '.'), "app", toApp(applicationId), "clustertype", allocation.get().membership().cluster().type().name(), "clusterid", allocation.get().membership().cluster().id().value());
/** * Returns the highest index number of all active and failed nodes in this cluster, or -1 if there are no nodes. * We include failed nodes to avoid reusing the index of the failed node in the case where the failed node is the * node with the highest index. */ private int findHighestIndex(ApplicationId application, ClusterSpec cluster) { int highestIndex = -1; for (Node node : nodeRepository.getNodes(application, Node.State.active, Node.State.inactive, Node.State.parked, Node.State.failed)) { ClusterSpec nodeCluster = node.allocation().get().membership().cluster(); if ( ! nodeCluster.id().equals(cluster.id())) continue; if ( ! nodeCluster.type().equals(cluster.type())) continue; highestIndex = Math.max(node.allocation().get().membership().index(), highestIndex); } return highestIndex; }
@Override public int hashCode() { return type.hashCode() + 17 * id.hashCode() + 31 * groupId.hashCode(); }
/** * Returns a list of the nodes which are * in groups with index number above or equal the group count */ private List<Node> findNodesInRemovableGroups(ApplicationId application, ClusterSpec requestedCluster, int wantedGroups) { List<Node> surplusNodes = new ArrayList<>(0); for (Node node : nodeRepository.getNodes(application, Node.State.active)) { ClusterSpec nodeCluster = node.allocation().get().membership().cluster(); if ( ! nodeCluster.id().equals(requestedCluster.id())) continue; if ( ! nodeCluster.type().equals(requestedCluster.type())) continue; if (nodeCluster.group().get().index() >= wantedGroups) surplusNodes.add(node); } return surplusNodes; }
private static int compareClusters(ClusterMembership cluster1, ClusterMembership cluster2) { // This depends on the declared order of enum constants. return cluster2.cluster().type().compareTo(cluster1.cluster().type()); }
/** Returns the subset of nodes assigned to the given cluster type */ public NodeList type(ClusterSpec.Type type) { return filter(node -> node.allocation().get().membership().cluster().type().equals(type)); }
/** Returns whether this is equal, disregarding the group value and wanted Vespa version */ public boolean equalsIgnoringGroupAndVespaVersion(Object o) { if (o == this) return true; if ( ! (o instanceof ClusterSpec)) return false; ClusterSpec other = (ClusterSpec)o; if ( ! other.type.equals(this.type)) return false; if ( ! other.id.equals(this.id)) return false; return true; }
@Override public boolean equals(Object o) { if (o == this) return true; if ( ! (o instanceof ClusterSpec)) return false; ClusterSpec other = (ClusterSpec)o; if ( ! other.type.equals(this.type)) return false; if ( ! other.id.equals(this.id)) return false; if ( ! other.groupId.equals(this.groupId)) return false; if ( ! other.vespaVersion.equals(this.vespaVersion)) return false; return true; }
private void toSlime(ClusterMembership membership, Cursor object) { object.setString("clustertype", membership.cluster().type().name()); object.setString("clusterid", membership.cluster().id().value()); object.setString("group", String.valueOf(membership.cluster().group().get().index())); object.setLong("index", membership.index()); object.setBool("retired", membership.retired()); }
private int totalAllocatedTo(ClusterSpec cluster) { int count = 0; for (Map.Entry<ClusterSpec, List<HostSpec>> allocation : allocations.entrySet()) { if ( ! allocation.getKey().type().equals(cluster.type())) continue; if ( ! allocation.getKey().id().equals(cluster.id())) continue; count += allocation.getValue().size(); } return count; }