diff options
author | Valerij Fredriksen <freva@users.noreply.github.com> | 2022-11-01 12:50:30 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-01 12:50:30 +0100 |
commit | 60d6cab0299751e22a7d8f5be8c5cc89f996389f (patch) | |
tree | ce9cb41db899856874648d75e5dd92c95ae3fe50 /node-repository | |
parent | 8b832bdcdf756c5bef9024776df052f079a40643 (diff) | |
parent | aba1d62aeb907003677cbe03133cb808b85a32ff (diff) |
Merge pull request #24683 from vespa-engine/bratseth/no-traffic-test
Test with no traffic
Diffstat (limited to 'node-repository')
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; } + +} |