aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java7
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java188
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ResourceTarget.java27
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java33
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java10
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java19
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java22
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java8
11 files changed, 143 insertions, 193 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 04b004bb5a5..91604cd667d 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
@@ -242,10 +242,10 @@ public class NodeResources {
/** Returns true if all the resources of this are the same as or compatible with the given resources */
public boolean compatibleWith(NodeResources other) {
- if (this.vcpu != other.vcpu) return false;
- if (this.memoryGb != other.memoryGb) return false;
- if (this.diskGb != other.diskGb) return false;
- if (this.bandwidthGbps != other.bandwidthGbps) return false;
+ if ( ! equal(this.vcpu, other.vcpu)) return false;
+ if ( ! equal(this.memoryGb, other.memoryGb)) return false;
+ if ( ! equal(this.diskGb, other.diskGb)) return false;
+ if ( ! equal(this.bandwidthGbps, other.bandwidthGbps)) return false;
if ( ! this.diskSpeed.compatibleWith(other.diskSpeed)) return false;
if ( ! this.storageType.compatibleWith(other.storageType)) return false;
@@ -254,6 +254,10 @@ public class NodeResources {
public boolean isUnspecified() { return this == unspecified; }
+ private boolean equal(double a, double b) {
+ return Math.abs(a - b) < 0.00000001;
+ }
+
/**
* Create this from serial form.
*
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java
index def3b18d14c..1637d99a07a 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java
@@ -118,13 +118,16 @@ public class AllocatableClusterResources {
}
public boolean preferableTo(AllocatableClusterResources other) {
- if (this.fulfilment > other.fulfilment) return true; // we always want to fulfil as much as possible
+ if (this.fulfilment < 1 || other.fulfilment < 1)
+ return this.fulfilment > other.fulfilment; // we always want to fulfil as much as possible
return this.cost() < other.cost(); // otherwise, prefer lower cost
}
@Override
public String toString() {
- return nodes + " nodes with " + realResources() +
+ return nodes + " nodes " +
+ ( groups > 1 ? "(in " + groups + " groups) " : "" ) +
+ "with " + realResources() +
" at cost $" + cost() +
(fulfilment < 1.0 ? " (fulfilment " + fulfilment + ")" : "");
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java
index 8d26bb89959..f0f53161d9b 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java
@@ -14,6 +14,15 @@ import java.util.Optional;
*/
public class AllocationOptimizer {
+ // The min and max nodes to consider when not using application supplied limits
+ private static final int minimumNodes = 2; // Since this number includes redundancy it cannot be lower than 2
+ private static final int maximumNodes = 150;
+
+ // When a query is issued on a node the cost is the sum of a fixed cost component and a cost component
+ // proportional to document count. We must account for this when comparing configurations with more or fewer nodes.
+ // TODO: Measure this, and only take it into account with queries
+ private static final double fixedCpuCostFraction = 0.1;
+
private final NodeRepository nodeRepository;
public AllocationOptimizer(NodeRepository nodeRepository) {
@@ -30,143 +39,66 @@ public class AllocationOptimizer {
public Optional<AllocatableClusterResources> findBestAllocation(ResourceTarget target,
AllocatableClusterResources current,
Limits limits) {
+ if (limits.isEmpty())
+ limits = Limits.of(new ClusterResources(minimumNodes, 1, NodeResources.unspecified),
+ new ClusterResources(maximumNodes, maximumNodes, NodeResources.unspecified));
Optional<AllocatableClusterResources> bestAllocation = Optional.empty();
- for (ResourceIterator i = new ResourceIterator(target, current, limits); i.hasNext(); ) {
- var allocatableResources = AllocatableClusterResources.from(i.next(), current.clusterType(), limits, nodeRepository);
- if (allocatableResources.isEmpty()) continue;
- if (bestAllocation.isEmpty() || allocatableResources.get().preferableTo(bestAllocation.get()))
- bestAllocation = allocatableResources;
+ for (int groups = limits.min().groups(); groups <= limits.max().groups(); groups++) {
+ for (int nodes = limits.min().nodes(); nodes <= limits.max().nodes(); nodes++) {
+ if (nodes % groups != 0) continue;
+ int groupSize = nodes / groups;
+
+ // Adjust for redundancy: Node in group if groups = 1, an extra group if multiple groups
+ // TODO: Make the best choice based on size and redundancy setting instead
+ int nodesAdjustedForRedundancy = target.adjustForRedundancy() ? (groups == 1 ? nodes - 1 : nodes - groupSize) : nodes;
+ int groupsAdjustedForRedundancy = target.adjustForRedundancy() ? (groups == 1 ? 1 : groups - 1) : groups;
+
+ ClusterResources next = new ClusterResources(nodes,
+ groups,
+ nodeResourcesWith(nodesAdjustedForRedundancy, groupsAdjustedForRedundancy, limits, current, target));
+
+ var allocatableResources = AllocatableClusterResources.from(next, current.clusterType(), limits, nodeRepository);
+ if (allocatableResources.isEmpty()) continue;
+ if (bestAllocation.isEmpty() || allocatableResources.get().preferableTo(bestAllocation.get()))
+ bestAllocation = allocatableResources;
+ }
}
+
return bestAllocation;
}
/**
- * Provides iteration over possible cluster resource allocations given a target total load
- * and current groups/nodes allocation.
+ * For the observed load this instance is initialized with, returns the resources needed per node to be at
+ * ideal load given a target node count
*/
- private static class ResourceIterator {
-
- // The min and max nodes to consider when not using application supplied limits
- private static final int minimumNodes = 3; // Since this number includes redundancy it cannot be lower than 2
- private static final int maximumNodes = 150;
-
- // When a query is issued on a node the cost is the sum of a fixed cost component and a cost component
- // proportional to document count. We must account for this when comparing configurations with more or fewer nodes.
- // TODO: Measure this, and only take it into account with queries
- private static final double fixedCpuCostFraction = 0.1;
-
- // Given state
- private final Limits limits;
- private final AllocatableClusterResources current;
- private final ResourceTarget target;
-
- // Derived from the observed state
- private final int nodeIncrement;
- private final boolean singleGroupMode;
-
- // Iterator state
- private int currentNodes;
-
- public ResourceIterator(ResourceTarget target, AllocatableClusterResources current, Limits limits) {
- this.target = target;
- this.current = current;
- this.limits = limits;
-
- // What number of nodes is it effective to add or remove at the time from this cluster?
- // This is the group size, since we (for now) assume the group size is decided by someone wiser than us
- // and we decide the number of groups.
- // The exception is when we only have one group, where we can add and remove single nodes in it.
- singleGroupMode = current.groups() == 1;
- nodeIncrement = singleGroupMode ? 1 : current.groupSize();
-
- // Step to the right starting point
- currentNodes = current.nodes();
- if (currentNodes < minNodes()) { // step up
- while (currentNodes < minNodes()
- && (singleGroupMode || currentNodes + nodeIncrement > current.groupSize())) // group level redundancy
- currentNodes += nodeIncrement;
- }
- else { // step down
- while (currentNodes - nodeIncrement >= minNodes()
- && (singleGroupMode || currentNodes - nodeIncrement > current.groupSize())) // group level redundancy
- currentNodes -= nodeIncrement;
- }
+ private NodeResources nodeResourcesWith(int nodes, int groups, Limits limits, AllocatableClusterResources current, ResourceTarget target) {
+ // Cpu: Scales with cluster size (TODO: Only reads, writes scales with group size)
+ // Memory and disk: Scales with group size
+ double cpu, memory, disk;
+
+ int groupSize = nodes / groups;
+
+ if (current.clusterType().isContent()) { // load scales with node share of content
+ // The fixed cost portion of cpu does not scale with changes to the node count
+ // TODO: Only for the portion of cpu consumed by queries
+ double cpuPerGroup = fixedCpuCostFraction * target.nodeCpu() +
+ (1 - fixedCpuCostFraction) * target.nodeCpu() * current.groupSize() / groupSize;
+ cpu = cpuPerGroup * current.groups() / groups;
+ memory = target.nodeMemory() * current.groupSize() / groupSize;
+ disk = target.nodeDisk() * current.groupSize() / groupSize;
}
-
- public ClusterResources next() {
- ClusterResources next = resourcesWith(currentNodes);
- currentNodes += nodeIncrement;
- return next;
- }
-
- public boolean hasNext() {
- return currentNodes <= maxNodes();
- }
-
- private int minNodes() {
- if (limits.isEmpty()) return minimumNodes;
- if (singleGroupMode) return limits.min().nodes();
- return Math.max(limits.min().nodes(), limits.min().groups() * current.groupSize() );
- }
-
- private int maxNodes() {
- if (limits.isEmpty()) return maximumNodes;
- if (singleGroupMode) return limits.max().nodes();
- return Math.min(limits.max().nodes(), limits.max().groups() * current.groupSize() );
- }
-
- private ClusterResources resourcesWith(int nodes) {
- int nodesAdjustedForRedundancy = nodes;
- if (target.adjustForRedundancy())
- nodesAdjustedForRedundancy = nodes - (singleGroupMode ? 1 : current.groupSize());
- return new ClusterResources(nodes,
- singleGroupMode ? 1 : nodes / current.groupSize(),
- nodeResourcesWith(nodesAdjustedForRedundancy));
- }
-
- /**
- * For the observed load this instance is initialized with, returns the resources needed per node to be at
- * ideal load given a target node count
- */
- private NodeResources nodeResourcesWith(int nodeCount) {
- // Cpu: Scales with cluster size (TODO: Only reads, writes scales with group size)
- // Memory and disk: Scales with group size
-
- double cpu, memory, disk;
- if (singleGroupMode) {
- // The fixed cost portion of cpu does not scale with changes to the node count
- // TODO: Only for the portion of cpu consumed by queries
- cpu = fixedCpuCostFraction * target.clusterCpu() / current.groupSize() +
- (1 - fixedCpuCostFraction) * target.clusterCpu() / nodeCount;
-
- if (current.clusterType().isContent()) { // load scales with node share of content
- memory = target.groupMemory() / nodeCount;
- disk = target.groupDisk() / nodeCount;
- }
- else {
- memory = target.nodeMemory();
- disk = target.nodeDisk();
- }
- }
- else {
- cpu = target.clusterCpu() / nodeCount;
- if (current.clusterType().isContent()) { // load scales with node share of content
- memory = target.groupMemory() / current.groupSize();
- disk = target.groupDisk() / current.groupSize();
- }
- else {
- memory = target.nodeMemory();
- disk = target.nodeDisk();
- }
- }
-
- // Combine the scaled resource values computed here
- // with the currently configured non-scaled values, given in the limits, if any
- NodeResources nonScaled = limits.isEmpty() || limits.min().nodeResources().isUnspecified()
- ? current.toAdvertisedClusterResources().nodeResources()
- : limits.min().nodeResources(); // min=max for non-scaled
- return nonScaled.withVcpu(cpu).withMemoryGb(memory).withDiskGb(disk);
+ else {
+ cpu = target.nodeCpu() * current.nodes() / nodes;
+ memory = target.nodeMemory();
+ disk = target.nodeDisk();
}
+ // Combine the scaled resource values computed here
+ // with the currently configured non-scaled values, given in the limits, if any
+ NodeResources nonScaled = limits.isEmpty() || limits.min().nodeResources().isUnspecified()
+ ? current.toAdvertisedClusterResources().nodeResources()
+ : limits.min().nodeResources(); // min=max for non-scaled
+ return nonScaled.withVcpu(cpu).withMemoryGb(memory).withDiskGb(disk);
}
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java
index 7ca60c4c86d..15bf4427346 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java
@@ -6,6 +6,8 @@ import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.vespa.hosted.provision.applications.Cluster;
+import java.util.Objects;
+
/**
* Optional allocation limits
*
@@ -60,6 +62,10 @@ public class Limits {
return new Limits(capacity.minResources(), capacity.maxResources());
}
+ public static Limits of(ClusterResources min, ClusterResources max) {
+ return new Limits(Objects.requireNonNull(min, "min"), Objects.requireNonNull(max, "max"));
+ }
+
@Override
public String toString() {
if (isEmpty()) return "no limits";
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ResourceTarget.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ResourceTarget.java
index 287cd2ae86a..9359d8251f7 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ResourceTarget.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ResourceTarget.java
@@ -1,11 +1,11 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.autoscale;
-import com.yahoo.config.provision.NodeResources;
-
/**
* A resource target to hit for the allocation optimizer.
* The target is measured in cpu, memory and disk per node in the allocation given by current.
+ *
+ * @author bratseth
*/
public class ResourceTarget {
@@ -14,31 +14,16 @@ public class ResourceTarget {
/** The target resources per node, assuming the node assignment in current */
private final double cpu, memory, disk;
- /** The current allocation leading to this target */
- private final AllocatableClusterResources current;
-
- private ResourceTarget(double cpu, double memory, double disk,
- boolean adjustForRedundancy,
- AllocatableClusterResources current) {
+ private ResourceTarget(double cpu, double memory, double disk, boolean adjustForRedundancy) {
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
this.adjustForRedundancy = adjustForRedundancy;
- this.current = current;
}
/** Are the target resources given by this including redundancy or not */
public boolean adjustForRedundancy() { return adjustForRedundancy; }
- /** Returns the target total cpu to allocate to the entire cluster */
- public double clusterCpu() { return nodeCpu() * current.nodes(); }
-
- /** Returns the target total memory to allocate to each group */
- public double groupMemory() { return nodeMemory() * current.groupSize(); }
-
- /** Returns the target total disk to allocate to each group */
- public double groupDisk() { return nodeDisk() * current.groupSize(); }
-
/** Returns the target cpu per node, in terms of the current allocation */
public double nodeCpu() { return cpu; }
@@ -58,8 +43,7 @@ public class ResourceTarget {
return new ResourceTarget(nodeUsage(Resource.cpu, currentCpuLoad, current) / Resource.cpu.idealAverageLoad(),
nodeUsage(Resource.memory, currentMemoryLoad, current) / Resource.memory.idealAverageLoad(),
nodeUsage(Resource.disk, currentDiskLoad, current) / Resource.disk.idealAverageLoad(),
- true,
- current);
+ true);
}
/** Crete a target of preserving a current allocation */
@@ -67,8 +51,7 @@ public class ResourceTarget {
return new ResourceTarget(current.realResources().vcpu(),
current.realResources().memoryGb(),
current.realResources().diskGb(),
- false,
- current);
+ false);
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java
index 5b7d67a86a5..781ef1f6114 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java
@@ -112,7 +112,7 @@ public class AutoscalingTest {
tester.addMeasurements(Resource.cpu, 0.25f, 1f, 120, application1);
ClusterResources scaledResources = tester.assertResources("Scaling up since cpu usage is too high",
- 7, 1, 2.6, 80.0, 80.0,
+ 7, 1, 2.5, 80.0, 80.0,
tester.autoscale(application1, cluster1.id(), min, max));
tester.deploy(application1, cluster1, scaledResources);
@@ -120,7 +120,7 @@ public class AutoscalingTest {
tester.addMeasurements(Resource.cpu, 0.1f, 1f, 120, application1);
tester.assertResources("Scaling down since cpu usage has gone down",
- 4, 1, 2.4, 68.6, 68.6,
+ 4, 1, 2.5, 68.6, 68.6,
tester.autoscale(application1, cluster1.id(), min, max));
}
@@ -213,7 +213,7 @@ public class AutoscalingTest {
tester.deploy(application1, cluster1, 5, 1, resources);
tester.addMeasurements(Resource.cpu, 0.25f, 1f, 120, application1);
tester.assertResources("Scaling up since resource usage is too high",
- 7, 1, 2.6, 80.0, 80.0,
+ 7, 1, 2.5, 80.0, 80.0,
tester.suggest(application1, cluster1.id(), min, max));
}
@@ -236,7 +236,7 @@ public class AutoscalingTest {
}
@Test
- public void testAutoscalingGroupSize3() {
+ public void testAutoscalingGroupSizeByCpu() {
NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources( 3, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(21, 7, new NodeResources(100, 1000, 1000, 1));
@@ -247,9 +247,28 @@ public class AutoscalingTest {
// deploy
tester.deploy(application1, cluster1, 6, 2, resources);
- tester.addMeasurements(Resource.cpu, 0.22f, 1f, 120, application1);
- tester.assertResources("Scaling up since resource usage is too high",
- 9, 3, 2.7, 83.3, 83.3,
+ tester.addMeasurements(Resource.cpu, 0.25f, 1f, 120, application1);
+ System.out.println("Autoscaling ... ");
+ tester.assertResources("Scaling up since resource usage is too high, changing to 1 group is cheaper",
+ 8, 1, 2.7, 83.3, 83.3,
+ tester.autoscale(application1, cluster1.id(), min, max));
+ }
+
+ @Test
+ public void testAutoscalingGroupSize() {
+ NodeResources hostResources = new NodeResources(100, 1000, 1000, 100);
+ ClusterResources min = new ClusterResources( 3, 2, new NodeResources(1, 1, 1, 1));
+ ClusterResources max = new ClusterResources(30, 30, new NodeResources(100, 100, 1000, 1));
+ AutoscalingTester tester = new AutoscalingTester(hostResources);
+
+ ApplicationId application1 = tester.applicationId("application1");
+ ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
+
+ // deploy
+ tester.deploy(application1, cluster1, 6, 2, new NodeResources(10, 100, 100, 1));
+ tester.addMeasurements(Resource.memory, 1.0f, 1f, 1000, application1);
+ tester.assertResources("Increase group size to reduce memory load",
+ 8, 2, 12.9, 89.3, 62.5,
tester.autoscale(application1, cluster1.id(), min, max));
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java
index 4dab064ce1e..c34db3210c1 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java
@@ -41,10 +41,10 @@ public class AutoscalingMaintainerTest {
ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east3"))).flavorsConfig(flavorsConfig()).build();
ApplicationId app1 = tester.makeApplicationId("app1");
- ClusterSpec cluster1 = tester.clusterSpec();
+ ClusterSpec cluster1 = tester.containerClusterSpec();
ApplicationId app2 = tester.makeApplicationId("app2");
- ClusterSpec cluster2 = tester.clusterSpec();
+ ClusterSpec cluster2 = tester.containerClusterSpec();
NodeResources lowResources = new NodeResources(4, 4, 10, 0.1);
NodeResources highResources = new NodeResources(6.5, 9, 20, 0.1);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java
index 59da88790d7..b7f21eb3114 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java
@@ -19,12 +19,10 @@ import com.yahoo.vespa.hosted.provision.autoscale.NodeMetricsDb;
import com.yahoo.vespa.hosted.provision.autoscale.Resource;
import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder;
import com.yahoo.vespa.hosted.provision.provisioning.ProvisioningTester;
-import com.yahoo.vespa.hosted.provision.testutils.MockDeployer;
import org.junit.Test;
import java.time.Duration;
import java.util.List;
-import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -42,10 +40,10 @@ public class ScalingSuggestionsMaintainerTest {
ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east3"))).flavorsConfig(flavorsConfig()).build();
ApplicationId app1 = tester.makeApplicationId("app1");
- ClusterSpec cluster1 = tester.clusterSpec();
+ ClusterSpec cluster1 = tester.containerClusterSpec();
ApplicationId app2 = tester.makeApplicationId("app2");
- ClusterSpec cluster2 = tester.clusterSpec();
+ ClusterSpec cluster2 = tester.contentClusterSpec();
NodeMetricsDb nodeMetricsDb = new NodeMetricsDb();
@@ -71,9 +69,9 @@ public class ScalingSuggestionsMaintainerTest {
Duration.ofMinutes(1));
maintainer.maintain();
- assertEquals("7 nodes with [vcpu: 15.3, memory: 5.1 Gb, disk 15.0 Gb, bandwidth: 0.1 Gbps, storage type: remote]",
+ assertEquals("14 nodes with [vcpu: 6.9, memory: 5.1 Gb, disk 15.0 Gb, bandwidth: 0.1 Gbps, storage type: remote]",
tester.nodeRepository().applications().get(app1).get().cluster(cluster1.id()).get().suggestedResources().get().toString());
- assertEquals("7 nodes with [vcpu: 16.8, memory: 5.7 Gb, disk 16.5 Gb, bandwidth: 0.1 Gbps, storage type: remote]",
+ assertEquals("8 nodes with [vcpu: 14.7, memory: 4.0 Gb, disk 11.8 Gb, bandwidth: 0.1 Gbps, storage type: remote]",
tester.nodeRepository().applications().get(app2).get().cluster(cluster2.id()).get().suggestedResources().get().toString());
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
index 510196551ab..e1a66d2ae21 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
@@ -251,28 +251,29 @@ public class DynamicDockerProvisionTest {
tester.activate(app1, cluster1, Capacity.from(resources(4, 2, 2, 10, 20),
resources(6, 3, 3, 25, 25)));
tester.assertNodes("New allocation at new max",
- 6, 3, 2, 20, 25,
+ 6, 2, 2, 20, 25,
app1, cluster1);
- // Widening window lets us find a cheaper alternative
+ // Widening window does not change allocation
tester.activate(app1, cluster1, Capacity.from(resources(2, 1, 1, 5, 15),
resources(8, 4, 4, 20, 30)));
- tester.assertNodes("Cheaper allocation",
- 8, 4, 1, 10, 25,
+ tester.assertNodes("No change",
+ 6, 2, 2, 20, 25,
app1, cluster1);
- // Changing group size
+ // Force 1 more groups: Reducing to 2 nodes per group to preserve node count is rejected
+ // since it will reduce total group memory from 60 to 40.
tester.activate(app1, cluster1, Capacity.from(resources(6, 3, 0.5, 5, 10),
resources(9, 3, 5, 20, 15)));
- tester.assertNodes("Groups changed",
- 6, 3, 1, 10, 15,
+ tester.assertNodes("Group size is preserved",
+ 9, 3, 2, 20, 15,
app1, cluster1);
// Stop specifying node resources
tester.activate(app1, cluster1, Capacity.from(new ClusterResources(6, 3, NodeResources.unspecified),
new ClusterResources(9, 3, NodeResources.unspecified)));
- tester.assertNodes("Minimal allocation",
- 6, 3, 1, 10, 15,
+ tester.assertNodes("Existing allocation is preserved",
+ 9, 3, 2, 20, 15,
app1, cluster1);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
index bafb52170dc..03c07515cd5 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
@@ -431,7 +431,7 @@ public class ProvisioningTest {
ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east")))
.flavors(List.of(hostFlavor))
.build();
- tester.makeReadyHosts(30, hostFlavor.resources()).deployZoneApp();
+ tester.makeReadyHosts(31, hostFlavor.resources()).deployZoneApp();
ApplicationId app1 = tester.makeApplicationId("app1");
ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build();
@@ -444,45 +444,45 @@ public class ProvisioningTest {
app1, cluster1);
// Move window above current allocation
- tester.activate(app1, cluster1, Capacity.from(resources(8, 4, 4, 20, 40),
+ tester.activate(app1, cluster1, Capacity.from(resources(8, 4, 4, 21, 40),
resources(10, 5, 5, 25, 50)));
tester.assertNodes("New allocation at new min",
- 8, 4, 4, 20, 40,
+ 8, 4, 4, 21, 40,
app1, cluster1);
// Move window below current allocation
tester.activate(app1, cluster1, Capacity.from(resources(4, 2, 2, 10, 20),
resources(6, 3, 3, 15, 25)));
- tester.assertNodes("New allocation at new max",
- 6, 3, 3, 15, 25,
+ tester.assertNodes("Allocation preserving resources within new limits",
+ 6, 2, 3, 8.0/4*21 / (6.0/2), 25,
app1, cluster1);
// Widening window does not change allocation
- tester.activate(app1, cluster1, Capacity.from(resources(2, 1, 1, 5, 15),
- resources(8, 4, 4, 20, 30)));
+ tester.activate(app1, cluster1, Capacity.from(resources(4, 2, 1, 5, 15),
+ resources(8, 4, 4, 21, 30)));
tester.assertNodes("Same allocation",
- 6, 3, 3, 15, 25,
+ 6, 2, 3, 8.0/4*21 / (6.0/2), 25,
app1, cluster1);
// Changing limits in opposite directions cause a mixture of min and max
tester.activate(app1, cluster1, Capacity.from(resources(2, 1, 10, 30, 10),
resources(4, 2, 14, 40, 13)));
tester.assertNodes("A mix of min and max",
- 4, 2, 10, 30, 13,
+ 4, 1, 10, 30, 13,
app1, cluster1);
// Changing group size
tester.activate(app1, cluster1, Capacity.from(resources(6, 3, 8, 25, 10),
resources(9, 3, 12, 35, 15)));
tester.assertNodes("Groups changed",
- 6, 3, 8, 30, 13,
+ 9, 3, 8, 35, 15,
app1, cluster1);
// Stop specifying node resources
tester.activate(app1, cluster1, Capacity.from(new ClusterResources(6, 3, NodeResources.unspecified),
new ClusterResources(9, 3, NodeResources.unspecified)));
tester.assertNodes("No change",
- 6, 3, 8, 30, 13,
+ 9, 3, 8, 35, 15,
app1, cluster1);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
index 227ebf8e692..c9d7e6bfa70 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
@@ -496,12 +496,16 @@ public class ProvisioningTester {
activate(applicationId, Set.copyOf(list));
}
- public ClusterSpec clusterSpec() {
+ public ClusterSpec containerClusterSpec() {
return ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test")).vespaVersion("6.42").build();
}
+ public ClusterSpec contentClusterSpec() {
+ return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test")).vespaVersion("6.42").build();
+ }
+
public List<Node> deploy(ApplicationId application, Capacity capacity) {
- return deploy(application, clusterSpec(), capacity);
+ return deploy(application, containerClusterSpec(), capacity);
}
public List<Node> deploy(ApplicationId application, ClusterSpec cluster, Capacity capacity) {