aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2023-01-13 11:04:34 +0100
committerMartin Polden <mpolden@mpolden.no>2023-01-13 15:42:01 +0100
commiteaac041f0b63b253cb345534370e0fd8d20c7d54 (patch)
tree400d6d048a0f7fc1df5b0ee3ce7d2c09dd83b39f
parent56f7ff45ceba9a4db0626b96e06c5ecb14d44ce9 (diff)
Move parent-child cache to NodeList
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java90
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodesAndHosts.java83
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java10
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java11
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/Rebalancer.java11
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainer.java7
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SwitchRebalancer.java11
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java40
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java15
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java29
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java14
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java2
13 files changed, 150 insertions, 185 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java
index 742b39e97c1..246a02aa441 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java
@@ -9,13 +9,17 @@ import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.node.ClusterId;
+import com.yahoo.vespa.hosted.provision.node.IP;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -30,13 +34,22 @@ import static java.util.stream.Collectors.collectingAndThen;
*/
public class NodeList extends AbstractFilteringList<Node, NodeList> {
+ private static final NodeList EMPTY = new NodeList(List.of(), false);
+
+ /**
+ * A lazily populated cache of parent-child relationships. This exists to improve the speed of parent<->child
+ * lookup which is a frequent operation
+ */
+ private final AtomicReference<Map<String, NodeFamily>> nodeCache = new AtomicReference<>(null);
+ private final AtomicReference<Set<String>> ipCache = new AtomicReference<>(null);
+
protected NodeList(List<Node> nodes, boolean negate) {
super(nodes, negate, NodeList::new);
}
/** Returns the node with the given hostname from this list, or empty if it is not present */
public Optional<Node> node(String hostname) {
- return matching(node -> node.hostname().equals(hostname)).first();
+ return get(hostname).map(NodeFamily::node);
}
/** Returns the subset of nodes which are retired */
@@ -189,7 +202,9 @@ public class NodeList extends AbstractFilteringList<Node, NodeList> {
/** Returns the child nodes of the given parent node */
public NodeList childrenOf(String hostname) {
- return matching(node -> node.hasParent(hostname));
+ NodeList children = get(hostname).map(NodeFamily::children).map(NodeList::copyOf).orElse(EMPTY);
+ // Fallback, in case the parent itself is not in this list
+ return children.isEmpty() ? matching(node -> node.hasParent(hostname)) : children;
}
public NodeList childrenOf(Node parent) {
@@ -221,24 +236,21 @@ public class NodeList extends AbstractFilteringList<Node, NodeList> {
public NodeList parentsOf(NodeList children) {
return children.stream()
.map(this::parentOf)
- .filter(Optional::isPresent)
.flatMap(Optional::stream)
.collect(collectingAndThen(Collectors.toList(), NodeList::copyOf));
}
+ /** Returns the parent node of the given child node */
+ public Optional<Node> parentOf(Node child) {
+ return child.parentHostname().flatMap(this::get).map(NodeFamily::node);
+ }
+
/** Returns the nodes contained in the group identified by given index */
public NodeList group(int index) {
return matching(n -> n.allocation().isPresent() &&
n.allocation().get().membership().cluster().group().equals(Optional.of(ClusterSpec.Group.from(index))));
}
- /** Returns the parent node of the given child node */
- public Optional<Node> parentOf(Node child) {
- return child.parentHostname()
- .flatMap(parentHostname -> stream().filter(node -> node.hostname().equals(parentHostname))
- .findFirst());
- }
-
/** Returns the hostnames of nodes in this */
public Set<String> hostnames() {
return stream().map(Node::hostname).collect(Collectors.toUnmodifiableSet());
@@ -316,6 +328,31 @@ public class NodeList extends AbstractFilteringList<Node, NodeList> {
});
}
+ /**
+ * Returns the number of unused IP addresses in the pool, assuming any and all unaccounted for hostnames
+ * in the pool are resolved to exactly 1 IP address (or 2 with {@link IP.IpAddresses.Protocol#dualStack}).
+ */
+ public int eventuallyUnusedIpAddressCount(Node host) {
+ // The count in this method relies on the size of the IP address pool if that's non-empty,
+ // otherwise fall back to the address/hostname pool.
+ if (host.ipConfig().pool().ipSet().isEmpty()) {
+ Set<String> allHostnames = cache().keySet();
+ return (int) host.ipConfig().pool().getAddressList().stream()
+ .filter(address -> !allHostnames.contains(address.hostname()))
+ .count();
+ }
+ Set<String> allIps = ipCache.updateAndGet((old) -> {
+ if (old != null) {
+ return old;
+ }
+ return stream().flatMap(node -> node.ipConfig().primary().stream())
+ .collect(Collectors.toUnmodifiableSet());
+ });
+ return (int) host.ipConfig().pool().ipSet().stream()
+ .filter(address -> !allIps.contains(address))
+ .count();
+ }
+
private void ensureSingleCluster() {
if (isEmpty()) return;
@@ -336,6 +373,7 @@ public class NodeList extends AbstractFilteringList<Node, NodeList> {
}
public static NodeList copyOf(List<Node> nodes) {
+ if (nodes.isEmpty()) return EMPTY;
return new NodeList(nodes, false);
}
@@ -354,4 +392,36 @@ public class NodeList extends AbstractFilteringList<Node, NodeList> {
return this.asList().equals(((NodeList) other).asList());
}
+ /** Get node family, by given hostname */
+ private Optional<NodeFamily> get(String hostname) {
+ return Optional.ofNullable(cache().get(hostname));
+ }
+
+ private Map<String, NodeFamily> cache() {
+ return nodeCache.updateAndGet((oldValue) -> {
+ if (oldValue != null) {
+ return oldValue;
+ }
+ Map<String, NodeFamily> newValue = new HashMap<>();
+ for (var node : this) {
+ NodeFamily family;
+ if (node.parentHostname().isEmpty()) {
+ family = new NodeFamily(node, new ArrayList<>());
+ for (var child : this) {
+ if (child.hasParent(node.hostname())) {
+ family.children.add(child);
+ }
+ }
+ } else {
+ family = new NodeFamily(node, List.of());
+ }
+ newValue.put(node.hostname(), family);
+ }
+ return newValue;
+ });
+ }
+
+ /** A node and its children, if any */
+ private record NodeFamily(Node node, List<Node> children) {}
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodesAndHosts.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodesAndHosts.java
deleted file mode 100644
index 6e7fc340231..00000000000
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodesAndHosts.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.provision;
-
-import com.yahoo.vespa.hosted.provision.node.IP;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Wraps a NodeList and builds a host to children mapping for faster access
- * as that is done very frequently.
- *
- * @author baldersheim
- */
-public class NodesAndHosts<NL extends NodeList> {
- private final NL nodes;
- private final Map<String, HostAndNodes> host2Nodes = new HashMap<>();
- private final Set<String> allPrimaryIps = new HashSet<>();
- private final Set<String> allHostNames;
-
- public static <L extends NodeList> NodesAndHosts<L> create(L nodes) {
- return new NodesAndHosts<L>(nodes);
- }
-
- private NodesAndHosts(NL nodes) {
- this.nodes = nodes;
- nodes.forEach(node -> allPrimaryIps.addAll(node.ipConfig().primary()));
- allHostNames = nodes.stream().map(Node::hostname).collect(Collectors.toSet());
- nodes.forEach(node -> {
- node.parentHostname().ifPresentOrElse(
- parent -> host2Nodes.computeIfAbsent(parent, key -> new HostAndNodes()).add(node),
- () -> host2Nodes.computeIfAbsent(node.hostname(), key -> new HostAndNodes()).setHost(node));
- });
-
- }
-
- /// Return the NodeList used for construction
- public NL nodes() { return nodes; }
-
- public NodeList childrenOf(Node host) {
- return childrenOf(host.hostname());
- }
- public NodeList childrenOf(String hostname) {
- HostAndNodes hostAndNodes = host2Nodes.get(hostname);
- return hostAndNodes != null ? NodeList.copyOf(hostAndNodes.children) : NodeList.of();
- }
-
- public Optional<Node> parentOf(Node node) {
- if (node.parentHostname().isEmpty()) return Optional.empty();
-
- HostAndNodes hostAndNodes = host2Nodes.get(node.parentHostname().get());
- return hostAndNodes != null ? Optional.ofNullable(hostAndNodes.host) : Optional.empty();
- }
-
- /**
- * Returns the number of unused IP addresses in the pool, assuming any and all unaccounted for hostnames
- * in the pool are resolved to exactly 1 IP address (or 2 with {@link IP.IpAddresses.Protocol#dualStack}).
- */
- public int eventuallyUnusedIpAddressCount(Node host) {
- // The count in this method relies on the size of the IP address pool if that's non-empty,
- // otherwise fall back to the address/hostname pool.
- return (int) (host.ipConfig().pool().ipSet().isEmpty()
- ? host.ipConfig().pool().getAddressList().stream().filter(address -> !allHostNames.contains(address.hostname())).count()
- : host.ipConfig().pool().ipSet().stream().filter(address -> !allPrimaryIps.contains(address)).count());
- }
-
- private static class HostAndNodes {
- private Node host;
- private final List<Node> children;
- HostAndNodes() {
- this.host = null;
- children = new ArrayList<>();
- }
- void setHost(Node host) { this.host = host; }
- void add(Node child) { children.add(child); }
- }
-}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java
index 5769f978089..1d5581b511d 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java
@@ -22,7 +22,6 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeMutex;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.History;
import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner;
@@ -31,6 +30,7 @@ import com.yahoo.vespa.hosted.provision.provisioning.NodeCandidate;
import com.yahoo.vespa.hosted.provision.provisioning.NodePrioritizer;
import com.yahoo.vespa.hosted.provision.provisioning.NodeSpec;
import com.yahoo.vespa.hosted.provision.provisioning.ProvisionedHost;
+
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@@ -222,8 +222,8 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer {
ArrayList<Node> mutableNodes) {
for (int clusterIndex = 0; clusterIndex < preprovisionCapacity.size(); ++clusterIndex) {
ClusterCapacity clusterCapacity = preprovisionCapacity.get(clusterIndex);
- NodesAndHosts<LockedNodeList> nodesAndHosts = NodesAndHosts.create(new LockedNodeList(mutableNodes, () -> {}));
- List<Node> candidates = findCandidates(clusterCapacity, clusterIndex, nodesAndHosts);
+ LockedNodeList allNodes = new LockedNodeList(mutableNodes, () -> {});
+ List<Node> candidates = findCandidates(clusterCapacity, clusterIndex, allNodes);
int deficit = Math.max(0, clusterCapacity.count() - candidates.size());
if (deficit > 0) {
return Optional.of(clusterCapacity.withCount(deficit));
@@ -236,7 +236,7 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer {
return Optional.empty();
}
- private List<Node> findCandidates(ClusterCapacity clusterCapacity, int clusterIndex, NodesAndHosts<LockedNodeList> nodesAndHosts) {
+ private List<Node> findCandidates(ClusterCapacity clusterCapacity, int clusterIndex, LockedNodeList allNodes) {
NodeResources nodeResources = toNodeResources(clusterCapacity);
// We'll allocate each ClusterCapacity as a unique cluster in a dummy application
@@ -249,7 +249,7 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer {
NodeSpec nodeSpec = NodeSpec.from(clusterCapacity.count(), nodeResources, false, true, nodeRepository().zone().cloud().account());
int wantedGroups = 1;
- NodePrioritizer prioritizer = new NodePrioritizer(nodesAndHosts, applicationId, clusterSpec, nodeSpec, wantedGroups,
+ NodePrioritizer prioritizer = new NodePrioritizer(allNodes, applicationId, clusterSpec, nodeSpec, wantedGroups,
true, nodeRepository().nameResolver(), nodeRepository().nodes(), nodeRepository().resourcesCalculator(),
nodeRepository().spareCount(), nodeSpec.cloudAccount().isEnclave(nodeRepository().zone()));
List<NodeCandidate> nodeCandidates = prioritizer.collect(List.of());
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java
index 552db84748d..fcc8e904a47 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMover.java
@@ -9,7 +9,6 @@ import com.yahoo.jdisc.Metric;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import com.yahoo.vespa.hosted.provision.provisioning.HostCapacity;
import java.time.Duration;
@@ -40,7 +39,7 @@ public abstract class NodeMover<MOVE> extends NodeRepositoryMaintainer {
}
/** Returns a suggested move for given node */
- protected abstract MOVE suggestedMove(Node node, Node fromHost, Node toHost, NodesAndHosts<? extends NodeList> allNodes);
+ protected abstract MOVE suggestedMove(Node node, Node fromHost, Node toHost, NodeList allNodes);
private static class HostWithResources {
private final Node node;
@@ -56,17 +55,17 @@ public abstract class NodeMover<MOVE> extends NodeRepositoryMaintainer {
}
/** Find the best possible move */
- protected final MOVE findBestMove(NodesAndHosts<? extends NodeList> allNodes) {
+ protected final MOVE findBestMove(NodeList allNodes) {
HostCapacity capacity = new HostCapacity(allNodes, nodeRepository().resourcesCalculator());
MOVE bestMove = emptyMove;
// Shuffle nodes to not get stuck if the chosen move is consistently discarded. Node moves happen through
// a soft request to retire (preferToRetire), which node allocation can disregard
- NodeList activeNodes = allNodes.nodes().nodeType(NodeType.tenant)
+ NodeList activeNodes = allNodes.nodeType(NodeType.tenant)
.state(Node.State.active)
.shuffle(random);
- Set<Node> spares = capacity.findSpareHosts(allNodes.nodes().asList(), nodeRepository().spareCount());
+ Set<Node> spares = capacity.findSpareHosts(allNodes.asList(), nodeRepository().spareCount());
List<HostWithResources> hostResources = new ArrayList<>();
- allNodes.nodes().matching(nodeRepository().nodes()::canAllocateTenantNodeTo).forEach(host -> hostResources.add(new HostWithResources(host, capacity.availableCapacityOf(host))));
+ allNodes.matching(nodeRepository().nodes()::canAllocateTenantNodeTo).forEach(host -> hostResources.add(new HostWithResources(host, capacity.availableCapacityOf(host))));
for (Node node : activeNodes) {
if (node.parentHostname().isEmpty()) continue;
ApplicationId applicationId = node.allocation().get().owner();
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/Rebalancer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/Rebalancer.java
index c853bfa2abe..3649f921480 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/Rebalancer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/Rebalancer.java
@@ -8,7 +8,6 @@ import com.yahoo.jdisc.Metric;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.provisioning.HostCapacity;
@@ -42,15 +41,15 @@ public class Rebalancer extends NodeMover<Rebalancer.Move> {
if (nodeRepository().zone().system().isCd()) return 1.0; // CD tests assert on # of nodes, avoid rebalnacing as it make tests unstable
// Work with an unlocked snapshot as this can take a long time and full consistency is not needed
- NodesAndHosts<NodeList> allNodes = NodesAndHosts.create(nodeRepository().nodes().list());
+ NodeList allNodes = nodeRepository().nodes().list();
updateSkewMetric(allNodes);
- if ( ! zoneIsStable(allNodes.nodes())) return 1.0;
+ if ( ! zoneIsStable(allNodes)) return 1.0;
findBestMove(allNodes).execute(true, Agent.Rebalancer, deployer, metric, nodeRepository());
return 1.0;
}
@Override
- protected Move suggestedMove(Node node, Node fromHost, Node toHost, NodesAndHosts<? extends NodeList> allNodes) {
+ protected Move suggestedMove(Node node, Node fromHost, Node toHost, NodeList allNodes) {
HostCapacity capacity = new HostCapacity(allNodes, nodeRepository().resourcesCalculator());
double skewReductionAtFromHost = skewReductionByRemoving(node, fromHost, capacity);
double skewReductionAtToHost = skewReductionByAdding(node, toHost, capacity);
@@ -65,11 +64,11 @@ public class Rebalancer extends NodeMover<Rebalancer.Move> {
}
/** We do this here rather than in MetricsReporter because it is expensive and frequent updates are unnecessary */
- private void updateSkewMetric(NodesAndHosts<? extends NodeList> allNodes) {
+ private void updateSkewMetric(NodeList allNodes) {
HostCapacity capacity = new HostCapacity(allNodes, nodeRepository().resourcesCalculator());
double totalSkew = 0;
int hostCount = 0;
- for (Node host : allNodes.nodes().nodeType(NodeType.host).state(Node.State.active)) {
+ for (Node host : allNodes.nodeType(NodeType.host).state(Node.State.active)) {
hostCount++;
totalSkew += Node.skew(host.flavor().resources(), capacity.unusedCapacityOf(host));
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainer.java
index d5b0c3baf19..5ce88346178 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainer.java
@@ -9,7 +9,6 @@ import com.yahoo.jdisc.Metric;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import com.yahoo.vespa.hosted.provision.maintenance.MaintenanceDeployment.Move;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.provisioning.HostCapacity;
@@ -117,13 +116,13 @@ public class SpareCapacityMaintainer extends NodeRepositoryMaintainer {
if (nodeWhichCantMove.isEmpty()) return List.of();
Node node = nodeWhichCantMove.get();
- NodesAndHosts<NodeList> allNodes = NodesAndHosts.create(nodeRepository().nodes().list());
+ NodeList allNodes = nodeRepository().nodes().list();
// Allocation will assign the spareCount most empty nodes as "spares", which will not be allocated on
// unless needed for node failing. Our goal here is to make room on these spares for the given node
HostCapacity hostCapacity = new HostCapacity(allNodes, nodeRepository().resourcesCalculator());
- Set<Node> spareHosts = hostCapacity.findSpareHosts(allNodes.nodes().hosts().satisfies(node.resources()).asList(),
+ Set<Node> spareHosts = hostCapacity.findSpareHosts(allNodes.hosts().satisfies(node.resources()).asList(),
nodeRepository().spareCount());
- List<Node> hosts = allNodes.nodes().hosts().except(spareHosts).asList();
+ List<Node> hosts = allNodes.hosts().except(spareHosts).asList();
CapacitySolver capacitySolver = new CapacitySolver(hostCapacity, maxIterations);
List<Move> shortestMitigation = null;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SwitchRebalancer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SwitchRebalancer.java
index f01e8ecd301..9f009b47983 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SwitchRebalancer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/SwitchRebalancer.java
@@ -8,7 +8,6 @@ import com.yahoo.jdisc.Metric;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import com.yahoo.vespa.hosted.provision.maintenance.MaintenanceDeployment.Move;
import com.yahoo.vespa.hosted.provision.node.Agent;
@@ -40,8 +39,8 @@ public class SwitchRebalancer extends NodeMover<Move> {
protected double maintain() {
if (!nodeRepository().nodes().isWorking()) return 0.0;
if (!nodeRepository().zone().environment().isProduction()) return 1.0;
- NodesAndHosts<NodeList> allNodes = NodesAndHosts.create(nodeRepository().nodes().list()); // Lockless as strong consistency is not needed
- if (!zoneIsStable(allNodes.nodes())) return 1.0;
+ NodeList allNodes = nodeRepository().nodes().list(); // Lockless as strong consistency is not needed
+ if (!zoneIsStable(allNodes)) return 1.0;
Move bestMove = findBestMove(allNodes);
if (!bestMove.isEmpty()) {
@@ -53,9 +52,9 @@ public class SwitchRebalancer extends NodeMover<Move> {
}
@Override
- protected Move suggestedMove(Node node, Node fromHost, Node toHost, NodesAndHosts<? extends NodeList> allNodes) {
- NodeList clusterNodes = clusterOf(node, allNodes.nodes());
- NodeList clusterHosts = allNodes.nodes().parentsOf(clusterNodes);
+ protected Move suggestedMove(Node node, Node fromHost, Node toHost, NodeList allNodes) {
+ NodeList clusterNodes = clusterOf(node, allNodes);
+ NodeList clusterHosts = allNodes.parentsOf(clusterNodes);
if (onExclusiveSwitch(node, clusterHosts)) return Move.empty();
if (!increasesExclusiveSwitches(clusterNodes, clusterHosts, toHost)) return Move.empty();
return new Move(node, fromHost, toHost);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
index 46cc32c7156..07f9c439fe2 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
@@ -11,7 +11,6 @@ import com.yahoo.transaction.Mutex;
import com.yahoo.vespa.hosted.provision.LockedNodeList;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner.HostSharing;
@@ -37,17 +36,8 @@ public class GroupPreparer {
private final NodeRepository nodeRepository;
private final Optional<HostProvisioner> hostProvisioner;
- /**
- * Contains list of prepared nodes and the NodesAndHost object to use for next prepare call.
- */
- public static class PrepareResult {
- public final List<Node> prepared;
- public final NodesAndHosts<LockedNodeList> allNodesAndHosts;
- PrepareResult(List<Node> prepared, NodesAndHosts<LockedNodeList> allNodesAndHosts) {
- this.prepared = prepared;
- this.allNodesAndHosts = allNodesAndHosts;
- }
- }
+ /** Contains list of prepared nodes and the {@link LockedNodeList} object to use for next prepare call */
+ record PrepareResult(List<Node> prepared, LockedNodeList allNodes) {}
public GroupPreparer(NodeRepository nodeRepository, Optional<HostProvisioner> hostProvisioner) {
this.nodeRepository = nodeRepository;
@@ -64,8 +54,8 @@ public class GroupPreparer {
* This method will remove from this list if it finds it needs additional nodes
* @param indices the next available node indices for this cluster.
* This method will consume these when it allocates new nodes to the cluster.
- * @param allNodesAndHosts list of all nodes and hosts. Use createNodesAndHostUnlocked to create param for
- * first invocation. Then use previous PrepareResult.allNodesAndHosts for the following.
+ * @param allNodes list of all nodes and hosts. Use {@link #createUnlockedNodeList()} to create param for
+ * first invocation. Then use previous {@link PrepareResult#allNodes()} for the following.
* @return the list of nodes this cluster group will have allocated if activated, and
*/
// Note: This operation may make persisted changes to the set of reserved and inactive nodes,
@@ -73,37 +63,37 @@ public class GroupPreparer {
// active config model which is changed on activate
public PrepareResult prepare(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes,
List<Node> surplusActiveNodes, NodeIndices indices, int wantedGroups,
- NodesAndHosts<LockedNodeList> allNodesAndHosts) {
+ LockedNodeList allNodes) {
log.log(Level.FINE, () -> "Preparing " + cluster.type().name() + " " + cluster.id() + " with requested resources " +
requestedNodes.resources().orElse(NodeResources.unspecified()));
// Try preparing in memory without global unallocated lock. Most of the time there should be no changes,
// and we can return nodes previously allocated.
NodeAllocation probeAllocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes,
- indices::probeNext, wantedGroups, allNodesAndHosts);
+ indices::probeNext, wantedGroups, allNodes);
if (probeAllocation.fulfilledAndNoChanges()) {
List<Node> acceptedNodes = probeAllocation.finalNodes();
surplusActiveNodes.removeAll(acceptedNodes);
indices.commitProbe();
- return new PrepareResult(acceptedNodes, allNodesAndHosts);
+ return new PrepareResult(acceptedNodes, allNodes);
} else {
// There were some changes, so re-do the allocation with locks
indices.resetProbe();
List<Node> prepared = prepareWithLocks(application, cluster, requestedNodes, surplusActiveNodes, indices, wantedGroups);
- return new PrepareResult(prepared, createNodesAndHostUnlocked());
+ return new PrepareResult(prepared, createUnlockedNodeList());
}
}
- // Use this to create allNodesAndHosts param to prepare method for first invocation of prepare
- public NodesAndHosts<LockedNodeList> createNodesAndHostUnlocked() { return NodesAndHosts.create(nodeRepository.nodes().list(PROBE_LOCK)); }
+ // Use this to create allNodes param to prepare method for first invocation of prepare
+ LockedNodeList createUnlockedNodeList() { return nodeRepository.nodes().list(PROBE_LOCK); }
/// Note that this will write to the node repo.
private List<Node> prepareWithLocks(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes,
List<Node> surplusActiveNodes, NodeIndices indices, int wantedGroups) {
try (Mutex lock = nodeRepository.applications().lock(application);
Mutex allocationLock = nodeRepository.nodes().lockUnallocated()) {
- NodesAndHosts<LockedNodeList> allNodesAndHosts = NodesAndHosts.create(nodeRepository.nodes().list(allocationLock));
+ LockedNodeList allNodes = nodeRepository.nodes().list(allocationLock);
NodeAllocation allocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes,
- indices::next, wantedGroups, allNodesAndHosts);
+ indices::next, wantedGroups, allNodes);
NodeType hostType = allocation.nodeType().hostType();
if (canProvisionDynamically(hostType) && allocation.hostDeficit().isPresent()) {
HostSharing sharing = hostSharing(cluster, hostType);
@@ -156,10 +146,10 @@ public class GroupPreparer {
private NodeAllocation prepareAllocation(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes,
List<Node> surplusActiveNodes, Supplier<Integer> nextIndex, int wantedGroups,
- NodesAndHosts<LockedNodeList> allNodesAndHosts) {
+ LockedNodeList allNodes) {
- NodeAllocation allocation = new NodeAllocation(allNodesAndHosts, application, cluster, requestedNodes, nextIndex, nodeRepository);
- NodePrioritizer prioritizer = new NodePrioritizer(allNodesAndHosts,
+ NodeAllocation allocation = new NodeAllocation(allNodes, application, cluster, requestedNodes, nextIndex, nodeRepository);
+ NodePrioritizer prioritizer = new NodePrioritizer(allNodes,
application,
cluster,
requestedNodes,
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java
index f29bf61149c..991bc22402d 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacity.java
@@ -5,7 +5,6 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import java.util.ArrayList;
import java.util.List;
@@ -23,18 +22,15 @@ import java.util.stream.Collectors;
*/
public class HostCapacity {
- private final NodesAndHosts<? extends NodeList> allNodes;
+ private final NodeList allNodes;
private final HostResourcesCalculator hostResourcesCalculator;
public HostCapacity(NodeList allNodes, HostResourcesCalculator hostResourcesCalculator) {
- this(NodesAndHosts.create(Objects.requireNonNull(allNodes, "allNodes must be non-null")), hostResourcesCalculator);
- }
- public HostCapacity(NodesAndHosts<? extends NodeList> allNodes, HostResourcesCalculator hostResourcesCalculator) {
this.allNodes = Objects.requireNonNull(allNodes, "allNodes must be non-null");
this.hostResourcesCalculator = Objects.requireNonNull(hostResourcesCalculator, "hostResourcesCalculator must be non-null");
}
- public NodeList allNodes() { return allNodes.nodes(); }
+ public NodeList allNodes() { return allNodes; }
/**
* Spare hosts are the hosts in the system with the most free capacity. A zone may reserve a minimum number of spare
@@ -97,9 +93,9 @@ public class HostCapacity {
/** Returns the number of available IP addresses on given host */
int freeIps(Node host) {
if (host.type() == NodeType.host) {
- return (allNodes.eventuallyUnusedIpAddressCount(host));
+ return allNodes.eventuallyUnusedIpAddressCount(host);
}
- return host.ipConfig().pool().findUnusedIpAddresses(allNodes.nodes()).size();
+ return host.ipConfig().pool().findUnusedIpAddresses(allNodes).size();
}
/** Returns the capacity of given host that is both free and usable */
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
index 63d5db09380..bf3ad5f15fb 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
@@ -14,7 +14,6 @@ import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Allocation;
@@ -43,7 +42,7 @@ class NodeAllocation {
private static final Logger LOG = Logger.getLogger(NodeAllocation.class.getName());
/** List of all nodes in node-repository */
- private final NodesAndHosts<? extends NodeList> allNodesAndHosts;
+ private final NodeList allNodes;
/** The application this list is for */
private final ApplicationId application;
@@ -86,9 +85,9 @@ class NodeAllocation {
private List<NodeCandidate> lastOffered;
- NodeAllocation(NodesAndHosts<? extends NodeList> allNodesAndHosts, ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes,
+ NodeAllocation(NodeList allNodes, ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes,
Supplier<Integer> nextIndex, NodeRepository nodeRepository) {
- this.allNodesAndHosts = allNodesAndHosts;
+ this.allNodes = allNodes;
this.application = application;
this.cluster = cluster;
this.requestedNodes = requestedNodes;
@@ -214,7 +213,7 @@ class NodeAllocation {
// In zones with shared hosts we require that if either of the nodes on the host requires exclusivity,
// then all the nodes on the host must have the same owner
- for (Node nodeOnHost : allNodesAndHosts.childrenOf(candidate.parentHostname().get())) {
+ for (Node nodeOnHost : allNodes.childrenOf(candidate.parentHostname().get())) {
if (nodeOnHost.allocation().isEmpty()) continue;
if (requestedNodes.isExclusive() || nodeOnHost.allocation().get().membership().cluster().isExclusive()) {
if ( ! nodeOnHost.allocation().get().owner().equals(application)) return true;
@@ -289,7 +288,7 @@ class NodeAllocation {
}
private Node resize(Node node) {
- NodeResources hostResources = allNodesAndHosts.parentOf(node).get().flavor().resources();
+ NodeResources hostResources = allNodes.parentOf(node).get().flavor().resources();
return node.with(new Flavor(requestedNodes.resources().get()
.with(hostResources.diskSpeed())
.with(hostResources.storageType())
@@ -341,7 +340,7 @@ class NodeAllocation {
if (hostType == NodeType.host) return nodeRepository.database().readProvisionIndices(count);
// Infrastructure hosts have fixed indices, starting at 1
- Set<Integer> currentIndices = allNodesAndHosts.nodes().nodeType(hostType)
+ Set<Integer> currentIndices = allNodes.nodeType(hostType)
.hostnames()
.stream()
// TODO(mpolden): Use cluster index instead of parsing hostname, once all
@@ -441,7 +440,7 @@ class NodeAllocation {
if (nodeType() == NodeType.tenant) return accepted;
// Infrastructure nodes are always allocated by type. Count all nodes as accepted so that we never exceed
// the wanted number of nodes for the type.
- return allNodesAndHosts.nodes().nodeType(nodeType()).size();
+ return allNodes.nodeType(nodeType()).size();
}
/** Prefer to retire nodes we want the least */
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java
index 79d05ce5c97..5e95d36fcfb 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java
@@ -7,7 +7,6 @@ import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.LockedNodeList;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import com.yahoo.vespa.hosted.provision.node.Nodes;
import com.yahoo.vespa.hosted.provision.persistence.NameResolver;
@@ -31,7 +30,7 @@ import java.util.stream.Collectors;
public class NodePrioritizer {
private final List<NodeCandidate> candidates = new ArrayList<>();
- private final NodesAndHosts<LockedNodeList> allNodesAndHosts;
+ private final LockedNodeList allNodes;
private final HostCapacity capacity;
private final NodeSpec requestedNodes;
private final ApplicationId application;
@@ -47,23 +46,23 @@ public class NodePrioritizer {
private final Set<Node> spareHosts;
private final boolean enclave;
- public NodePrioritizer(NodesAndHosts<LockedNodeList> allNodesAndHosts, ApplicationId application, ClusterSpec clusterSpec, NodeSpec nodeSpec,
+ public NodePrioritizer(LockedNodeList allNodes, ApplicationId application, ClusterSpec clusterSpec, NodeSpec nodeSpec,
int wantedGroups, boolean dynamicProvisioning, NameResolver nameResolver, Nodes nodes,
HostResourcesCalculator hostResourcesCalculator, int spareCount, boolean enclave) {
- this.allNodesAndHosts = allNodesAndHosts;
- this.capacity = new HostCapacity(this.allNodesAndHosts, hostResourcesCalculator);
+ this.allNodes = allNodes;
+ this.capacity = new HostCapacity(this.allNodes, hostResourcesCalculator);
this.requestedNodes = nodeSpec;
this.clusterSpec = clusterSpec;
this.application = application;
this.dynamicProvisioning = dynamicProvisioning;
this.spareHosts = dynamicProvisioning ?
- capacity.findSpareHostsInDynamicallyProvisionedZones(this.allNodesAndHosts.nodes().asList()) :
- capacity.findSpareHosts(this.allNodesAndHosts.nodes().asList(), spareCount);
+ capacity.findSpareHostsInDynamicallyProvisionedZones(this.allNodes.asList()) :
+ capacity.findSpareHosts(this.allNodes.asList(), spareCount);
this.nameResolver = nameResolver;
this.nodes = nodes;
this.enclave = enclave;
- NodeList nodesInCluster = this.allNodesAndHosts.nodes().owner(application).type(clusterSpec.type()).cluster(clusterSpec.id());
+ NodeList nodesInCluster = this.allNodes.owner(application).type(clusterSpec.type()).cluster(clusterSpec.id());
NodeList nonRetiredNodesInCluster = nodesInCluster.not().retired();
long currentGroups = nonRetiredNodesInCluster.state(Node.State.active).stream()
.flatMap(node -> node.allocation()
@@ -139,7 +138,7 @@ public class NodePrioritizer {
private void addCandidatesOnExistingHosts() {
if ( !canAllocateNew) return;
- for (Node host : allNodesAndHosts.nodes()) {
+ for (Node host : allNodes) {
if ( ! nodes.canAllocateTenantNodeTo(host, dynamicProvisioning)) continue;
if (host.reservedTo().isPresent() && !host.reservedTo().get().equals(application.tenant())) continue;
if (host.reservedTo().isPresent() && application.instance().isTester()) continue;
@@ -147,13 +146,13 @@ public class NodePrioritizer {
if ( ! host.exclusiveToClusterType().map(clusterSpec.type()::equals).orElse(true)) continue;
if (spareHosts.contains(host) && !canAllocateToSpareHosts) continue;
if ( ! capacity.hasCapacity(host, requestedNodes.resources().get())) continue;
- if ( ! allNodesAndHosts.childrenOf(host).owner(application).cluster(clusterSpec.id()).isEmpty()) continue;
+ if ( ! allNodes.childrenOf(host).owner(application).cluster(clusterSpec.id()).isEmpty()) continue;
candidates.add(NodeCandidate.createNewChild(requestedNodes.resources().get(),
capacity.availableCapacityOf(host),
host,
spareHosts.contains(host),
- allNodesAndHosts.nodes(),
+ allNodes,
nameResolver,
!enclave));
}
@@ -162,7 +161,7 @@ public class NodePrioritizer {
/** Add existing nodes allocated to the application */
private void addApplicationNodes() {
EnumSet<Node.State> legalStates = EnumSet.of(Node.State.active, Node.State.inactive, Node.State.reserved);
- allNodesAndHosts.nodes().stream()
+ allNodes.stream()
.filter(node -> node.type() == requestedNodes.type())
.filter(node -> legalStates.contains(node.state()))
.filter(node -> node.allocation().isPresent())
@@ -175,7 +174,7 @@ public class NodePrioritizer {
/** Add nodes already provisioned, but not allocated to any application */
private void addReadyNodes() {
- allNodesAndHosts.nodes().stream()
+ allNodes.stream()
.filter(node -> node.type() == requestedNodes.type())
.filter(node -> node.state() == Node.State.ready)
.map(node -> candidateFrom(node, false))
@@ -185,7 +184,7 @@ public class NodePrioritizer {
/** Create a candidate from given pre-existing node */
private NodeCandidate candidateFrom(Node node, boolean isSurplus) {
- Optional<Node> optionalParent = allNodesAndHosts.parentOf(node);
+ Optional<Node> optionalParent = allNodes.parentOf(node);
if (optionalParent.isPresent()) {
Node parent = optionalParent.get();
return NodeCandidate.createChild(node,
@@ -223,7 +222,7 @@ public class NodePrioritizer {
*/
private boolean canStillAllocate(Node node) {
if (node.type() != NodeType.tenant || node.parentHostname().isEmpty()) return true;
- Optional<Node> parent = allNodesAndHosts.parentOf(node);
+ Optional<Node> parent = allNodes.parentOf(node);
return parent.isPresent() && nodes.canAllocateTenantNodeTo(parent.get(), dynamicProvisioning);
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java
index 139e8848ab1..b6c7324c75c 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java
@@ -9,13 +9,11 @@ import com.yahoo.vespa.hosted.provision.LockedNodeList;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.NodesAndHosts;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Optional;
-import java.util.stream.Collectors;
/**
* Performs preparation of node activation changes for an application.
@@ -58,8 +56,8 @@ class Preparer {
// active config model which is changed on activate
private List<Node> prepareNodes(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes,
int wantedGroups) {
- NodesAndHosts<LockedNodeList> allNodesAndHosts = groupPreparer.createNodesAndHostUnlocked();
- NodeList appNodes = allNodesAndHosts.nodes().owner(application);
+ LockedNodeList allNodes = groupPreparer.createUnlockedNodeList();
+ NodeList appNodes = allNodes.owner(application);
List<Node> surplusNodes = findNodesInRemovableGroups(appNodes, cluster, wantedGroups);
List<Integer> usedIndices = appNodes.cluster(cluster.id()).mapToList(node -> node.allocation().get().membership().index());
@@ -71,11 +69,11 @@ class Preparer {
GroupPreparer.PrepareResult result = groupPreparer.prepare(application, clusterGroup,
requestedNodes.fraction(wantedGroups),
surplusNodes, indices, wantedGroups,
- allNodesAndHosts);
- allNodesAndHosts = result.allNodesAndHosts; // Might have changed
- List<Node> accepted = result.prepared;
+ allNodes);
+ allNodes = result.allNodes(); // Might have changed
+ List<Node> accepted = result.prepared();
if (requestedNodes.rejectNonActiveParent()) {
- NodeList activeHosts = allNodesAndHosts.nodes().state(Node.State.active).parents().nodeType(requestedNodes.type().hostType());
+ NodeList activeHosts = allNodes.state(Node.State.active).parents().nodeType(requestedNodes.type().hostType());
accepted = accepted.stream()
.filter(node -> node.parentHostname().isEmpty() || activeHosts.parentOf(node).isPresent())
.toList();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java
index 2327b40885f..23c2d0fc47a 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java
@@ -547,7 +547,7 @@ public class DynamicAllocationTest {
}
private List<Node> findSpareCapacity(ProvisioningTester tester) {
- NodeList nodes = tester.nodeRepository().nodes().list(State.values());
+ NodeList nodes = tester.nodeRepository().nodes().list();
return nodes.nodeType(NodeType.host)
.matching(host -> nodes.childrenOf(host).size() == 0) // Hosts without children
.asList();