diff options
4 files changed, 42 insertions, 20 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java index b5d16c35dac..55074b46c19 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java @@ -56,6 +56,10 @@ public class NodeResources { public double diskGb() { return diskGb; } public DiskSpeed diskSpeed() { return diskSpeed; } + public NodeResources withDiskSpeed(DiskSpeed speed) { + return new NodeResources(vcpu, memoryGb, diskGb, speed); + } + public NodeResources subtract(NodeResources other) { if ( ! this.isInterchangeableWith(other)) throw new IllegalArgumentException(this + " and " + other + " are not interchangeable"); 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 bf8ea6158ee..294ba12497a 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 @@ -148,31 +148,31 @@ class NodePrioritizer { if ( ! isDocker) return; NodeResources wantedResources = resources(requestedNodes); - for (Node node : candidates) { - if (node.type() != NodeType.host) continue; - if (node.status().wantToRetire()) continue; + for (Node host : candidates) { + if (host.type() != NodeType.host) continue; + if (host.status().wantToRetire()) continue; - boolean hostHasCapacityForWantedFlavor = capacity.hasCapacity(node, wantedResources); - boolean conflictingCluster = allNodes.childrenOf(node).owner(appId).asList().stream() + boolean hostHasCapacityForWantedFlavor = capacity.hasCapacity(host, wantedResources); + boolean conflictingCluster = allNodes.childrenOf(host).owner(appId).asList().stream() .anyMatch(child -> child.allocation().get().membership().cluster().id().equals(clusterSpec.id())); if (!hostHasCapacityForWantedFlavor || conflictingCluster) continue; - log.log(LogLevel.DEBUG, "Trying to add new Docker node on " + node); + log.log(LogLevel.DEBUG, "Trying to add new Docker node on " + host); Optional<IP.Allocation> allocation; try { - allocation = node.ipConfig().pool().findAllocation(allNodes, nameResolver); + allocation = host.ipConfig().pool().findAllocation(allNodes, nameResolver); if (allocation.isEmpty()) continue; // No free addresses in this pool } catch (Exception e) { - log.log(LogLevel.WARNING, "Failed allocating IP address on " + node.hostname(), e); + log.log(LogLevel.WARNING, "Failed allocating IP address on " + host.hostname(), e); continue; } Node newNode = Node.createDockerNode(allocation.get().addresses(), Set.of(), allocation.get().hostname(), - Optional.of(node.hostname()), - resources(requestedNodes), + Optional.of(host.hostname()), + resources(requestedNodes).withDiskSpeed(host.flavor().resources().diskSpeed()), NodeType.tenant); PrioritizableNode nodePri = toNodePriority(newNode, false, true); if ( ! nodePri.violatesSpares || isAllocatingForReplacement) { @@ -256,11 +256,8 @@ class NodePrioritizer { } private static NodeResources resources(NodeSpec requestedNodes) { - if (requestedNodes instanceof NodeSpec.CountNodeSpec) { - NodeSpec.CountNodeSpec countSpec = (NodeSpec.CountNodeSpec) requestedNodes; - return countSpec.resources(); - } - return null; + if ( ! (requestedNodes instanceof NodeSpec.CountNodeSpec)) return null; + return ((NodeSpec.CountNodeSpec)requestedNodes).resources(); } private boolean isDocker() { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java index d54b1e66708..5c2c78cef5d 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java @@ -97,11 +97,14 @@ public interface NodeSpec { return true; } else { - // Note: changing this condition to flavor.resources().satisfies(requestedNodeResources)) - // is semantically correct, but we only want partial matching when allocating from docker hosts, - // which is done separately, so we compare by equality here - if (requestedNodeResources.equals(flavor.resources())) - return true; + if (flavor.isDocker()) { // Docker nodes can satisfy a request for parts of their resources + if (flavor.resources().satisfies(requestedNodeResources)) + return true; + } + else { // Other nodes must be matched exactly + if (requestedNodeResources.equals(flavor.resources())) + return true; + } } return requestedFlavorCanBeAchievedByResizing(flavor); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java index c5a8e5213b6..4141161c20e 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java @@ -294,6 +294,24 @@ public class DynamicDockerAllocationTest { provisionFastAndSlowThenDeploy(NodeResources.DiskSpeed.any, false); } + @Test + public void slow_disk_nodes_are_preferentially_allocated() { + ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); + tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 2, 3, NodeResources.DiskSpeed.fast)), NodeType.host, 10, true); + tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 2, 3, NodeResources.DiskSpeed.slow)), NodeType.host, 10, true); + deployZoneApp(tester); + + ApplicationId application = tester.makeApplicationId(); + ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("1"), false); + NodeResources resources = new NodeResources(1, 1, 1, NodeResources.DiskSpeed.any); + + List<HostSpec> hosts = tester.prepare(application, cluster, 2, 1, resources); + assertEquals(2, hosts.size()); + assertEquals(NodeResources.DiskSpeed.slow, hosts.get(0).flavor().get().resources().diskSpeed()); + assertEquals(NodeResources.DiskSpeed.slow, hosts.get(1).flavor().get().resources().diskSpeed()); + tester.activate(application, hosts); + } + private void provisionFastAndSlowThenDeploy(NodeResources.DiskSpeed requestDiskSpeed, boolean expectOutOfCapacity) { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 2, 3, NodeResources.DiskSpeed.fast)), NodeType.host, 10, true); |