aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@gmail.com>2021-12-01 08:49:52 +0100
committerJon Bratseth <bratseth@gmail.com>2021-12-01 08:49:52 +0100
commite4833e7380f6a18adda7eaaa5a6ad017f46c6761 (patch)
tree01fe4395d1ad29ddb276cdfcf83b709d7f64d82c /node-repository
parentff8465b785fb92c7f9b69771a06106b38bba9181 (diff)
Revert "Merge pull request #20302 from vespa-engine/revert-20298-bratseth/autoscaling-in-dev"
This reverts commit aa3ffac0caaba2c60fabc585be8376cfe132c17d, reversing changes made to 318edd10866b25b06419ed6069227eafbe63d685.
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java9
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java33
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java26
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java5
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java10
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java157
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java18
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java14
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java8
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java4
15 files changed, 209 insertions, 102 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java
index 3c2ab8cead2..df5044de05c 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java
@@ -2,10 +2,9 @@
package com.yahoo.vespa.hosted.provision.applications;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ClusterResources;
+import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterSpec;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Optional;
@@ -59,12 +58,12 @@ public class Application {
* Returns an application with the given cluster having the min and max resource limits of the given cluster.
* If the cluster has a target which is not inside the new limits, the target is removed.
*/
- public Application withCluster(ClusterSpec.Id id, boolean exclusive, ClusterResources min, ClusterResources max) {
+ public Application withCluster(ClusterSpec.Id id, boolean exclusive, Capacity requested) {
Cluster cluster = clusters.get(id);
if (cluster == null)
- cluster = new Cluster(id, exclusive, min, max, Optional.empty(), Optional.empty(), List.of(), AutoscalingStatus.empty());
+ cluster = Cluster.create(id, exclusive, requested);
else
- cluster = cluster.withConfiguration(exclusive, min, max);
+ cluster = cluster.withConfiguration(exclusive, requested);
return with(cluster);
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java
index 5478999e4fe..e0ccbe10b10 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java
@@ -1,6 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.applications;
+import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.vespa.hosted.provision.autoscale.Autoscaler;
@@ -25,6 +26,7 @@ public class Cluster {
private final ClusterSpec.Id id;
private final boolean exclusive;
private final ClusterResources min, max;
+ private boolean required;
private final Optional<Suggestion> suggested;
private final Optional<ClusterResources> target;
@@ -36,6 +38,7 @@ public class Cluster {
boolean exclusive,
ClusterResources minResources,
ClusterResources maxResources,
+ boolean required,
Optional<Suggestion> suggestedResources,
Optional<ClusterResources> targetResources,
List<ScalingEvent> scalingEvents,
@@ -44,6 +47,7 @@ public class Cluster {
this.exclusive = exclusive;
this.min = Objects.requireNonNull(minResources);
this.max = Objects.requireNonNull(maxResources);
+ this.required = required;
this.suggested = Objects.requireNonNull(suggestedResources);
Objects.requireNonNull(targetResources);
if (targetResources.isPresent() && ! targetResources.get().isWithin(minResources, maxResources))
@@ -56,14 +60,20 @@ public class Cluster {
public ClusterSpec.Id id() { return id; }
+ /** Returns whether the nodes allocated to this cluster must be on host exclusively dedicated to this application */
+ public boolean exclusive() { return exclusive; }
+
/** Returns the configured minimal resources in this cluster */
public ClusterResources minResources() { return min; }
/** Returns the configured maximal resources in this cluster */
public ClusterResources maxResources() { return max; }
- /** Returns whether the nodes allocated to this cluster must be on host exclusively dedicated to this application */
- public boolean exclusive() { return exclusive; }
+ /**
+ * Returns whether the resources of this cluster are required to be within the specified min and max.
+ * Otherwise they may be adjusted by capacity policies.
+ */
+ public boolean required() { return required; }
/**
* Returns the computed resources (between min and max, inclusive) this cluster should
@@ -97,16 +107,18 @@ public class Cluster {
/** The latest autoscaling status of this cluster, or unknown (never null) if none */
public AutoscalingStatus autoscalingStatus() { return autoscalingStatus; }
- public Cluster withConfiguration(boolean exclusive, ClusterResources min, ClusterResources max) {
- return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus);
+ public Cluster withConfiguration(boolean exclusive, Capacity capacity) {
+ return new Cluster(id, exclusive,
+ capacity.minResources(), capacity.maxResources(), capacity.isRequired(),
+ suggested, target, scalingEvents, autoscalingStatus);
}
public Cluster withSuggested(Optional<Suggestion> suggested) {
- return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus);
+ return new Cluster(id, exclusive, min, max, required, suggested, target, scalingEvents, autoscalingStatus);
}
public Cluster withTarget(Optional<ClusterResources> target) {
- return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus);
+ return new Cluster(id, exclusive, min, max, required, suggested, target, scalingEvents, autoscalingStatus);
}
/** Add or update (based on "at" time) a scaling event */
@@ -120,12 +132,12 @@ public class Cluster {
scalingEvents.add(scalingEvent);
prune(scalingEvents);
- return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus);
+ return new Cluster(id, exclusive, min, max, required, suggested, target, scalingEvents, autoscalingStatus);
}
public Cluster with(AutoscalingStatus autoscalingStatus) {
if (autoscalingStatus.equals(this.autoscalingStatus)) return this;
- return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus);
+ return new Cluster(id, exclusive, min, max, required, suggested, target, scalingEvents, autoscalingStatus);
}
@Override
@@ -156,6 +168,11 @@ public class Cluster {
return -1;
}
+ public static Cluster create(ClusterSpec.Id id, boolean exclusive, Capacity requested) {
+ return new Cluster(id, exclusive, requested.minResources(), requested.maxResources(), requested.isRequired(),
+ Optional.empty(), Optional.empty(), List.of(), AutoscalingStatus.empty());
+ }
+
public static class Suggestion {
private final ClusterResources resources;
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 f1e707be7b4..078b0621a99 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
@@ -8,6 +8,7 @@ import com.yahoo.config.provision.NodeResources;
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.provisioning.CapacityPolicies;
import com.yahoo.vespa.hosted.provision.provisioning.NodeResourceLimits;
import java.util.List;
@@ -54,14 +55,14 @@ public class AllocatableClusterResources {
public AllocatableClusterResources(ClusterResources realResources,
NodeResources advertisedResources,
- NodeResources idealResources,
+ ClusterResources idealResources,
ClusterSpec clusterSpec) {
this.nodes = realResources.nodes();
this.groups = realResources.groups();
this.realResources = realResources.nodeResources();
this.advertisedResources = advertisedResources;
this.clusterSpec = clusterSpec;
- this.fulfilment = fulfilment(realResources.nodeResources(), idealResources);
+ this.fulfilment = fulfilment(realResources, idealResources);
}
/**
@@ -99,10 +100,10 @@ public class AllocatableClusterResources {
*/
public double fulfilment() { return fulfilment; }
- private static double fulfilment(NodeResources realResources, NodeResources idealResources) {
- double vcpuFulfilment = Math.min(1, realResources.vcpu() / idealResources.vcpu());
- double memoryGbFulfilment = Math.min(1, realResources.memoryGb() / idealResources.memoryGb());
- double diskGbFulfilment = Math.min(1, realResources.diskGb() / idealResources.diskGb());
+ private static double fulfilment(ClusterResources realResources, ClusterResources idealResources) {
+ double vcpuFulfilment = Math.min(1, realResources.totalResources().vcpu() / idealResources.totalResources().vcpu());
+ double memoryGbFulfilment = Math.min(1, realResources.totalResources().memoryGb() / idealResources.totalResources().memoryGb());
+ double diskGbFulfilment = Math.min(1, realResources.totalResources().diskGb() / idealResources.totalResources().diskGb());
return (vcpuFulfilment + memoryGbFulfilment + diskGbFulfilment) / 3;
}
@@ -138,21 +139,25 @@ public class AllocatableClusterResources {
public static Optional<AllocatableClusterResources> from(ClusterResources wantedResources,
ClusterSpec clusterSpec,
Limits applicationLimits,
+ boolean required,
NodeList hosts,
NodeRepository nodeRepository) {
+ var capacityPolicies = new CapacityPolicies(nodeRepository);
var systemLimits = new NodeResourceLimits(nodeRepository);
boolean exclusive = clusterSpec.isExclusive();
+ int actualNodes = capacityPolicies.decideSize(wantedResources.nodes(), required, true, false, clusterSpec);
if ( !clusterSpec.isExclusive() && !nodeRepository.zone().getCloud().dynamicProvisioning()) {
// We decide resources: Add overhead to what we'll request (advertised) to make sure real becomes (at least) cappedNodeResources
var advertisedResources = nodeRepository.resourcesCalculator().realToRequest(wantedResources.nodeResources(), exclusive);
advertisedResources = systemLimits.enlargeToLegal(advertisedResources, clusterSpec.type(), exclusive); // Ask for something legal
advertisedResources = applicationLimits.cap(advertisedResources); // Overrides other conditions, even if it will then fail
+ advertisedResources = capacityPolicies.decideNodeResources(advertisedResources, required, clusterSpec); // Adjust to what we can request
var realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, exclusive); // What we'll really get
if ( ! systemLimits.isWithinRealLimits(realResources, clusterSpec.type())) return Optional.empty();
if (matchesAny(hosts, advertisedResources))
- return Optional.of(new AllocatableClusterResources(wantedResources.with(realResources),
+ return Optional.of(new AllocatableClusterResources(wantedResources.withNodes(actualNodes).with(realResources),
advertisedResources,
- wantedResources.nodeResources(),
+ wantedResources,
clusterSpec));
else
return Optional.empty();
@@ -163,6 +168,7 @@ public class AllocatableClusterResources {
for (Flavor flavor : nodeRepository.flavors().getFlavors()) {
// Flavor decide resources: Real resources are the worst case real resources we'll get if we ask for these advertised resources
NodeResources advertisedResources = nodeRepository.resourcesCalculator().advertisedResourcesOf(flavor);
+ advertisedResources = capacityPolicies.decideNodeResources(advertisedResources, required, clusterSpec); // Adjust to what we can get
NodeResources realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, exclusive);
// Adjust where we don't need exact match to the flavor
@@ -178,9 +184,9 @@ public class AllocatableClusterResources {
if ( ! between(applicationLimits.min().nodeResources(), applicationLimits.max().nodeResources(), advertisedResources)) continue;
if ( ! systemLimits.isWithinRealLimits(realResources, clusterSpec.type())) continue;
- var candidate = new AllocatableClusterResources(wantedResources.with(realResources),
+ var candidate = new AllocatableClusterResources(wantedResources.withNodes(actualNodes).with(realResources),
advertisedResources,
- wantedResources.nodeResources(),
+ wantedResources,
clusterSpec);
if (best.isEmpty() || candidate.preferableTo(best.get()))
best = Optional.of(candidate);
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 6fd9801164a..b8a80a9bd2b 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
@@ -65,7 +65,9 @@ public class AllocationOptimizer {
nodeResourcesWith(nodesAdjustedForRedundancy,
groupsAdjustedForRedundancy,
limits, target, current, clusterModel));
- var allocatableResources = AllocatableClusterResources.from(next, current.clusterSpec(), limits, hosts, nodeRepository);
+ var allocatableResources = AllocatableClusterResources.from(next, current.clusterSpec(), limits,
+ clusterModel.cluster().required(),
+ hosts, nodeRepository);
if (allocatableResources.isEmpty()) continue;
if (bestAllocation.isEmpty() || allocatableResources.get().preferableTo(bestAllocation.get()))
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java
index 35aafd3e0f4..1001ab83cc0 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java
@@ -32,6 +32,7 @@ public class ClusterModel {
static final double idealDiskLoad = 0.6;
private final Application application;
+ private final Cluster cluster;
/** The current nodes of this cluster, or empty if this models a new cluster not yet deployed */
private final NodeList nodes;
private final Clock clock;
@@ -50,6 +51,7 @@ public class ClusterModel {
MetricsDb metricsDb,
Clock clock) {
this.application = application;
+ this.cluster = cluster;
this.nodes = clusterNodes;
this.clock = clock;
this.scalingDuration = computeScalingDuration(cluster, clusterSpec);
@@ -65,6 +67,7 @@ public class ClusterModel {
ClusterTimeseries clusterTimeseries,
ClusterNodesTimeseries nodeTimeseries) {
this.application = application;
+ this.cluster = cluster;
this.nodes = null;
this.clock = clock;
@@ -73,6 +76,8 @@ public class ClusterModel {
this.nodeTimeseries = nodeTimeseries;
}
+ public Cluster cluster() { return cluster; }
+
/** Returns the predicted duration of a rescaling of this cluster */
public Duration scalingDuration() { return scalingDuration; }
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java
index 9cdb4c69b97..2289ba4a0ea 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java
@@ -48,6 +48,7 @@ public class ApplicationSerializer {
private static final String exclusiveKey = "exclusive";
private static final String minResourcesKey = "min";
private static final String maxResourcesKey = "max";
+ private static final String requiredKey = "required";
private static final String suggestedKey = "suggested";
private static final String resourcesKey = "resources";
private static final String targetResourcesKey = "target";
@@ -99,7 +100,6 @@ public class ApplicationSerializer {
}
private static Status statusFromSlime(Inspector statusObject) {
- if ( ! statusObject.valid()) return Status.initial(); // TODO: Remove this line after March 2021
return new Status(statusObject.field(currentReadShareKey).asDouble(),
statusObject.field(maxReadShareKey).asDouble());
}
@@ -118,6 +118,7 @@ public class ApplicationSerializer {
clusterObject.setBool(exclusiveKey, cluster.exclusive());
toSlime(cluster.minResources(), clusterObject.setObject(minResourcesKey));
toSlime(cluster.maxResources(), clusterObject.setObject(maxResourcesKey));
+ clusterObject.setBool(requiredKey, cluster.required());
cluster.suggestedResources().ifPresent(suggested -> toSlime(suggested, clusterObject.setObject(suggestedKey)));
cluster.targetResources().ifPresent(target -> toSlime(target, clusterObject.setObject(targetResourcesKey)));
scalingEventsToSlime(cluster.scalingEvents(), clusterObject.setArray(scalingEventsKey));
@@ -130,6 +131,7 @@ public class ApplicationSerializer {
clusterObject.field(exclusiveKey).asBool(),
clusterResourcesFromSlime(clusterObject.field(minResourcesKey)),
clusterResourcesFromSlime(clusterObject.field(maxResourcesKey)),
+ clusterObject.field(requiredKey).asBool(),
optionalSuggestionFromSlime(clusterObject.field(suggestedKey)),
optionalClusterResourcesFromSlime(clusterObject.field(targetResourcesKey)),
scalingEventsFromSlime(clusterObject.field(scalingEventsKey)),
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
index 839bc21827c..0c2c3c48df1 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
@@ -29,11 +29,11 @@ public class CapacityPolicies {
this.sharedHosts = type -> PermanentFlags.SHARED_HOST.bindTo(nodeRepository.flagSource()).value().isEnabled(type.name());
}
- public int decideSize(int requested, Capacity capacity, ClusterSpec cluster, ApplicationId application) {
- if (application.instance().isTester()) return 1;
+ public int decideSize(int requested, boolean required, boolean canFail, boolean isTester, ClusterSpec cluster) {
+ if (isTester) return 1;
- ensureRedundancy(requested, cluster, capacity.canFail());
- if (capacity.isRequired()) return requested;
+ ensureRedundancy(requested, cluster, canFail);
+ if (required) return requested;
switch(zone.environment()) {
case dev : case test : return 1;
case perf : return Math.min(requested, 3);
@@ -43,11 +43,11 @@ public class CapacityPolicies {
}
}
- public NodeResources decideNodeResources(NodeResources target, Capacity capacity, ClusterSpec cluster) {
+ public NodeResources decideNodeResources(NodeResources target, boolean required, ClusterSpec cluster) {
if (target.isUnspecified())
target = defaultNodeResources(cluster.type());
- if (capacity.isRequired()) return target;
+ if (required) return target;
// Dev does not cap the cpu or network of containers since usage is spotty: Allocate just a small amount exclusively
if (zone.environment() == Environment.dev && !zone.getCloud().dynamicProvisioning())
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
index 0ab04a1a73d..b35b0a5e301 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
@@ -97,9 +97,13 @@ public class NodeRepositoryProvisioner implements Provisioner {
NodeSpec nodeSpec;
if (requested.type() == NodeType.tenant) {
ClusterResources target = decideTargetResources(application, cluster, requested);
- int nodeCount = capacityPolicies.decideSize(target.nodes(), requested, cluster, application);
+ int nodeCount = capacityPolicies.decideSize(target.nodes(),
+ requested.isRequired(),
+ requested.canFail(),
+ application.instance().isTester(),
+ cluster);
groups = Math.min(target.groups(), nodeCount); // cannot have more groups than nodes
- resources = capacityPolicies.decideNodeResources(target.nodeResources(), requested, cluster);
+ resources = capacityPolicies.decideNodeResources(target.nodeResources(), requested.isRequired(), cluster);
boolean exclusive = capacityPolicies.decideExclusivity(requested, cluster.isExclusive());
nodeSpec = NodeSpec.from(nodeCount, resources, exclusive, requested.canFail());
logIfDownscaled(target.nodes(), nodeCount, cluster, logger);
@@ -141,7 +145,7 @@ public class NodeRepositoryProvisioner implements Provisioner {
private ClusterResources decideTargetResources(ApplicationId applicationId, ClusterSpec clusterSpec, Capacity requested) {
try (Mutex lock = nodeRepository.nodes().lock(applicationId)) {
var application = nodeRepository.applications().get(applicationId).orElse(Application.empty(applicationId))
- .withCluster(clusterSpec.id(), clusterSpec.isExclusive(), requested.minResources(), requested.maxResources());
+ .withCluster(clusterSpec.id(), clusterSpec.isExclusive(), requested);
nodeRepository.applications().put(application, lock);
var cluster = application.cluster(clusterSpec.id()).get();
return cluster.targetResources().orElseGet(() -> currentResources(application, clusterSpec, cluster, requested));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java
index 4a0c2012ae4..667dffef2a6 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.provision.autoscale;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostSpec;
@@ -55,7 +56,7 @@ public class AutoscalingIntegrationTest {
ClusterResources max = new ClusterResources(2, 1, nodes);
Application application = tester.nodeRepository().applications().get(application1).orElse(Application.empty(application1))
- .withCluster(cluster1.id(), false, min, max);
+ .withCluster(cluster1.id(), false, Capacity.from(min, max));
try (Mutex lock = tester.nodeRepository().nodes().lock(application1)) {
tester.nodeRepository().applications().put(application, lock);
}
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 a3c7b7d2d2b..601a7109533 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
@@ -41,6 +41,7 @@ public class AutoscalingTest {
new NodeResources(1, 1, 1, 1, NodeResources.DiskSpeed.any));
ClusterResources max = new ClusterResources(20, 1,
new NodeResources(100, 1000, 1000, 1, NodeResources.DiskSpeed.any));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(hostResources);
ApplicationId application1 = tester.applicationId("application1");
@@ -50,10 +51,10 @@ public class AutoscalingTest {
tester.deploy(application1, cluster1, 5, 1, hostResources);
tester.clock().advance(Duration.ofDays(1));
- assertTrue("No measurements -> No change", tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
+ assertTrue("No measurements -> No change", tester.autoscale(application1, cluster1.id(), capacity).isEmpty());
tester.addCpuMeasurements(0.25f, 1f, 59, application1);
- assertTrue("Too few measurements -> No change", tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
+ assertTrue("Too few measurements -> No change", tester.autoscale(application1, cluster1.id(), capacity).isEmpty());
tester.clock().advance(Duration.ofDays(1));
tester.addCpuMeasurements(0.25f, 1f, 120, application1);
@@ -61,10 +62,10 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
ClusterResources scaledResources = tester.assertResources("Scaling up since resource usage is too high",
15, 1, 1.2, 28.6, 28.6,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.deploy(application1, cluster1, scaledResources);
- assertTrue("Cluster in flux -> No further change", tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
+ assertTrue("Cluster in flux -> No further change", tester.autoscale(application1, cluster1.id(), capacity).isEmpty());
tester.deactivateRetired(application1, cluster1, scaledResources);
@@ -73,19 +74,19 @@ public class AutoscalingTest {
tester.clock().advance(Duration.ofMinutes(-10 * 5));
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
assertTrue("Load change is large, but insufficient measurements for new config -> No change",
- tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
+ tester.autoscale(application1, cluster1.id(), capacity).isEmpty());
tester.addCpuMeasurements(0.19f, 1f, 100, application1);
tester.clock().advance(Duration.ofMinutes(-10 * 5));
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
- assertEquals("Load change is small -> No change", Optional.empty(), tester.autoscale(application1, cluster1.id(), min, max).target());
+ assertEquals("Load change is small -> No change", Optional.empty(), tester.autoscale(application1, cluster1.id(), capacity).target());
tester.addCpuMeasurements(0.1f, 1f, 120, application1);
tester.clock().advance(Duration.ofMinutes(-10 * 5));
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling down to minimum since usage has gone down significantly",
7, 1, 1.0, 66.7, 66.7,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
var events = tester.nodeRepository().applications().get(application1).get().cluster(cluster1.id()).get().scalingEvents();
}
@@ -96,6 +97,7 @@ public class AutoscalingTest {
NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(20, 1, new NodeResources(100, 1000, 1000, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -108,7 +110,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
ClusterResources scaledResources = tester.assertResources("Scaling up since cpu usage is too high",
7, 1, 2.5, 80.0, 80.0,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.deploy(application1, cluster1, scaledResources);
tester.deactivateRetired(application1, cluster1, scaledResources);
@@ -118,7 +120,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling down since cpu usage has gone down",
4, 1, 2.5, 68.6, 68.6,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -142,9 +144,10 @@ public class AutoscalingTest {
new NodeResources(1, 1, 1, 1, NodeResources.DiskSpeed.any));
ClusterResources max = new ClusterResources(20, 1,
new NodeResources(100, 1000, 1000, 1, NodeResources.DiskSpeed.any));
+ var capacity = Capacity.from(min, max);
ClusterResources scaledResources = tester.assertResources("Scaling up since resource usage is too high",
14, 1, 1.4, 30.8, 30.8,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
assertEquals("Disk speed from min/max is used",
NodeResources.DiskSpeed.any, scaledResources.nodeResources().diskSpeed());
tester.deploy(application1, cluster1, scaledResources);
@@ -164,6 +167,7 @@ public class AutoscalingTest {
NodeResources resources = new NodeResources(1, 10, 10, 1);
var min = new ClusterResources( 2, 1, resources.with(NodeResources.DiskSpeed.any));
var max = new ClusterResources( 10, 1, resources.with(NodeResources.DiskSpeed.any));
+ var capacity = Capacity.from(min, max);
tester.deploy(application1, cluster1, Capacity.from(min, max));
// Redeployment without target: Uses current resource numbers with *requested* non-numbers (i.e disk-speed any)
@@ -176,7 +180,7 @@ public class AutoscalingTest {
// Autoscaling: Uses disk-speed any as well
tester.clock().advance(Duration.ofDays(2));
tester.addCpuMeasurements(0.8f, 1f, 120, application1);
- Autoscaler.Advice advice = tester.autoscale(application1, cluster1.id(), min, max);
+ Autoscaler.Advice advice = tester.autoscale(application1, cluster1.id(), capacity);
assertEquals(NodeResources.DiskSpeed.any, advice.target().get().nodeResources().diskSpeed());
@@ -187,6 +191,7 @@ public class AutoscalingTest {
NodeResources hostResources = new NodeResources(6, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources( 6, 1, new NodeResources(2.4, 78, 79, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(hostResources);
ApplicationId application1 = tester.applicationId("application1");
@@ -200,7 +205,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling up to limit since resource usage is too high",
6, 1, 2.4, 78.0, 79.0,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -208,6 +213,7 @@ public class AutoscalingTest {
NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources( 4, 1, new NodeResources(1.8, 7.4, 8.5, 1));
ClusterResources max = new ClusterResources( 6, 1, new NodeResources(2.4, 78, 79, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -218,7 +224,7 @@ public class AutoscalingTest {
tester.addMeasurements(0.05f, 0.05f, 0.05f, 0, 120, application1);
tester.assertResources("Scaling down to limit since resource usage is low",
4, 1, 1.8, 7.7, 10.0,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -226,6 +232,7 @@ public class AutoscalingTest {
NodeResources hostResources = new NodeResources(6, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 1, NodeResources.unspecified());
ClusterResources max = new ClusterResources( 6, 1, NodeResources.unspecified());
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(hostResources);
ApplicationId application1 = tester.applicationId("application1");
@@ -244,7 +251,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling up to limit since resource usage is too high",
4, 1, defaultResources,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -252,6 +259,7 @@ public class AutoscalingTest {
NodeResources hostResources = new NodeResources(30.0, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 2, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(18, 6, new NodeResources(100, 1000, 1000, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(hostResources);
ApplicationId application1 = tester.applicationId("application1");
@@ -264,7 +272,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling up since resource usage is too high",
6, 6, 3.6, 8.0, 10.0,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -272,6 +280,7 @@ public class AutoscalingTest {
NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = min;
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources);
ApplicationId application1 = tester.applicationId("application1");
@@ -281,13 +290,13 @@ public class AutoscalingTest {
tester.deploy(application1, cluster1, 5, 1, resources);
tester.clock().advance(Duration.ofDays(1));
tester.addCpuMeasurements(0.25f, 1f, 120, application1);
- assertTrue(tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
+ assertTrue(tester.autoscale(application1, cluster1.id(), capacity).isEmpty());
}
@Test
public void prefers_remote_disk_when_no_local_match() {
- NodeResources resources = new NodeResources(3, 100, 100, 1);
- ClusterResources min = new ClusterResources( 2, 1, new NodeResources(3, 100, 50, 1));
+ NodeResources resources = new NodeResources(3, 100, 50, 1);
+ ClusterResources min = new ClusterResources( 2, 1, resources);
ClusterResources max = min;
// AutoscalingTester hardcodes 3Gb memory overhead:
Flavor localFlavor = new Flavor("local", new NodeResources(3, 97, 75, 1, DiskSpeed.fast, StorageType.local));
@@ -341,6 +350,7 @@ public class AutoscalingTest {
NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources(2, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(5, 1, new NodeResources(100, 1000, 1000, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -350,7 +360,7 @@ public class AutoscalingTest {
tester.deploy(application1, cluster1, 2, 1, resources);
tester.addMeasurements(0.5f, 0.6f, 0.7f, 1, false, true, 120, application1);
assertTrue("Not scaling up since nodes were measured while cluster was unstable",
- tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
+ tester.autoscale(application1, cluster1.id(), capacity).isEmpty());
}
@Test
@@ -358,6 +368,7 @@ public class AutoscalingTest {
NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources(2, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(5, 1, new NodeResources(100, 1000, 1000, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -367,7 +378,7 @@ public class AutoscalingTest {
tester.deploy(application1, cluster1, 2, 1, resources);
tester.addMeasurements(0.5f, 0.6f, 0.7f, 1, true, false, 120, application1);
assertTrue("Not scaling up since nodes were measured while cluster was unstable",
- tester.autoscale(application1, cluster1.id(), min, max).isEmpty());
+ tester.autoscale(application1, cluster1.id(), capacity).isEmpty());
}
@Test
@@ -375,6 +386,7 @@ public class AutoscalingTest {
NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 2, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(20, 20, new NodeResources(100, 1000, 1000, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -387,7 +399,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling up since resource usage is too high",
7, 7, 2.5, 80.0, 80.0,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -395,6 +407,7 @@ public class AutoscalingTest {
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));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -409,7 +422,7 @@ public class AutoscalingTest {
t -> 1.0);
tester.assertResources("Scaling up since resource usage is too high, changing to 1 group is cheaper",
8, 1, 2.6, 83.3, 83.3,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
/** Same as above but mostly write traffic, which favors smaller groups */
@@ -418,6 +431,7 @@ public class AutoscalingTest {
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));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -432,14 +446,15 @@ public class AutoscalingTest {
t -> 100.0);
tester.assertResources("Scaling down since resource usage is too high, changing to 1 group is cheaper",
4, 1, 2.1, 83.3, 83.3,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
public void test_autoscaling_group_size() {
NodeResources hostResources = new NodeResources(100, 1000, 1000, 100);
- ClusterResources min = new ClusterResources( 3, 2, new NodeResources(1, 1, 1, 1));
+ ClusterResources min = new ClusterResources( 2, 2, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(30, 30, new NodeResources(100, 100, 1000, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(hostResources);
ApplicationId application1 = tester.applicationId("application1");
@@ -453,7 +468,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Increase group size to reduce memory load",
8, 2, 12.4, 96.2, 62.5,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -461,6 +476,7 @@ public class AutoscalingTest {
NodeResources hostResources = new NodeResources(6, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(20, 1, new NodeResources(100, 1000, 1000, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(hostResources);
ApplicationId application1 = tester.applicationId("application1");
@@ -473,7 +489,7 @@ public class AutoscalingTest {
tester.addMemMeasurements(0.02f, 0.95f, 120, application1);
tester.assertResources("Scaling down",
6, 1, 2.9, 4.0, 95.0,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -481,6 +497,7 @@ public class AutoscalingTest {
NodeResources hostResources = new NodeResources(6, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(20, 1, new NodeResources(100, 1000, 1000, 1));
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(hostResources);
ApplicationId application1 = tester.applicationId("application1");
@@ -492,7 +509,7 @@ public class AutoscalingTest {
tester.addMemMeasurements(0.02f, 0.95f, 120, application1);
tester.clock().advance(Duration.ofMinutes(-10 * 5));
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
- assertTrue(tester.autoscale(application1, cluster1.id(), min, max).target().isEmpty());
+ assertTrue(tester.autoscale(application1, cluster1.id(), capacity).target().isEmpty());
// Trying the same later causes autoscaling
tester.clock().advance(Duration.ofDays(2));
@@ -501,7 +518,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling down",
6, 1, 1.4, 4.0, 95.0,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -509,9 +526,11 @@ public class AutoscalingTest {
NodeResources hostResources = new NodeResources(60, 100, 1000, 10);
ClusterResources min = new ClusterResources(2, 1, new NodeResources( 2, 20, 200, 1));
ClusterResources max = new ClusterResources(4, 1, new NodeResources(60, 100, 1000, 1));
+ var capacity = Capacity.from(min, max);
{ // No memory tax
- AutoscalingTester tester = new AutoscalingTester(hostResources, new OnlySubtractingWhenForecastingCalculator(0));
+ AutoscalingTester tester = new AutoscalingTester(Environment.prod, hostResources,
+ new OnlySubtractingWhenForecastingCalculator(0));
ApplicationId application1 = tester.applicationId("app1");
ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
@@ -522,11 +541,12 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling up",
4, 1, 6.7, 20.5, 200,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
{ // 15 Gb memory tax
- AutoscalingTester tester = new AutoscalingTester(hostResources, new OnlySubtractingWhenForecastingCalculator(15));
+ AutoscalingTester tester = new AutoscalingTester(Environment.prod, hostResources,
+ new OnlySubtractingWhenForecastingCalculator(15));
ApplicationId application1 = tester.applicationId("app1");
ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
@@ -537,7 +557,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling up",
4, 1, 6.7, 35.5, 200,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
}
@@ -545,6 +565,7 @@ public class AutoscalingTest {
public void test_autoscaling_with_dynamic_provisioning() {
ClusterResources min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
ClusterResources max = new ClusterResources(20, 1, new NodeResources(100, 1000, 1000, 1));
+ var capacity = Capacity.from(min, max);
List<Flavor> flavors = new ArrayList<>();
flavors.add(new Flavor("aws-xlarge", new NodeResources(3, 200, 100, 1, NodeResources.DiskSpeed.fast, NodeResources.StorageType.remote)));
flavors.add(new Flavor("aws-large", new NodeResources(3, 150, 100, 1, NodeResources.DiskSpeed.fast, NodeResources.StorageType.remote)));
@@ -567,7 +588,7 @@ public class AutoscalingTest {
tester.addMemMeasurements(0.9f, 0.6f, 120, application1);
ClusterResources scaledResources = tester.assertResources("Scaling up since resource usage is too high.",
8, 1, 3, 83, 34.3,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.deploy(application1, cluster1, scaledResources);
tester.deactivateRetired(application1, cluster1, scaledResources);
@@ -578,7 +599,7 @@ public class AutoscalingTest {
tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
tester.assertResources("Scaling down since resource usage has gone down",
5, 1, 3, 83, 36.0,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -586,6 +607,7 @@ public class AutoscalingTest {
NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources( 1, 1, resources);
ClusterResources max = new ClusterResources(10, 1, resources);
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -599,17 +621,17 @@ public class AutoscalingTest {
// (no read share stored)
tester.assertResources("Advice to scale up since we set aside for bcp by default",
7, 1, 3, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.storeReadShare(0.25, 0.5, application1);
tester.assertResources("Half of global share is the same as the default assumption used above",
7, 1, 3, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.storeReadShare(0.5, 0.5, application1);
tester.assertResources("Advice to scale down since we don't need room for bcp",
4, 1, 3, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@@ -620,6 +642,7 @@ public class AutoscalingTest {
NodeResources maxResources = new NodeResources(10, 100, 100, 1);
ClusterResources min = new ClusterResources(5, 1, minResources);
ClusterResources max = new ClusterResources(5, 1, maxResources);
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(maxResources.withVcpu(maxResources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -633,7 +656,7 @@ public class AutoscalingTest {
// (no query rate data)
tester.assertResources("Scale up since we assume we need 2x cpu for growth when no data scaling time data",
5, 1, 6.3, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.setScalingDuration(application1, cluster1.id(), Duration.ofMinutes(5));
tester.addQueryRateMeasurements(application1, cluster1.id(),
@@ -643,7 +666,7 @@ public class AutoscalingTest {
tester.addCpuMeasurements(0.25f, 1f, 100, application1);
tester.assertResources("Scale down since observed growth is slower than scaling time",
5, 1, 3.4, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.clearQueryRateMeasurements(application1, cluster1.id());
@@ -655,7 +678,7 @@ public class AutoscalingTest {
tester.addCpuMeasurements(0.25f, 1f, 100, application1);
tester.assertResources("Scale up since observed growth is faster than scaling time",
5, 1, 10.0, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -665,6 +688,7 @@ public class AutoscalingTest {
NodeResources maxResources = new NodeResources(10, 100, 100, 1);
ClusterResources min = new ClusterResources(5, 1, minResources);
ClusterResources max = new ClusterResources(5, 1, maxResources);
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(maxResources.withVcpu(maxResources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
@@ -681,35 +705,35 @@ public class AutoscalingTest {
tester.addLoadMeasurements(application1, cluster1.id(), 100, t -> t == 0 ? 20.0 : 10.0, t -> 10.0);
tester.assertResources("Query and write load is equal -> scale up somewhat",
5, 1, 7.3, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.addCpuMeasurements(0.4f, 1f, 100, application1);
tester.clock().advance(Duration.ofMinutes(-100 * 5));
tester.addLoadMeasurements(application1, cluster1.id(), 100, t -> t == 0 ? 80.0 : 40.0, t -> 10.0);
tester.assertResources("Query load is 4x write load -> scale up more",
5, 1, 9.5, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.addCpuMeasurements(0.3f, 1f, 100, application1);
tester.clock().advance(Duration.ofMinutes(-100 * 5));
tester.addLoadMeasurements(application1, cluster1.id(), 100, t -> t == 0 ? 20.0 : 10.0, t -> 100.0);
tester.assertResources("Write load is 10x query load -> scale down",
5, 1, 2.9, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.addCpuMeasurements(0.4f, 1f, 100, application1);
tester.clock().advance(Duration.ofMinutes(-100 * 5));
tester.addLoadMeasurements(application1, cluster1.id(), 100, t -> t == 0 ? 20.0 : 10.0, t-> 0.0);
tester.assertResources("Query only -> largest possible",
5, 1, 10.0, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
tester.addCpuMeasurements(0.4f, 1f, 100, application1);
tester.clock().advance(Duration.ofMinutes(-100 * 5));
tester.addLoadMeasurements(application1, cluster1.id(), 100, t -> 0.0, t -> 10.0);
tester.assertResources("Write only -> smallest possible",
5, 1, 2.1, 100, 100,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
@Test
@@ -717,6 +741,7 @@ public class AutoscalingTest {
NodeResources resources = new NodeResources(1, 4, 50, 1);
ClusterResources min = new ClusterResources( 2, 1, resources);
ClusterResources max = new ClusterResources(3, 1, resources);
+ var capacity = Capacity.from(min, max);
AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
ApplicationId application1 = tester.applicationId("application1");
ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
@@ -728,7 +753,47 @@ public class AutoscalingTest {
tester.assertResources("Advice to scale up since observed growth is much faster than scaling time",
3, 1, 1, 4, 50,
- tester.autoscale(application1, cluster1.id(), min, max).target());
+ tester.autoscale(application1, cluster1.id(), capacity).target());
+ }
+
+ @Test
+ public void test_autoscaling_in_dev() {
+ NodeResources resources = new NodeResources(1, 4, 50, 1);
+ ClusterResources min = new ClusterResources( 1, 1, resources);
+ ClusterResources max = new ClusterResources(3, 1, resources);
+ Capacity capacity = Capacity.from(min, max, false, true);
+
+ AutoscalingTester tester = new AutoscalingTester(Environment.dev, resources.withVcpu(resources.vcpu() * 2));
+ ApplicationId application1 = tester.applicationId("application1");
+ ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
+
+ tester.deploy(application1, cluster1, capacity);
+ tester.addQueryRateMeasurements(application1, cluster1.id(),
+ 500, t -> 100.0);
+ tester.addCpuMeasurements(1.0f, 1f, 10, application1);
+ assertTrue("Not attempting to scale up because policies dictate we'll only get one node",
+ tester.autoscale(application1, cluster1.id(), capacity).target().isEmpty());
+ }
+
+ /** Same setup as test_autoscaling_in_dev(), just with required = true */
+ @Test
+ public void test_autoscaling_in_dev_with_required_resources() {
+ NodeResources resources = new NodeResources(1, 4, 50, 1);
+ ClusterResources min = new ClusterResources( 1, 1, resources);
+ ClusterResources max = new ClusterResources(3, 1, resources);
+ Capacity capacity = Capacity.from(min, max, true, true);
+
+ AutoscalingTester tester = new AutoscalingTester(Environment.dev, resources.withVcpu(resources.vcpu() * 2));
+ ApplicationId application1 = tester.applicationId("application1");
+ ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
+
+ tester.deploy(application1, cluster1, capacity);
+ tester.addQueryRateMeasurements(application1, cluster1.id(),
+ 500, t -> 100.0);
+ tester.addCpuMeasurements(1.0f, 1f, 10, application1);
+ tester.assertResources("We scale up even in dev because resources are required",
+ 3, 1, 1.0, 4, 50,
+ tester.autoscale(application1, cluster1.id(), capacity).target());
}
/**
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java
index c45b6caf14c..8d59181a027 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java
@@ -48,11 +48,15 @@ class AutoscalingTester {
/** Creates an autoscaling tester with a single host type ready */
public AutoscalingTester(NodeResources hostResources) {
- this(hostResources, null);
+ this(Environment.prod, hostResources);
}
- public AutoscalingTester(NodeResources hostResources, HostResourcesCalculator resourcesCalculator) {
- this(new Zone(Environment.prod, RegionName.from("us-east")), List.of(new Flavor("hostFlavor", hostResources)), resourcesCalculator);
+ public AutoscalingTester(Environment environment, NodeResources hostResources) {
+ this(environment, hostResources, null);
+ }
+
+ public AutoscalingTester(Environment environment, NodeResources hostResources, HostResourcesCalculator resourcesCalculator) {
+ this(new Zone(environment, RegionName.from("us-east")), List.of(new Flavor("hostFlavor", hostResources)), resourcesCalculator);
provisioningTester.makeReadyNodes(20, "hostFlavor", NodeType.host, 8);
provisioningTester.activateTenantHosts();
}
@@ -251,6 +255,7 @@ class AutoscalingTester {
cluster.exclusive(),
cluster.minResources(),
cluster.maxResources(),
+ cluster.required(),
cluster.suggestedResources(),
cluster.targetResources(),
List.of(), // Remove scaling events
@@ -295,10 +300,9 @@ class AutoscalingTester {
((MemoryMetricsDb)nodeMetricsDb()).clearClusterMetrics(application, cluster);
}
- public Autoscaler.Advice autoscale(ApplicationId applicationId, ClusterSpec.Id clusterId,
- ClusterResources min, ClusterResources max) {
+ public Autoscaler.Advice autoscale(ApplicationId applicationId, ClusterSpec.Id clusterId, Capacity capacity) {
Application application = nodeRepository().applications().get(applicationId).orElse(Application.empty(applicationId))
- .withCluster(clusterId, false, min, max);
+ .withCluster(clusterId, false, capacity);
try (Mutex lock = nodeRepository().nodes().lock(applicationId)) {
nodeRepository().applications().put(application, lock);
}
@@ -309,7 +313,7 @@ class AutoscalingTester {
public Autoscaler.Advice suggest(ApplicationId applicationId, ClusterSpec.Id clusterId,
ClusterResources min, ClusterResources max) {
Application application = nodeRepository().applications().get(applicationId).orElse(Application.empty(applicationId))
- .withCluster(clusterId, false, min, max);
+ .withCluster(clusterId, false, Capacity.from(min, max));
try (Mutex lock = nodeRepository().nodes().lock(applicationId)) {
nodeRepository().applications().put(application, lock);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java
index 0e37d953d2d..bd7300ad6bf 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java
@@ -2,12 +2,12 @@
package com.yahoo.vespa.hosted.provision.autoscale;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.provision.applications.Application;
-import com.yahoo.vespa.hosted.provision.applications.AutoscalingStatus;
import com.yahoo.vespa.hosted.provision.applications.Cluster;
import com.yahoo.vespa.hosted.provision.applications.Status;
import org.junit.Test;
@@ -15,7 +15,6 @@ import org.junit.Test;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.function.IntFunction;
import static org.junit.Assert.assertEquals;
@@ -73,14 +72,9 @@ public class ClusterModelTest {
}
private Cluster cluster(NodeResources resources) {
- return new Cluster(ClusterSpec.Id.from("test"),
- false,
- new ClusterResources(5, 1, resources),
- new ClusterResources(5, 1, resources),
- Optional.empty(),
- Optional.empty(),
- List.of(),
- AutoscalingStatus.empty());
+ return Cluster.create(ClusterSpec.Id.from("test"),
+ false,
+ Capacity.from(new ClusterResources(5, 1, resources)));
}
/** Creates the given number of measurements, spaced 5 minutes between, using the given function */
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java
index 7266da9ff46..e34f63d8062 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.provision.persistence;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeResources;
@@ -33,6 +34,7 @@ public class ApplicationSerializerTest {
false,
new ClusterResources( 8, 4, new NodeResources(1, 2, 3, 4)),
new ClusterResources(12, 6, new NodeResources(3, 6, 21, 24)),
+ true,
Optional.empty(),
Optional.empty(),
List.of(),
@@ -42,6 +44,7 @@ public class ApplicationSerializerTest {
true,
new ClusterResources( 8, 4, minResources),
new ClusterResources(14, 7, new NodeResources(3, 6, 21, 24)),
+ false,
Optional.of(new Cluster.Suggestion(new ClusterResources(20, 10,
new NodeResources(0.5, 4, 14, 16)),
Instant.ofEpochMilli(1234L))),
@@ -72,6 +75,7 @@ public class ApplicationSerializerTest {
assertEquals(originalCluster.exclusive(), serializedCluster.exclusive());
assertEquals(originalCluster.minResources(), serializedCluster.minResources());
assertEquals(originalCluster.maxResources(), serializedCluster.maxResources());
+ assertEquals(originalCluster.required(), serializedCluster.required());
assertEquals(originalCluster.suggestedResources(), serializedCluster.suggestedResources());
assertEquals(originalCluster.targetResources(), serializedCluster.targetResources());
assertEquals(originalCluster.scalingEvents(), serializedCluster.scalingEvents());
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 20546cc5bd9..db165aae919 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
@@ -1015,10 +1015,10 @@ public class ProvisioningTest {
allHosts.addAll(content1);
Function<Integer, Capacity> capacity = count -> Capacity.from(new ClusterResources(count, 1, NodeResources.unspecified()), required, true);
- int expectedContainer0Size = tester.capacityPolicies().decideSize(container0Size, capacity.apply(container0Size), containerCluster0, application);
- int expectedContainer1Size = tester.capacityPolicies().decideSize(container1Size, capacity.apply(container1Size), containerCluster1, application);
- int expectedContent0Size = tester.capacityPolicies().decideSize(content0Size, capacity.apply(content0Size), contentCluster0, application);
- int expectedContent1Size = tester.capacityPolicies().decideSize(content1Size, capacity.apply(content1Size), contentCluster1, application);
+ int expectedContainer0Size = tester.decideSize(container0Size, capacity.apply(container0Size), containerCluster0, application);
+ int expectedContainer1Size = tester.decideSize(container1Size, capacity.apply(container1Size), containerCluster1, application);
+ int expectedContent0Size = tester.decideSize(content0Size, capacity.apply(content0Size), contentCluster0, application);
+ int expectedContent1Size = tester.decideSize(content1Size, capacity.apply(content1Size), contentCluster1, application);
assertEquals("Hosts in each group cluster is disjunct and the total number of unretired nodes is correct",
expectedContainer0Size + expectedContainer1Size + expectedContent0Size + expectedContent1Size,
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 6d525762ecc..6ca93671087 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
@@ -152,6 +152,10 @@ public class ProvisioningTester {
public NodeList getNodes(ApplicationId id, Node.State ... inState) { return nodeRepository.nodes().list(inState).owner(id); }
public InMemoryFlagSource flagSource() { return (InMemoryFlagSource) nodeRepository.flagSource(); }
+ public int decideSize(int size, Capacity capacity, ClusterSpec cluster, ApplicationId application) {
+ return capacityPolicies.decideSize(size, capacity.isRequired(), capacity.canFail(), application.instance().isTester(), cluster);
+ }
+
public Node patchNode(Node node, UnaryOperator<Node> patcher) {
return patchNodes(List.of(node), patcher).get(0);
}