summaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorValerij Fredriksen <freva@users.noreply.github.com>2022-11-01 12:50:30 +0100
committerGitHub <noreply@github.com>2022-11-01 12:50:30 +0100
commit60d6cab0299751e22a7d8f5be8c5cc89f996389f (patch)
treece9cb41db899856874648d75e5dd92c95ae3fe50 /node-repository
parent8b832bdcdf756c5bef9024776df052f079a40643 (diff)
parentaba1d62aeb907003677cbe03133cb808b85a32ff (diff)
Merge pull request #24683 from vespa-engine/bratseth/no-traffic-test
Test with no traffic
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationPatcher.java14
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingIntegrationTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java38
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java15
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Fixture.java32
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Loader.java9
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsHostResourcesCalculatorImpl.java87
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsNodeTypes.java122
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsResourcesCalculator.java71
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/VespaFlavor.java37
12 files changed, 405 insertions, 30 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 552f67ba6d3..4e7504cd5af 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
@@ -202,12 +202,14 @@ 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),
advertisedResources,
wantedResources,
clusterSpec);
- if (best.isEmpty() || candidate.preferableTo(best.get()))
+ if (best.isEmpty() || candidate.preferableTo(best.get())) {
best = Optional.of(candidate);
+ }
}
return best;
}
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 e6f2a1216f3..3025124b174 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
@@ -214,7 +214,7 @@ public class ClusterModel {
return nodes > 1 ? (groups == 1 ? 1 : groups - 1) : groups;
}
- /** Ideal cpu load must take the application traffic fraction into account */
+ /** Ideal cpu load must take the application traffic fraction into account. */
private double idealCpuLoad() {
double queryCpuFraction = queryCpuFraction();
@@ -238,7 +238,7 @@ public class ClusterModel {
// Assumptions: 1) Write load is not organic so we should not grow to handle more.
// (TODO: But allow applications to set their target write rate and size for that)
// 2) Write load does not change in BCP scenarios.
- return queryCpuFraction * 1 / growthRateHeadroom * 1 / trafficShiftHeadroom * idealQueryCpuLoad +
+ return queryCpuFraction * 1/growthRateHeadroom * 1/trafficShiftHeadroom * idealQueryCpuLoad +
(1 - queryCpuFraction) * idealWriteCpuLoad;
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationPatcher.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationPatcher.java
index eee1d364c03..871f30570f5 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationPatcher.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationPatcher.java
@@ -68,14 +68,12 @@ public class ApplicationPatcher implements AutoCloseable {
}
private Application applyField(Application application, String name, Inspector value) {
- switch (name) {
- case "currentReadShare" :
- return application.with(application.status().withCurrentReadShare(asDouble(value)));
- case "maxReadShare" :
- return application.with(application.status().withMaxReadShare(asDouble(value)));
- default :
- throw new IllegalArgumentException("Could not apply field '" + name + "' on an application: No such modifiable field");
- }
+ return switch (name) {
+ case "currentReadShare" -> application.with(application.status().withCurrentReadShare(asDouble(value)));
+ case "maxReadShare" -> application.with(application.status().withMaxReadShare(asDouble(value)));
+ default -> throw new IllegalArgumentException("Could not apply field '" + name +
+ "' on an application: No such modifiable field");
+ };
}
private Double asDouble(Inspector field) {
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 d8becdf7c80..bff4a4b21de 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
@@ -23,7 +23,7 @@ public class AutoscalingIntegrationTest {
@Test
public void testComponentIntegration() {
var fixture = AutoscalingTester.fixture()
- .hostResources(new NodeResources(3, 20, 200, 1))
+ .hostFlavors(new NodeResources(3, 20, 200, 1))
.initialResources(Optional.of(new ClusterResources(2, 1,
new NodeResources(1, 10, 100, 1))))
.build();
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 e4389e84255..5f372a01b1c 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
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.provision.autoscale;
import com.yahoo.config.provision.Capacity;
+import com.yahoo.config.provision.Cloud;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
@@ -14,9 +15,11 @@ import com.yahoo.config.provision.NodeResources.StorageType;
import static com.yahoo.config.provision.NodeResources.StorageType.remote;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.RegionName;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.Nodelike;
+import com.yahoo.vespa.hosted.provision.autoscale.awsnodes.AwsHostResourcesCalculatorImpl;
import com.yahoo.vespa.hosted.provision.provisioning.CapacityPolicies;
import com.yahoo.vespa.hosted.provision.provisioning.HostResourcesCalculator;
import org.junit.Test;
@@ -108,6 +111,27 @@ public class AutoscalingTest {
fixture.autoscale());
}
+ @Test
+ public void test_autoscaling_without_traffic() {
+ var min = new ClusterResources(1, 1, new NodeResources(2, 4, 10, 0.3));
+ var now = new ClusterResources(4, 1, new NodeResources(2, 16, 10, 0.3));
+ var max = new ClusterResources(4, 1, new NodeResources(3, 16, 50, 0.3));
+ var fixture = AutoscalingTester.fixture(min, now, max)
+ .clusterType(ClusterSpec.Type.container)
+ .awsProdSetup()
+ .build();
+ var duration = fixture.loader().addMeasurements(new Load(0.04, 0.39, 0.01), 20);
+ fixture.tester().clock().advance(duration.negated());
+ fixture.loader().zeroTraffic(20);
+
+ //System.out.println("Average " + fixture.clusterModel().averageLoad());
+ //System.out.println("Ideal " + fixture.clusterModel().idealLoad());
+ //System.out.println("Adjustment to " + fixture.clusterModel().loadAdjustment());
+ fixture.tester().assertResources("Scaled down",
+ 2, 1, 2, 16, 10,
+ fixture.autoscale());
+ }
+
/** We prefer fewer nodes for container clusters as (we assume) they all use the same disk and memory */
@Test
public void test_autoscaling_single_container_group() {
@@ -129,7 +153,7 @@ public class AutoscalingTest {
public void autoscaling_handles_disk_setting_changes() {
var resources = new NodeResources(3, 100, 100, 1, slow);
var fixture = AutoscalingTester.fixture()
- .hostResources(resources)
+ .hostFlavors(resources)
.initialResources(Optional.of(new ClusterResources(5, 1, resources)))
.capacity(Capacity.from(new ClusterResources(5, 1, resources)))
.build();
@@ -266,7 +290,7 @@ public class AutoscalingTest {
var fixture = AutoscalingTester.fixture()
.dynamicProvisioning(true)
.clusterType(ClusterSpec.Type.container)
- .hostResources(local, remote)
+ .hostFlavors(local, remote)
.capacity(Capacity.from(resources))
.initialResources(Optional.of(new ClusterResources(3, 1, resources.nodeResources())))
.build();
@@ -289,7 +313,7 @@ public class AutoscalingTest {
var fixture = AutoscalingTester.fixture()
.dynamicProvisioning(true)
.clusterType(ClusterSpec.Type.content)
- .hostResources(local, remote)
+ .hostFlavors(local, remote)
.capacity(Capacity.from(resources))
.initialResources(Optional.of(new ClusterResources(3, 1, resources.nodeResources())))
.build();
@@ -459,10 +483,10 @@ public class AutoscalingTest {
ClusterResources max = new ClusterResources(20, 1, new NodeResources(100, 1000, 1000, 1));
var fixture = AutoscalingTester.fixture()
.dynamicProvisioning(true)
- .hostResources(new NodeResources(3, 200, 100, 1, fast, remote),
- new NodeResources(3, 150, 100, 1, fast, remote),
- new NodeResources(3, 100, 100, 1, fast, remote),
- new NodeResources(3, 80, 100, 1, fast, remote))
+ .hostFlavors(new NodeResources(3, 200, 100, 1, fast, remote),
+ new NodeResources(3, 150, 100, 1, fast, remote),
+ new NodeResources(3, 100, 100, 1, fast, remote),
+ new NodeResources(3, 80, 100, 1, fast, remote))
.capacity(Capacity.from(min, max))
.initialResources(Optional.of(new ClusterResources(5, 1,
new NodeResources(3, 100, 100, 1))))
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 e75ef10e689..393b25a78da 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
@@ -27,6 +27,7 @@ import com.yahoo.vespa.hosted.provision.provisioning.ProvisioningTester;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import static org.junit.Assert.assertEquals;
@@ -42,13 +43,9 @@ class AutoscalingTester {
private final HostResourcesCalculator hostResourcesCalculator;
private final CapacityPolicies capacityPolicies;
- public AutoscalingTester(Zone zone, HostResourcesCalculator resourcesCalculator, List<NodeResources> hostResources) {
- this(zone, hostResources, resourcesCalculator, 20);
- }
-
- private AutoscalingTester(Zone zone, List<NodeResources> hostResources, HostResourcesCalculator resourcesCalculator, int hostCount) {
- this(zone, toFlavors(hostResources), resourcesCalculator);
- for (Flavor flavor : toFlavors(hostResources))
+ public AutoscalingTester(Zone zone, HostResourcesCalculator resourcesCalculator, List<Flavor> hostFlavors, int hostCount) {
+ this(zone, hostFlavors, resourcesCalculator);
+ for (Flavor flavor : hostFlavors)
provisioningTester.makeReadyNodes(hostCount, flavor.name(), NodeType.host, 8);
provisioningTester.activateTenantHosts();
}
@@ -74,6 +71,10 @@ class AutoscalingTester {
public static Fixture.Builder fixture() { return new Fixture.Builder(); }
+ public static Fixture.Builder fixture(ClusterResources min, ClusterResources now, ClusterResources max) {
+ return new Fixture.Builder().initialResources(Optional.of(now)).capacity(Capacity.from(min, max));
+ }
+
public ProvisioningTester provisioning() { return provisioningTester; }
public static ApplicationId applicationId(String applicationName) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Fixture.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Fixture.java
index 35881c59c5e..d3aa5a849a1 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Fixture.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Fixture.java
@@ -8,13 +8,17 @@ import com.yahoo.config.provision.Cloud;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.RegionName;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.applications.Application;
import com.yahoo.vespa.hosted.provision.applications.Cluster;
+import com.yahoo.vespa.hosted.provision.autoscale.awsnodes.AwsHostResourcesCalculatorImpl;
+import com.yahoo.vespa.hosted.provision.autoscale.awsnodes.AwsNodeTypes;
import com.yahoo.vespa.hosted.provision.provisioning.HostResourcesCalculator;
import java.time.Duration;
@@ -40,7 +44,7 @@ public class Fixture {
applicationId = builder.application;
clusterSpec = builder.cluster;
capacity = builder.capacity;
- tester = new AutoscalingTester(builder.zone, builder.resourceCalculator, builder.hostResources);
+ tester = new AutoscalingTester(builder.zone, builder.resourceCalculator, builder.hostFlavors, 20);
var deployCapacity = initialResources.isPresent() ? Capacity.from(initialResources.get()) : capacity;
tester.deploy(builder.application, builder.cluster, deployCapacity);
this.loader = new Loader(this);
@@ -121,7 +125,7 @@ public class Fixture {
ApplicationId application = AutoscalingTester.applicationId("application1");
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("cluster1")).vespaVersion("7").build();
Zone zone = new Zone(Environment.prod, RegionName.from("us-east"));
- List<NodeResources> hostResources = List.of(new NodeResources(100, 100, 100, 1));
+ List<Flavor> hostFlavors = List.of(new Flavor(new NodeResources(100, 100, 100, 1)));
Optional<ClusterResources> initialResources = Optional.of(new ClusterResources(5, 1, new NodeResources(3, 10, 100, 1)));
Capacity capacity = Capacity.from(new ClusterResources(2, 1,
new NodeResources(1, 1, 1, 1, NodeResources.DiskSpeed.any)),
@@ -153,13 +157,28 @@ public class Fixture {
return this;
}
+ public Fixture.Builder awsProdSetup() {
+ return this.awsHostFlavors()
+ .awsResourceCalculator()
+ .zone(new Zone(Cloud.builder().dynamicProvisioning(true).build(),
+ SystemName.Public,
+ Environment.prod,
+ RegionName.from("aws-eu-west-1a")));
+ }
+
public Fixture.Builder vespaVersion(Version version) {
cluster = ClusterSpec.request(cluster.type(), cluster.id()).vespaVersion(version).build();
return this;
}
- public Fixture.Builder hostResources(NodeResources ... hostResources) {
- this.hostResources = Arrays.stream(hostResources).collect(Collectors.toList());
+ public Fixture.Builder hostFlavors(NodeResources ... hostResources) {
+ this.hostFlavors = Arrays.stream(hostResources).map(r -> new Flavor(r)).collect(Collectors.toList());
+ return this;
+ }
+
+ /** Adds the host resources available on AWS. */
+ public Fixture.Builder awsHostFlavors() {
+ this.hostFlavors = AwsNodeTypes.asFlavors();
return this;
}
@@ -178,6 +197,11 @@ public class Fixture {
return this;
}
+ public Fixture.Builder awsResourceCalculator() {
+ this.resourceCalculator = new AwsHostResourcesCalculatorImpl();
+ return this;
+ }
+
public Fixture build() {
return new Fixture(this, initialResources);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Loader.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Loader.java
index 7aaaceb0fdd..39745b726a0 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Loader.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Loader.java
@@ -25,6 +25,15 @@ public class Loader {
this.fixture = fixture;
}
+ /** Assign measured zero traffic in the same way as the system will. */
+ public Duration zeroTraffic(int measurements) {
+ try (var lock = fixture.tester().nodeRepository().applications().lock(fixture.applicationId())) {
+ var statusWithZeroLoad = fixture.application().status().withCurrentReadShare(0).withMaxReadShare(1);
+ fixture.tester().nodeRepository().applications().put(fixture.application().with(statusWithZeroLoad), lock);
+ }
+ return addQueryRateMeasurements(measurements, (n) -> 0.0);
+ }
+
/**
* Adds measurements with the given resource value and ideal values for the other resources,
* scaled to take one node redundancy into account.
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsHostResourcesCalculatorImpl.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsHostResourcesCalculatorImpl.java
new file mode 100644
index 00000000000..deb6f41c383
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsHostResourcesCalculatorImpl.java
@@ -0,0 +1,87 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.autoscale.awsnodes;
+
+import com.yahoo.config.provision.Flavor;
+import com.yahoo.config.provision.NodeResources;
+import com.yahoo.config.provision.NodeType;
+import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.NodeRepository;
+import com.yahoo.vespa.hosted.provision.Nodelike;
+import com.yahoo.vespa.hosted.provision.provisioning.HostResourcesCalculator;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author valerijf
+ * @author bratseth
+ */
+public class AwsHostResourcesCalculatorImpl implements HostResourcesCalculator {
+
+ private final Map<String, VespaFlavor> flavors;
+ private final AwsResourcesCalculator resourcesCalculator;
+
+ public AwsHostResourcesCalculatorImpl() {
+ this.flavors = AwsNodeTypes.asVespaFlavors().stream().collect(Collectors.toMap(f -> f.name(), f -> f));
+ this.resourcesCalculator = new AwsResourcesCalculator();
+ }
+
+ @Override
+ public NodeResources realResourcesOf(Nodelike node, NodeRepository nodeRepository) {
+ if (node.parentHostname().isEmpty()) return resourcesCalculator.realResourcesOfParentHost(node.resources()); // hosts use configured flavors
+
+ Node parentHost = nodeRepository.nodes().node(node.parentHostname().get()).orElseThrow();
+ VespaFlavor hostFlavor = flavors.get(parentHost.flavor().name());
+ return resourcesCalculator.realResourcesOfChildContainer(node.resources(), hostFlavor);
+ }
+
+ @Override
+ public NodeResources advertisedResourcesOf(Flavor flavor) {
+ if ( ! flavor.isConfigured()) return flavor.resources(); // Node 'flavors' just wrap the advertised resources
+ return flavors.get(flavor.name()).advertisedResources();
+ }
+
+ @Override
+ public NodeResources requestToReal(NodeResources advertisedResources, boolean exclusive) {
+ double memoryOverhead = compatibleFlavors(advertisedResources, exclusive)
+ .mapToDouble(flavor -> resourcesCalculator.memoryOverhead(flavor, advertisedResources, false)).max().orElse(0);
+ double diskOverhead = compatibleFlavors(advertisedResources, exclusive)
+ .mapToDouble(flavor -> resourcesCalculator.diskOverhead(flavor, advertisedResources, false, exclusive)).max().orElse(0);
+ return advertisedResources.withMemoryGb(advertisedResources.memoryGb() - memoryOverhead)
+ .withDiskGb(advertisedResources.diskGb() - diskOverhead);
+ }
+
+ @Override
+ public NodeResources realToRequest(NodeResources realResources, boolean exclusive) {
+ double worstMemoryOverhead = 0;
+ double worstDiskOverhead = 0;
+ for (VespaFlavor flavor : flavors.values()) {
+ double memoryOverhead = resourcesCalculator.memoryOverhead(flavor, realResources, true);
+ double diskOverhead = resourcesCalculator.diskOverhead(flavor, realResources, true, exclusive);
+ NodeResources advertised = realResources.withMemoryGb(realResources.memoryGb() + memoryOverhead)
+ .withDiskGb(realResources.diskGb() + diskOverhead);
+ if ( ! flavor.advertisedResources().satisfies(advertised)) continue;
+ if (memoryOverhead > worstMemoryOverhead)
+ worstMemoryOverhead = memoryOverhead;
+ if (diskOverhead > worstDiskOverhead)
+ worstDiskOverhead = diskOverhead;
+ }
+ return realResources.withMemoryGb(realResources.memoryGb() + worstMemoryOverhead)
+ .withDiskGb(realResources.diskGb() + worstDiskOverhead);
+ }
+
+ @Override
+ public long reservedDiskSpaceInBase2Gb(NodeType nodeType, boolean sharedHost) {
+ return 1;
+ }
+
+ /** Returns the flavors of hosts which are eligible and matches the given advertised resources */
+ private Stream<VespaFlavor> compatibleFlavors(NodeResources advertisedResources, boolean exclusive) {
+ return flavors.values().stream()
+ .filter(flavor -> exclusive
+ ? flavor.advertisedResources().compatibleWith(advertisedResources)
+ : flavor.advertisedResources().satisfies(advertisedResources));
+ }
+
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsNodeTypes.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsNodeTypes.java
new file mode 100644
index 00000000000..ceaa6861f20
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsNodeTypes.java
@@ -0,0 +1,122 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.autoscale.awsnodes;
+
+import com.yahoo.config.provision.Flavor;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.yahoo.config.provision.NodeResources.Architecture.arm64;
+import static com.yahoo.config.provision.NodeResources.Architecture.x86_64;
+import static com.yahoo.config.provision.NodeResources.DiskSpeed.fast;
+import static com.yahoo.config.provision.NodeResources.StorageType.local;
+import static com.yahoo.config.provision.NodeResources.StorageType.remote;
+
+/**
+ * Returns the information about all AWS node types supported on Vespa Cloud as of 2022-10-31.
+ *
+ * @author bratseth
+ */
+public class AwsNodeTypes {
+
+ private static final List<VespaFlavor> hostFlavors =
+ List.of(new VespaFlavor("t3_nano", 0.5, 0.5, 0.5, 0.45, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("t3_micro", 0.5, 0.5, 1.0, 0.94, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("t3_small", 0.5, 0.5, 2.0, 1.8, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("t3_medium", 0.5, 0.5, 4.0, 3.6, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("t3_large", 0.5, 0.5, 8.0, 7.5, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("t3_xlarge", 0.5, 0.5, 16.0, 15.1, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("t3_2xlarge", 0.5, 0.5, 32.0, 30.5, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("m5_large", 2.0, 2.0, 8.0, 7.3, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("m5d_large", 2.0, 2.0, 8.0, 7.3, 75.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("m5_xlarge", 4.0, 4.0, 16.0, 15.1, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("m5d_xlarge", 4.0, 4.0, 16.0, 15.1, 150.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("m5_2xlarge", 8.0, 8.0, 32.0, 30.5, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("m5d_2xlarge", 8.0, 8.0, 32.0, 30.5, 300.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("m5_4xlarge", 16.0, 16.0, 64.0, 61.3, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("m5d_4xlarge", 16.0, 16.0, 64.0, 61.3, 600.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("m5_8xlarge", 32.0, 32.0, 128.0, 123.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("m5d_8xlarge", 32.0, 32.0, 128.0, 123.0, 1200.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("m5_12xlarge", 48.0, 48.0, 192.0, 185.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("m5d_12xlarge", 48.0, 48.0, 192.0, 185.0, 1800.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("m5_16xlarge", 64.0, 64.0, 256.0, 246.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("m5d_16xlarge", 64.0, 64.0, 256.0, 246.0, 2400.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("m5_24xlarge", 96.0, 96.0, 384.0, 370.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("m5d_24xlarge", 96.0, 96.0, 384.0, 370.0, 3600.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("m6g_large", 2.0, 2.0, 8.0, 7.3, 16384.0, 10.0, fast, remote, arm64),
+ new VespaFlavor("m6g_xlarge", 4.0, 4.0, 16.0, 15.1, 16384.0, 10.0, fast, remote, arm64),
+ new VespaFlavor("m6g_2xlarge", 8.0, 8.0, 32.0, 30.5, 16384.0, 10.0, fast, remote, arm64),
+ new VespaFlavor("m6g_4xlarge", 16.0, 16.0, 64.0, 61.3, 16384.0, 10.0, fast, remote, arm64),
+ new VespaFlavor("m6g_8xlarge", 32.0, 32.0, 128.0, 123.0, 16384.0, 10.0, fast, remote, arm64),
+ new VespaFlavor("m6g_12xlarge", 48.0, 48.0, 192.0, 185.0, 16384.0, 10.0, fast, remote, arm64),
+ new VespaFlavor("m6g_16xlarge", 64.0, 64.0, 256.0, 246.0, 16384.0, 10.0, fast, remote, arm64),
+ new VespaFlavor("c5_large", 2.0, 2.0, 4.0, 3.5, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("c5d_large", 2.0, 2.0, 4.0, 3.5, 50.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c5_xlarge", 4.0, 4.0, 8.0, 7.3, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("c5d_xlarge", 4.0, 4.0, 8.0, 7.3, 100.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c5_2xlarge", 8.0, 8.0, 16.0, 15.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("c5d_2xlarge", 8.0, 8.0, 16.0, 15.0, 200.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c5_4xlarge", 16.0, 16.0, 32.0, 30.3, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("c5d_4xlarge", 16.0, 16.0, 32.0, 30.3, 400.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c5_9xlarge", 36.0, 36.0, 72.0, 68.5, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("c5d_9xlarge", 36.0, 36.0, 72.0, 68.5, 900.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c5_12xlarge", 48.0, 48.0, 96.0, 92.2, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("c5d_12xlarge", 48.0, 48.0, 96.0, 92.2, 1800.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c5_18xlarge", 72.0, 72.0, 144.0, 137.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("c5d_18xlarge", 72.0, 72.0, 144.0, 137.0, 1800.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c5_24xlarge", 96.0, 96.0, 192.0, 185.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("c5d_24xlarge", 96.0, 96.0, 192.0, 185.0, 3600.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c5ad_8xlarge", 32.0, 32.0, 64.0, 62.0, 1200.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c6gd_12xlarge", 48.0, 48.0, 96.0, 93.0, 2850.0, 10.0, fast, local, arm64),
+ new VespaFlavor("c6gd_16xlarge", 64.0, 64.0, 128.0, 125.0, 3800.0, 10.0, fast, local, arm64),
+ new VespaFlavor("c6id_16xlarge", 64.0, 64.0, 128.0, 123.0, 3800.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("c7g_16xlarge", 64.0, 64.0, 128.0, 125.0, 16384.0, 10.0, fast, remote, arm64),
+ new VespaFlavor("r5_large", 2.0, 2.0, 16.0, 15.2, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("r5d_large", 2.0, 2.0, 16.0, 15.2, 75.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("r5_xlarge", 4.0, 4.0, 32.0, 30.8, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("r5d_xlarge", 4.0, 4.0, 32.0, 30.8, 150.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("r5_2xlarge", 8.0, 8.0, 64.0, 62.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("r5d_2xlarge", 8.0, 8.0, 64.0, 62.0, 300.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("r5_4xlarge", 16.0, 16.0, 128.0, 124.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("r5d_4xlarge", 16.0, 16.0, 128.0, 124.0, 600.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("r5_8xlarge", 32.0, 32.0, 256.0, 249.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("r5d_8xlarge", 32.0, 32.0, 256.0, 249.0, 1200.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("r5_12xlarge", 48.0, 48.0, 384.0, 374.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("r5d_12xlarge", 48.0, 48.0, 384.0, 374.0, 1800.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("r5_16xlarge", 64.0, 64.0, 512.0, 498.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("r5d_16xlarge", 64.0, 64.0, 512.0, 498.0, 2400.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("r5_24xlarge", 96.0, 96.0, 768.0, 748.0, 16384.0, 10.0, fast, remote, x86_64),
+ new VespaFlavor("r5d_24xlarge", 96.0, 96.0, 768.0, 748.0, 3600.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("x1_16xlarge", 64.0, 64.0, 976.0, 946.0, 1920.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("x1_32xlarge", 128.0, 128.0, 1952.0, 1893.0, 3840.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("x1e_xlarge", 4.0, 4.0, 122.0, 118.0, 120.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("x1e_2xlarge", 8.0, 8.0, 244.0, 237.0, 240.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("x1e_4xlarge", 16.0, 16.0, 488.0, 474.0, 480.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("x1e_8xlarge", 32.0, 32.0, 976.0, 946.0, 960.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("x1e_16xlarge", 64.0, 64.0, 1952.0, 1893.0, 1920.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("x1e_32xlarge", 128.0, 128.0, 3904.0, 3786.0, 3840.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("z1d_6xlarge", 24.0, 24.0, 192.0, 185.0, 900.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("i4i_large", 2.0, 2.0, 16.0, 15.1, 468.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("i4i_xlarge", 4.0, 4.0, 32.0, 30.5, 937.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("i4i_2xlarge", 8.0, 8.0, 64.0, 61.3, 1875.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("i4i_4xlarge", 16.0, 16.0, 128.0, 123.0, 3750.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("i4i_8xlarge", 32.0, 32.0, 256.0, 246.0, 7500.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("i4i_16xlarge", 64.0, 64.0, 512.0, 498.0, 15000.0, 10.0, fast, local, x86_64),
+ new VespaFlavor("i4i_32xlarge", 128.0, 128.0, 1024.0, 996.0, 30000.0, 10.0, fast, local, x86_64));
+
+ public static List<VespaFlavor> asVespaFlavors() { return hostFlavors; }
+
+ public static List<Flavor> asFlavors() {
+ return hostFlavors.stream().map(f -> toFlavor(f)).toList();
+ }
+
+ private static Flavor toFlavor(VespaFlavor vespaFlavor) {
+ return new Flavor(vespaFlavor.name(),
+ vespaFlavor.realResources(),
+ Optional.empty(),
+ Flavor.Type.VIRTUAL_MACHINE,
+ true,
+ (int)(vespaFlavor.advertisedResources().cost()*24*30));
+ }
+
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsResourcesCalculator.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsResourcesCalculator.java
new file mode 100644
index 00000000000..477c16bf6d4
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/AwsResourcesCalculator.java
@@ -0,0 +1,71 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.autoscale.awsnodes;
+
+import com.yahoo.config.provision.NodeResources;
+
+/**
+ * Calculations and logic on node resources common to provision-service and host-admin (at least).
+ *
+ * @author hakon
+ */
+public class AwsResourcesCalculator {
+
+ private final double hostMemory = 0.6;
+ private final double hostDiskOverhead = 1;
+
+ /** The real resources of a parent host node in the node repository, given the real resources of the flavor. */
+ public NodeResources realResourcesOfParentHost(NodeResources realResourcesOfFlavor) {
+ return realResourcesOfFlavor;
+ }
+
+ /** The real resources of a child. */
+ public NodeResources realResourcesOfChildContainer(NodeResources resources, VespaFlavor hostFlavor) {
+ boolean exclusive = saturates(hostFlavor, resources);
+ return resources.withMemoryGb(resources.memoryGb() - memoryOverhead(hostFlavor, resources, false))
+ .withDiskGb(resources.diskGb() - diskOverhead(hostFlavor, resources, false, exclusive));
+ }
+
+ /**
+ * Returns the memory overhead resulting if the given advertised resources are placed on the given node
+ *
+ * @param real true if the given resources are in real values, false if they are in advertised
+ */
+ public double memoryOverhead(VespaFlavor hostFlavor, NodeResources resources, boolean real) {
+ double hostMemoryOverhead =
+ hostFlavor.advertisedResources().memoryGb() - hostFlavor.realResources().memoryGb()
+ + hostMemory; // Approximate cost of host administration processes
+
+ if (hostMemoryOverhead > hostFlavor.advertisedResources().memoryGb()) // An unusably small flavor,
+ hostMemoryOverhead = hostFlavor.advertisedResources().memoryGb(); // overhead cannot exceed what's available
+
+ double memoryShare = resources.memoryGb() /
+ ( hostFlavor.advertisedResources().memoryGb() - ( real ? hostMemoryOverhead : 0));
+ return hostMemoryOverhead * memoryShare;
+ }
+
+ /**
+ * Returns the disk overhead resulting if the given advertised resources are placed on the given node
+ *
+ * @param real true if the resources are in real values, false if they are in advertised
+ */
+ public double diskOverhead(VespaFlavor flavor, NodeResources resources, boolean real, boolean exclusive) {
+ if ( flavor.realResources().storageType() != NodeResources.StorageType.local) return 0;
+ double diskShare = resources.diskGb() /
+ ( flavor.advertisedResources().diskGb() - ( real ? hostDiskOverhead : 0) );
+ return hostDiskOverhead * diskShare;
+ }
+
+ /** Returns whether nodeResources saturates at least one resource dimension of hostFlavor */
+ private boolean saturates(VespaFlavor hostFlavor, NodeResources nodeResources) {
+ NodeResources hostResources = hostFlavor.advertisedResources();
+ return equal(hostResources.vcpu(), nodeResources.vcpu()) ||
+ equal(hostResources.memoryGb(), nodeResources.memoryGb()) ||
+ equal(hostResources.diskGb(), nodeResources.diskGb()) ||
+ equal(hostResources.bandwidthGbps(), nodeResources.bandwidthGbps());
+ }
+
+ private boolean equal(double a, double b) {
+ return Math.abs(a - b) < 0.00000001;
+ }
+
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/VespaFlavor.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/VespaFlavor.java
new file mode 100644
index 00000000000..cd5f18db516
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/awsnodes/VespaFlavor.java
@@ -0,0 +1,37 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.autoscale.awsnodes;
+
+import com.yahoo.config.provision.NodeResources;
+
+/**
+ * Holds the advertised and real resources of a host type.
+ *
+ * @author bratseth
+ */
+public class VespaFlavor {
+
+ private final String name;
+ private final NodeResources realResources, advertisedResources;
+
+ public VespaFlavor(String name,
+ double advertisedVcpu,
+ double realVcpu,
+ double advertisedMemoryGb,
+ double realMemoryGb,
+ double diskGb,
+ double bandwidthGbps,
+ NodeResources.DiskSpeed diskSpeed,
+ NodeResources.StorageType storageType,
+ NodeResources.Architecture architecture) {
+ this.name = name;
+ this.realResources = new NodeResources(realVcpu, realMemoryGb, diskGb, bandwidthGbps, diskSpeed, storageType, architecture);
+ this.advertisedResources = new NodeResources(advertisedVcpu, advertisedMemoryGb, diskGb, bandwidthGbps, diskSpeed, storageType, architecture);
+ }
+
+ public String name() { return name; }
+
+ public NodeResources realResources() { return realResources; }
+
+ public NodeResources advertisedResources() { return advertisedResources; }
+
+}