aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java')
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java912
1 files changed, 364 insertions, 548 deletions
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 d3142b3cc7f..a2eb5fba9f9 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
@@ -36,260 +36,196 @@ public class AutoscalingTest {
@Test
public void test_autoscaling_single_content_group() {
- NodeResources hostResources = new NodeResources(3, 100, 100, 1);
- ClusterResources min = new ClusterResources( 2, 1,
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
+ var fixture = AutoscalingTester.fixture().build();
- // deploy
- tester.deploy(application1, cluster1, 5, 1, hostResources);
+ fixture.tester().clock().advance(Duration.ofDays(1));
+ assertTrue("No measurements -> No change", fixture.autoscale().isEmpty());
- tester.clock().advance(Duration.ofDays(1));
- assertTrue("No measurements -> No change", tester.autoscale(application1, cluster1, capacity).isEmpty());
+ fixture.applyCpuLoad(0.7f, 59);
+ assertTrue("Too few measurements -> No change", fixture.autoscale().isEmpty());
- tester.addCpuMeasurements(0.25f, 1f, 59, application1);
- assertTrue("Too few measurements -> No change", tester.autoscale(application1, cluster1, capacity).isEmpty());
+ fixture.tester().clock().advance(Duration.ofDays(1));
+ fixture.applyCpuLoad(0.7f, 120);
+ ClusterResources scaledResources = fixture.tester().assertResources("Scaling up since resource usage is too high",
+ 9, 1, 2.8, 5.0, 50.0,
+ fixture.autoscale());
- tester.clock().advance(Duration.ofDays(1));
- tester.addCpuMeasurements(0.25f, 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
- ClusterResources scaledResources = tester.assertResources("Scaling up since resource usage is too high",
- 15, 1, 1.2, 28.6, 28.6,
- tester.autoscale(application1, cluster1, capacity));
+ fixture.deploy(Capacity.from(scaledResources));
+ assertTrue("Cluster in flux -> No further change", fixture.autoscale().isEmpty());
- tester.deploy(application1, cluster1, scaledResources);
- assertTrue("Cluster in flux -> No further change", tester.autoscale(application1, cluster1, capacity).isEmpty());
+ fixture.deactivateRetired(Capacity.from(scaledResources));
- tester.deactivateRetired(application1, cluster1, scaledResources);
-
- tester.clock().advance(Duration.ofDays(2));
- tester.addCpuMeasurements(0.8f, 1f, 3, application1);
- tester.clock().advance(Duration.ofMinutes(-10 * 5));
- tester.addQueryRateMeasurements(application1, cluster1.id(), 10, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyCpuLoad(0.8f, 3);
assertTrue("Load change is large, but insufficient measurements for new config -> No change",
- tester.autoscale(application1, cluster1, capacity).isEmpty());
+ fixture.autoscale().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, capacity).target());
+ fixture.applyCpuLoad(0.19f, 100);
+ assertEquals("Load change is small -> No change", Optional.empty(), fixture.autoscale().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, capacity));
+ fixture.applyCpuLoad(0.1f, 120);
+ fixture.tester().assertResources("Scaling cpu down since usage has gone down significantly",
+ 9, 1, 1.0, 5.0, 50.0,
+ fixture.autoscale());
+ }
+
+ /** Using too many resources for a short period is proof we should scale up regardless of the time that takes. */
+ @Test
+ public void test_autoscaling_up_is_fast_TODO() {
+ var fixture = AutoscalingTester.fixture().build();
+ fixture.tester().clock().advance(Duration.ofDays(1)); // TODO: Remove the need for this
+ fixture.applyLoad(1.0, 1.0, 1.0, 120); // TODO: Make this low
+ fixture.tester().assertResources("Scaling up since resource usage is too high",
+ 10, 1, 9.4, 8.5, 92.6,
+ 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() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- // deploy
- tester.deploy(application1, cluster1, 5, 1, resources);
- tester.addCpuMeasurements(0.25f, 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
- ClusterResources scaledResources = tester.assertResources("Scaling up since cpu usage is too high",
- 7, 1, 2.5, 80.0, 50.5,
- tester.autoscale(application1, cluster1, capacity));
-
- tester.deploy(application1, cluster1, scaledResources);
- tester.deactivateRetired(application1, cluster1, scaledResources);
-
- 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 since cpu usage has gone down",
- 4, 1, 2.5, 68.6, 27.4,
- tester.autoscale(application1, cluster1, capacity));
+ var fixture = AutoscalingTester.fixture().clusterType(ClusterSpec.Type.container).build();
+ fixture.applyCpuLoad(0.25f, 120);
+ ClusterResources scaledResources = fixture.tester().assertResources("Scaling up since cpu usage is too high",
+ 5, 1, 3.8, 8.0, 50.5,
+ fixture.autoscale());
+ fixture.deploy(Capacity.from(scaledResources));
+ fixture.applyCpuLoad(0.1f, 120);
+ fixture.tester().assertResources("Scaling down since cpu usage has gone down",
+ 4, 1, 2.5, 6.4, 25.5,
+ fixture.autoscale());
}
@Test
public void autoscaling_handles_disk_setting_changes() {
- NodeResources hostResources = new NodeResources(3, 100, 100, 1, NodeResources.DiskSpeed.slow);
- AutoscalingTester tester = new AutoscalingTester(hostResources);
-
- ApplicationId application1 = tester.applicationId("application1");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
-
- // deploy with slow
- tester.deploy(application1, cluster1, 5, 1, hostResources);
- assertTrue(tester.nodeRepository().nodes().list().owner(application1).stream()
+ var resources = new NodeResources(3, 100, 100, 1, NodeResources.DiskSpeed.slow);
+ var fixture = AutoscalingTester.fixture()
+ .hostResources(resources)
+ .initialResources(Optional.of(new ClusterResources(5, 1, resources)))
+ .capacity(Capacity.from(new ClusterResources(5, 1, resources)))
+ .build();
+
+ assertTrue(fixture.tester().nodeRepository().nodes().list().owner(fixture.application).stream()
.allMatch(n -> n.allocation().get().requestedResources().diskSpeed() == NodeResources.DiskSpeed.slow));
- tester.clock().advance(Duration.ofDays(2));
- tester.addQueryRateMeasurements(application1, cluster1.id(), 100, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
- tester.addCpuMeasurements(0.25f, 1f, 120, application1);
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyCpuLoad(0.25, 120);
+
// Changing min and max from slow to any
ClusterResources min = new ClusterResources( 2, 1,
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, capacity));
- assertEquals("Disk speed from min/max is used",
+ ClusterResources scaledResources = fixture.tester().assertResources("Scaling up",
+ 14, 1, 1.4, 30.8, 30.8,
+ fixture.autoscale(capacity));
+ assertEquals("Disk speed from new capacity is used",
NodeResources.DiskSpeed.any, scaledResources.nodeResources().diskSpeed());
- tester.deploy(application1, cluster1, scaledResources);
- assertTrue(tester.nodeRepository().nodes().list().owner(application1).stream()
- .allMatch(n -> n.allocation().get().requestedResources().diskSpeed() == NodeResources.DiskSpeed.any));
+ fixture.deploy(Capacity.from(scaledResources));
+ assertTrue(fixture.nodes().stream()
+ .allMatch(n -> n.allocation().get().requestedResources().diskSpeed() == NodeResources.DiskSpeed.any));
}
@Test
public void autoscaling_target_preserves_any() {
- NodeResources hostResources = new NodeResources(3, 100, 100, 1);
- AutoscalingTester tester = new AutoscalingTester(hostResources);
-
- ApplicationId application1 = tester.applicationId("application1");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
-
- // Initial deployment
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));
+ var capacity = Capacity.from(new ClusterResources( 2, 1, resources.with(NodeResources.DiskSpeed.any)),
+ new ClusterResources( 10, 1, resources.with(NodeResources.DiskSpeed.any)));
+ var fixture = AutoscalingTester.fixture()
+ .capacity(capacity)
+ .initialResources(Optional.empty())
+ .build();
// Redeployment without target: Uses current resource numbers with *requested* non-numbers (i.e disk-speed any)
- assertTrue(tester.nodeRepository().applications().get(application1).get().cluster(cluster1.id()).get().targetResources().isEmpty());
- tester.deploy(application1, cluster1, Capacity.from(min, max));
- assertEquals(NodeResources.DiskSpeed.any,
- tester.nodeRepository().nodes().list().owner(application1).cluster(cluster1.id()).first().get()
- .allocation().get().requestedResources().diskSpeed());
+ assertTrue(fixture.tester().nodeRepository().applications().get(fixture.application).get().cluster(fixture.cluster.id()).get().targetResources().isEmpty());
+ fixture.deploy();
+ assertEquals(NodeResources.DiskSpeed.any, fixture.nodes().first().get().allocation().get().requestedResources().diskSpeed());
// 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, capacity);
- assertEquals(NodeResources.DiskSpeed.any, advice.target().get().nodeResources().diskSpeed());
-
-
+ fixture.deactivateRetired(capacity);
+ fixture.tester().clock().advance(Duration.ofDays(1));
+ fixture.applyCpuLoad(0.8, 120);
+ assertEquals(NodeResources.DiskSpeed.any, fixture.autoscale(capacity).target().get().nodeResources().diskSpeed());
}
@Test
public void autoscaling_respects_upper_limit() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- // deploy
- tester.deploy(application1, cluster1, 5, 1,
- new NodeResources(1.9, 70, 70, 1));
- tester.addMeasurements(0.25f, 0.95f, 0.95f, 0, 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 up to limit since resource usage is too high",
- 6, 1, 2.4, 78.0, 70.0,
- tester.autoscale(application1, cluster1, capacity));
+ var min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
+ var now = new ClusterResources(5, 1, new NodeResources(1.9, 70, 70, 1));
+ var max = new ClusterResources( 6, 1, new NodeResources(2.4, 78, 79, 1));
+ var fixture = AutoscalingTester.fixture()
+ .initialResources(Optional.of(now))
+ .capacity(Capacity.from(min, max)).build();
+
+ fixture.tester().clock().advance(Duration.ofDays(1));
+ fixture.applyLoad(0.25, 0.95, 0.95, 120);
+ fixture.tester().assertResources("Scaling up to limit since resource usage is too high",
+ 6, 1, 2.4, 78.0, 79.0,
+ fixture.autoscale());
}
@Test
public void autoscaling_respects_lower_limit() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
+ var min = new ClusterResources( 4, 1, new NodeResources(1.8, 7.4, 8.5, 1));
+ var max = new ClusterResources( 6, 1, new NodeResources(2.4, 78, 79, 1));
+ var fixture = AutoscalingTester.fixture().capacity(Capacity.from(min, max)).build();
// deploy
- tester.deploy(application1, cluster1, 5, 1, resources);
- 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, capacity));
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyLoad(0.05f, 0.05f, 0.05f, 120);
+ fixture.tester().assertResources("Scaling down to limit since resource usage is low",
+ 4, 1, 1.8, 7.4, 13.9,
+ fixture.autoscale());
}
@Test
public void autoscaling_with_unspecified_resources_use_defaults() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
+ var min = new ClusterResources( 2, 1, NodeResources.unspecified());
+ var max = new ClusterResources( 6, 1, NodeResources.unspecified());
+ var fixture = AutoscalingTester.fixture()
+ .initialResources(Optional.empty())
+ .capacity(Capacity.from(min, max))
+ .build();
NodeResources defaultResources =
- new CapacityPolicies(tester.nodeRepository()).defaultNodeResources(cluster1, application1, false);
-
- // deploy
- tester.deploy(application1, cluster1, Capacity.from(min, max));
- tester.assertResources("Min number of nodes and default resources",
- 2, 1, defaultResources,
- tester.nodeRepository().nodes().list().owner(application1).toResources());
- tester.addMeasurements(0.25f, 0.95f, 0.95f, 0, 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 up to limit since resource usage is too high",
- 4, 1,
- defaultResources.vcpu(), defaultResources.memoryGb(), defaultResources.diskGb(),
- tester.autoscale(application1, cluster1, capacity));
+ new CapacityPolicies(fixture.tester().nodeRepository()).defaultNodeResources(fixture.cluster, fixture.application, false);
+
+ fixture.tester().assertResources("Min number of nodes and default resources",
+ 2, 1, defaultResources,
+ fixture.nodes().toResources());
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyLoad(0.25, 0.95, 0.95, 120);
+ fixture.tester().assertResources("Scaling up",
+ 5, 1,
+ defaultResources.vcpu(), defaultResources.memoryGb(), defaultResources.diskGb(),
+ fixture.autoscale());
}
@Test
public void autoscaling_respects_group_limit() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- // deploy
- tester.deploy(application1, cluster1, 5, 5, new NodeResources(3.0, 10, 10, 1));
- tester.addCpuMeasurements( 0.3f, 1f, 240, 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 up since resource usage is too high",
- 6, 6, 3.6, 8.0, 10.0,
- tester.autoscale(application1, cluster1, capacity));
+ var min = new ClusterResources( 2, 2, new NodeResources(1, 1, 1, 1));
+ var now = new ClusterResources(5, 5, new NodeResources(3.0, 10, 10, 1));
+ var max = new ClusterResources(18, 6, new NodeResources(100, 1000, 1000, 1));
+ var fixture = AutoscalingTester.fixture()
+ .initialResources(Optional.of(now))
+ .capacity(Capacity.from(min, max))
+ .build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyCpuLoad(0.3, 240);
+ fixture.tester().assertResources("Scaling up",
+ 6, 6, 3.8, 8.0, 10.0,
+ fixture.autoscale());
}
@Test
public void test_autoscaling_limits_when_min_equals_max() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
+ var fixture = AutoscalingTester.fixture().capacity(Capacity.from(min, min)).build();
// deploy
- 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, capacity).isEmpty());
+ fixture.tester().clock().advance(Duration.ofDays(1));
+ fixture.applyCpuLoad(0.25, 120);
+ assertTrue(fixture.autoscale().isEmpty());
}
@Test
@@ -308,8 +244,8 @@ public class AutoscalingTest {
tester.provisioning().makeReadyNodes(5, remoteFlavor.name(), NodeType.host, 8);
tester.provisioning().activateTenantHosts();
- ApplicationId application1 = tester.applicationId("application1");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
+ ApplicationId application1 = AutoscalingTester.applicationId("application1");
+ ClusterSpec cluster1 = AutoscalingTester.clusterSpec(ClusterSpec.Type.container, "cluster1");
// deploy
tester.deploy(application1, cluster1, 3, 1, min.nodeResources());
@@ -326,239 +262,150 @@ public class AutoscalingTest {
@Test
public void suggestions_ignores_limits() {
- NodeResources resources = new NodeResources(3, 100, 100, 1);
ClusterResources min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
- ClusterResources max = min;
- AutoscalingTester tester = new AutoscalingTester(resources.withVcpu(resources.vcpu() * 2));
-
- ApplicationId application1 = tester.applicationId("application1");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- // deploy
- tester.deploy(application1, cluster1, 5, 1, resources);
- tester.addCpuMeasurements(0.25f, 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 up since resource usage is too high",
- 7, 1, 2.5, 80.0, 50.5,
- tester.suggest(application1, cluster1.id(), min, max));
+ var fixture = AutoscalingTester.fixture().capacity(Capacity.from(min, min)).build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyCpuLoad(1.0, 120);
+ fixture.tester().assertResources("Suggesting above capacity limit",
+ 8, 1, 9.3, 5.7, 57.1,
+ fixture.tester().suggest(fixture.application, fixture.cluster.id(), min, min));
}
@Test
public void not_using_out_of_service_measurements() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- // deploy
- 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, capacity).isEmpty());
+ var fixture = AutoscalingTester.fixture().build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyLoad(0.9, 0.6, 0.7, 1, false, true, 120);
+ assertTrue("Not scaling up since nodes were measured while cluster was out of service",
+ fixture.autoscale().isEmpty());
}
@Test
public void not_using_unstable_measurements() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- // deploy
- 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, capacity).isEmpty());
+ var fixture = AutoscalingTester.fixture().build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyLoad(0.9, 0.6, 0.7, 1, true, false, 120);
+ assertTrue("Not scaling up since nodes were measured while cluster was out of service",
+ fixture.autoscale().isEmpty());
}
@Test
public void test_autoscaling_group_size_1() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- // deploy
- tester.deploy(application1, cluster1, 5, 5, resources);
- tester.addCpuMeasurements(0.25f, 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 up since resource usage is too high",
- 7, 7, 2.5, 80.0, 50.5,
- tester.autoscale(application1, cluster1, capacity));
+ var min = new ClusterResources( 2, 2, new NodeResources(1, 1, 1, 1));
+ var now = new ClusterResources(5, 5, new NodeResources(3, 100, 100, 1));
+ var max = new ClusterResources(20, 20, new NodeResources(10, 1000, 1000, 1));
+ var fixture = AutoscalingTester.fixture()
+ .initialResources(Optional.of(now))
+ .capacity(Capacity.from(min, max))
+ .build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyCpuLoad(0.9, 120);
+ fixture.tester().assertResources("Scaling the number of groups, but nothing requires us to stay with 1 node per group",
+ 10, 5, 7.7, 40.0, 40.0,
+ fixture.autoscale());
}
@Test
public void test_autoscaling_groupsize_by_cpu_read_dominated() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- // deploy
- tester.deploy(application1, cluster1, 6, 2, resources);
- tester.addCpuMeasurements(0.25f, 1f, 120, application1);
- tester.clock().advance(Duration.ofMinutes(-10 * 5));
- tester.addLoadMeasurements(application1, cluster1.id(), 10,
- t -> t == 0 ? 20.0 : 10.0,
- 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, 52.6,
- tester.autoscale(application1, cluster1, capacity));
+ var min = new ClusterResources( 3, 1, new NodeResources(1, 1, 1, 1));
+ var now = new ClusterResources(6, 2, new NodeResources(3, 100, 100, 1));
+ var max = new ClusterResources(21, 7, new NodeResources(100, 1000, 1000, 1));
+ var fixture = AutoscalingTester.fixture()
+ .initialResources(Optional.of(now))
+ .capacity(Capacity.from(min, max))
+ .build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ Duration timePassed = fixture.addCpuMeasurements(0.25, 120);
+ fixture.tester().clock().advance(timePassed.negated());
+ fixture.addLoadMeasurements(10, t -> t == 0 ? 20.0 : 10.0, t -> 1.0);
+ fixture.tester().assertResources("Scaling up since resource usage is too high, changing to 1 group is cheaper",
+ 10, 1, 2.3, 27.8, 27.8,
+ fixture.autoscale());
}
/** Same as above but mostly write traffic, which favors smaller groups */
@Test
public void test_autoscaling_groupsize_by_cpu_write_dominated() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- // deploy
- tester.deploy(application1, cluster1, 6, 2, resources);
- tester.addCpuMeasurements(0.25f, 1f, 120, application1);
- tester.clock().advance(Duration.ofMinutes(-10 * 5));
- tester.addLoadMeasurements(application1, cluster1.id(), 10,
- t -> t == 0 ? 20.0 : 10.0,
- 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, 52.6,
- tester.autoscale(application1, cluster1, capacity));
+ var min = new ClusterResources( 3, 1, new NodeResources(1, 1, 1, 1));
+ var now = new ClusterResources(6, 2, new NodeResources(3, 100, 100, 1));
+ var max = new ClusterResources(21, 7, new NodeResources(100, 1000, 1000, 1));
+ var fixture = AutoscalingTester.fixture()
+ .initialResources(Optional.of(now))
+ .capacity(Capacity.from(min, max))
+ .build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ Duration timePassed = fixture.addCpuMeasurements(0.25, 120);
+ fixture.tester().clock().advance(timePassed.negated());
+ fixture.addLoadMeasurements(10, t -> t == 0 ? 20.0 : 10.0, t -> 100.0);
+ fixture.tester().assertResources("Scaling down since resource usage is too high, changing to 1 group is cheaper",
+ 6, 1, 1.0, 50.0, 50.0,
+ fixture.autoscale());
}
@Test
public void test_autoscaling_group_size() {
- NodeResources hostResources = new NodeResources(100, 1000, 1000, 100);
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
-
- // deploy
- tester.deploy(application1, cluster1, 6, 2, new NodeResources(10, 100, 100, 1));
- tester.clock().advance(Duration.ofDays(1));
- tester.addMemMeasurements(1.0f, 1f, 1000, 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("Increase group size to reduce memory load",
- 8, 2, 12.4, 96.2, 62.5,
- tester.autoscale(application1, cluster1, capacity));
+ var min = new ClusterResources( 2, 2, new NodeResources(1, 1, 1, 1));
+ var now = new ClusterResources(6, 2, new NodeResources(10, 100, 100, 1));
+ var max = new ClusterResources(30, 30, new NodeResources(100, 100, 1000, 1));
+ var fixture = AutoscalingTester.fixture()
+ .initialResources(Optional.of(now))
+ .capacity(Capacity.from(min, max))
+ .build();
+ fixture.tester().clock().advance(Duration.ofDays(1));
+ fixture.applyMemLoad(1.0, 1000);
+ fixture.tester().assertResources("Increase group size to reduce memory load",
+ 8, 2, 6.5, 96.2, 62.5,
+ fixture.autoscale());
}
@Test
public void autoscaling_avoids_illegal_configurations() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
-
- // deploy
- tester.deploy(application1, cluster1, 6, 1, hostResources.withVcpu(hostResources.vcpu() / 2));
- tester.clock().advance(Duration.ofDays(2));
- tester.addQueryRateMeasurements(application1, cluster1.id(), 100, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
- tester.addMemMeasurements(0.02f, 0.95f, 120, application1);
- tester.assertResources("Scaling down",
- 6, 1, 2.9, 4.0, 95.0,
- tester.autoscale(application1, cluster1, capacity));
+ var min = new ClusterResources( 2, 1, new NodeResources(1, 1, 1, 1));
+ var now = new ClusterResources(6, 1, new NodeResources(3, 100, 100, 1));
+ var max = new ClusterResources(20, 1, new NodeResources(100, 1000, 1000, 1));
+ var fixture = AutoscalingTester.fixture()
+ .initialResources(Optional.of(now))
+ .capacity(Capacity.from(min, max))
+ .build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyMemLoad(0.02, 120);
+ fixture.tester().assertResources("Scaling down",
+ 6, 1, 3.1, 4.0, 100.0,
+ fixture.autoscale());
}
@Test
public void scaling_down_only_after_delay() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
-
- tester.deploy(application1, cluster1, 6, 1, hostResources.withVcpu(hostResources.vcpu() / 2));
-
- // No autoscaling as it is too soon to scale down after initial deploy (counting as a scaling event)
- 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, capacity).target().isEmpty());
-
- // Trying the same later causes autoscaling
- tester.clock().advance(Duration.ofDays(2));
- 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
- tester.assertResources("Scaling down",
- 6, 1, 1.4, 4.0, 95.0,
- tester.autoscale(application1, cluster1, capacity));
+ var fixture = AutoscalingTester.fixture().build();
+ fixture.applyMemLoad(0.02, 120);
+ assertTrue("Too soon after initial deployment", fixture.autoscale().target().isEmpty());
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyMemLoad(0.02, 120);
+ fixture.tester().assertResources("Scaling down since enough time has passed",
+ 6, 1, 1.2, 4.0, 80.0,
+ fixture.autoscale());
}
@Test
public void test_autoscaling_considers_real_resources() {
- 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(new Zone(Environment.prod, RegionName.from("us-east")),
- hostResources,
- new OnlySubtractingWhenForecastingCalculator(0));
-
- ApplicationId application1 = tester.applicationId("app1");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
-
- tester.deploy(application1, cluster1, min);
- tester.addMeasurements(1.0f, 1.0f, 0.7f, 0, 1000, 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 up",
- 4, 1, 6.7, 20.5, 200,
- tester.autoscale(application1, cluster1, capacity));
+ var fixture = AutoscalingTester.fixture()
+ .resourceCalculator(new OnlySubtractingWhenForecastingCalculator(0))
+ .build();
+ fixture.applyLoad(1.0, 1.0, 0.7, 1000);
+ fixture.tester().assertResources("Scaling up",
+ 9, 1, 5.0, 9.6, 72.9,
+ fixture.autoscale());
}
- { // 15 Gb memory tax
- AutoscalingTester tester = new AutoscalingTester(new Zone(Environment.prod, RegionName.from("us-east")),
- hostResources,
- new OnlySubtractingWhenForecastingCalculator(15));
-
- ApplicationId application1 = tester.applicationId("app1");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
-
- tester.deploy(application1, cluster1, min);
- tester.addMeasurements(1.0f, 1.0f, 0.7f, 0, 1000, 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 up",
- 4, 1, 6.7, 35.5, 200,
- tester.autoscale(application1, cluster1, capacity));
+ {
+ var fixture = AutoscalingTester.fixture()
+ .resourceCalculator(new OnlySubtractingWhenForecastingCalculator(3))
+ .build();
+ fixture.applyLoad(1.0, 1.0, 0.7, 1000);
+ fixture.tester().assertResources("With 3Gb memory tax, we scale up memory more",
+ 7, 1, 6.4, 15.8, 97.2,
+ fixture.autoscale());
}
}
@@ -579,8 +426,8 @@ public class AutoscalingTest {
Environment.prod, RegionName.from("us-east")),
flavors);
- ApplicationId application1 = tester.applicationId("application1");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.content, "cluster1");
+ ApplicationId application1 = AutoscalingTester.applicationId("application1");
+ ClusterSpec cluster1 = AutoscalingTester.clusterSpec(ClusterSpec.Type.content, "cluster1");
// deploy (Why 103 Gb memory? See AutoscalingTester.MockHostResourcesCalculator
tester.deploy(application1, cluster1, 5, 1, new NodeResources(3, 103, 100, 1));
@@ -605,196 +452,165 @@ public class AutoscalingTest {
@Test
public void test_autoscaling_considers_read_share() {
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- tester.deploy(application1, cluster1, 5, 1, resources);
- tester.addQueryRateMeasurements(application1, cluster1.id(), 100, t -> t == 0 ? 20.0 : 10.0); // Query traffic only
- tester.clock().advance(Duration.ofMinutes(-100 * 5));
- tester.addCpuMeasurements(0.25f, 1f, 100, application1);
+ var min = new ClusterResources( 1, 1, new NodeResources(3, 100, 100, 1));
+ var max = new ClusterResources(10, 1, new NodeResources(3, 100, 100, 1));
+ var fixture = AutoscalingTester.fixture()
+ .capacity(Capacity.from(min, max))
+ .build();
+ fixture.tester.clock().advance(Duration.ofDays(1));
+ fixture.applyCpuLoad(0.25, 120);
// (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, capacity));
-
- 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, capacity));
-
- 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, capacity));
+ fixture.tester().assertResources("Advice to scale up since we set aside for bcp by default",
+ 7, 1, 3, 100, 100,
+ fixture.autoscale());
+
+ fixture.storeReadShare(0.25, 0.5);
+ fixture.tester().assertResources("Half of global share is the same as the default assumption used above",
+ 7, 1, 3, 100, 100,
+ fixture.autoscale());
+
+ fixture.storeReadShare(0.5, 0.5);
+ fixture.tester().assertResources("Advice to scale down since we don't need room for bcp",
+ 6, 1, 3, 100, 100,
+ fixture.autoscale());
}
@Test
public void test_autoscaling_considers_growth_rate() {
- NodeResources minResources = new NodeResources( 1, 100, 100, 1);
- NodeResources midResources = new NodeResources( 5, 100, 100, 1);
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
-
- tester.deploy(application1, cluster1, 5, 1, midResources);
- Duration timeAdded = tester.addQueryRateMeasurements(application1, cluster1.id(), 100, t -> t == 0 ? 20.0 : 10.0);
- tester.clock().advance(timeAdded.negated());
- tester.addCpuMeasurements(0.25f, 1f, 200, application1);
-
- // (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, capacity));
-
- tester.setScalingDuration(application1, cluster1.id(), Duration.ofMinutes(5));
- timeAdded = tester.addQueryRateMeasurements(application1, cluster1.id(),
- 100,
- t -> 10.0 + (t < 50 ? t : 100 - t));
- tester.clock().advance(timeAdded.negated());
- tester.addCpuMeasurements(0.25f, 1f, 200, application1);
- tester.assertResources("Scale down since observed growth is slower than scaling time",
- 5, 1, 3.4, 100, 100,
- tester.autoscale(application1, cluster1, capacity));
-
- tester.clearQueryRateMeasurements(application1, cluster1.id());
-
- tester.setScalingDuration(application1, cluster1.id(), Duration.ofMinutes(60));
- timeAdded = tester.addQueryRateMeasurements(application1, cluster1.id(),
- 100,
- t -> 10.0 + (t < 50 ? t * t * t : 125000 - (t - 49) * (t - 49) * (t - 49)));
- tester.clock().advance(timeAdded.negated());
- tester.addCpuMeasurements(0.25f, 1f, 200, application1);
- tester.assertResources("Scale up since observed growth is faster than scaling time",
- 5, 1, 10.0, 100, 100,
- tester.autoscale(application1, cluster1, capacity));
+ var fixture = AutoscalingTester.fixture().build();
+
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ Duration timeAdded = fixture.addLoadMeasurements(100, t -> t == 0 ? 20.0 : 10.0, t -> 0.0);
+ fixture.tester.clock().advance(timeAdded.negated());
+ fixture.addCpuMeasurements(0.25, 200);
+
+ fixture.tester().assertResources("Scale up since we assume we need 2x cpu for growth when no data scaling time data",
+ 9, 1, 2.1, 5, 50,
+ fixture.autoscale());
+
+ fixture.setScalingDuration(Duration.ofMinutes(5));
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ timeAdded = fixture.addLoadMeasurements(100, t -> 10.0 + (t < 50 ? t : 100 - t), t -> 0.0);
+ fixture.tester.clock().advance(timeAdded.negated());
+ fixture.addCpuMeasurements(0.25, 200);
+ fixture.tester().assertResources("Scale down since observed growth is slower than scaling time",
+ 9, 1, 1.8, 5, 50,
+ fixture.autoscale());
+
+ fixture.setScalingDuration(Duration.ofMinutes(60));
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ timeAdded = fixture.addLoadMeasurements(100,
+ t -> 10.0 + (t < 50 ? t * t * t : 125000 - (t - 49) * (t - 49) * (t - 49)),
+ t -> 0.0);
+ fixture.tester.clock().advance(timeAdded.negated());
+ fixture.addCpuMeasurements(0.25, 200);
+ fixture.tester().assertResources("Scale up since observed growth is faster than scaling time",
+ 9, 1, 2.1, 5, 50,
+ fixture.autoscale());
}
@Test
public void test_autoscaling_considers_query_vs_write_rate() {
- NodeResources minResources = new NodeResources( 1, 100, 100, 1);
- NodeResources midResources = new NodeResources( 5, 100, 100, 1);
- 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");
- ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1");
+ var fixture = AutoscalingTester.fixture().build();
- tester.deploy(application1, cluster1, 5, 1, midResources);
- tester.addCpuMeasurements(0.4f, 1f, 120, application1);
+ fixture.addCpuMeasurements(0.4, 220);
// Why twice the query rate at time = 0?
// This makes headroom for queries doubling, which we want to observe the effect of here
- 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 -> 10.0);
- tester.assertResources("Query and write load is equal -> scale up somewhat",
- 5, 1, 7.3, 100, 100,
- tester.autoscale(application1, cluster1, capacity));
-
- 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, capacity));
-
- 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, capacity));
-
- 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, capacity));
-
- 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, capacity));
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ var timeAdded = fixture.addLoadMeasurements(100, t -> t == 0 ? 20.0 : 10.0, t -> 10.0);
+ fixture.tester.clock().advance(timeAdded.negated());
+ fixture.addCpuMeasurements(0.4, 200);
+ fixture.tester.assertResources("Query and write load is equal -> scale up somewhat",
+ 9, 1, 2.4, 5, 50,
+ fixture.autoscale());
+
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ timeAdded = fixture.addLoadMeasurements(100, t -> t == 0 ? 80.0 : 40.0, t -> 10.0);
+ fixture.tester.clock().advance(timeAdded.negated());
+ fixture.addCpuMeasurements(0.4, 200);
+ // TODO: Ackhually, we scale down here - why?
+ fixture.tester().assertResources("Query load is 4x write load -> scale up more",
+ 9, 1, 2.1, 5, 50,
+ fixture.autoscale());
+
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ timeAdded = fixture.addLoadMeasurements(100, t -> t == 0 ? 20.0 : 10.0, t -> 100.0);
+ fixture.tester.clock().advance(timeAdded.negated());
+ fixture.addCpuMeasurements(0.4, 200);
+ fixture.tester().assertResources("Write load is 10x query load -> scale down",
+ 9, 1, 1.1, 5, 50,
+ fixture.autoscale());
+
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ timeAdded = fixture.addLoadMeasurements(100, t -> t == 0 ? 20.0 : 10.0, t-> 0.0);
+ fixture.tester.clock().advance(timeAdded.negated());
+ fixture.addCpuMeasurements(0.4, 200);
+ fixture.tester().assertResources("Query only -> largest possible",
+ 8, 1, 4.9, 5.7, 57.1,
+ fixture.autoscale());
+
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ timeAdded = fixture.addLoadMeasurements(100, t -> 0.0, t -> 10.0);
+ fixture.tester.clock().advance(timeAdded.negated());
+ fixture.addCpuMeasurements(0.4, 200);
+ fixture.tester().assertResources("Write only -> smallest possible",
+ 6, 1, 1.0, 8, 80,
+ fixture.autoscale());
}
@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);
+ var fixture = AutoscalingTester.fixture()
+ .zone(new Zone(Environment.dev, RegionName.from("us-east")))
+ .build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyLoad(1.0, 1.0, 1.0, 200);
assertTrue("Not attempting to scale up because policies dictate we'll only get one node",
- tester.autoscale(application1, cluster1, capacity).target().isEmpty());
+ fixture.autoscale().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, capacity));
+ var requiredCapacity =
+ Capacity.from(new ClusterResources(2, 1,
+ new NodeResources(1, 1, 1, 1, NodeResources.DiskSpeed.any)),
+ new ClusterResources(20, 1,
+ new NodeResources(100, 1000, 1000, 1, NodeResources.DiskSpeed.any)),
+ true,
+ true);
+
+ var fixture = AutoscalingTester.fixture()
+ .capacity(requiredCapacity)
+ .zone(new Zone(Environment.dev, RegionName.from("us-east")))
+ .build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyLoad(1.0, 1.0, 1.0, 200);
+ fixture.tester().assertResources("We scale even in dev because resources are required",
+ 3, 1, 1.0, 7.7, 83.3,
+ fixture.autoscale());
}
@Test
public void test_autoscaling_in_dev_with_required_unspecified_resources() {
- NodeResources resources = NodeResources.unspecified();
- 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,
- new NodeResources(10, 16, 100, 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.5, 8, 50,
- tester.autoscale(application1, cluster1, capacity));
+ var requiredCapacity =
+ Capacity.from(new ClusterResources(1, 1, NodeResources.unspecified()),
+ new ClusterResources(3, 1, NodeResources.unspecified()),
+ true,
+ true);
+
+ var fixture = AutoscalingTester.fixture()
+ .capacity(requiredCapacity)
+ .zone(new Zone(Environment.dev, RegionName.from("us-east")))
+ .build();
+ fixture.tester().clock().advance(Duration.ofDays(2));
+ fixture.applyLoad(1.0, 1.0, 1.0, 200);
+ fixture.tester().assertResources("We scale even in dev because resources are required",
+ 3, 1, 1.5, 8, 50,
+ fixture.autoscale());
}
/**