summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java27
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java13
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java18
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);