diff options
author | Jon Bratseth <bratseth@oath.com> | 2020-05-18 18:53:53 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-18 18:53:53 +0200 |
commit | c73c579251721a5f46ad376b99dd9c94cd1bed94 (patch) | |
tree | d29cf5da51916df74744998ae008762f9c32455a /node-repository | |
parent | 37e567a295a6f1dec583a407225d70bfb690dc70 (diff) | |
parent | b9905b7315e434216cb2115b98f7cdae82194cdf (diff) |
Merge pull request #13287 from vespa-engine/bratseth/autoscale-groupsize
Bratseth/autoscale groupsize
Diffstat (limited to 'node-repository')
10 files changed, 135 insertions, 189 deletions
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) { |