From 18a5a0d9be2e9ffc29374a0b72aca8ef406c12f1 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Fri, 4 Jun 2021 16:02:05 +0200 Subject: More autoscaling status information --- .../hosted/provision/applications/Application.java | 2 +- .../provision/applications/AutoscalingStatus.java | 69 ++++++++++++++++++++++ .../hosted/provision/applications/Cluster.java | 11 ++-- .../hosted/provision/autoscale/Autoscaler.java | 49 ++++++++++----- .../maintenance/AutoscalingMaintainer.java | 5 +- .../persistence/ApplicationSerializer.java | 44 +++++++++++++- .../hosted/provision/provisioning/Activator.java | 4 +- .../provision/restapi/ApplicationSerializer.java | 3 +- .../hosted/provision/restapi/NodeSerializer.java | 2 - .../provision/autoscale/ClusterModelTest.java | 3 +- .../persistence/ApplicationSerializerTest.java | 5 +- .../provision/restapi/responses/application1.json | 1 + .../provision/restapi/responses/application2.json | 1 + 13 files changed, 164 insertions(+), 35 deletions(-) create mode 100644 node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/AutoscalingStatus.java diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java index 5eb01b4fe72..c8d5e4361a5 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java @@ -62,7 +62,7 @@ public class Application { public Application withCluster(ClusterSpec.Id id, boolean exclusive, ClusterResources min, ClusterResources max) { Cluster cluster = clusters.get(id); if (cluster == null) - cluster = new Cluster(id, exclusive, min, max, Optional.empty(), Optional.empty(), List.of(), ""); + cluster = new Cluster(id, exclusive, min, max, Optional.empty(), Optional.empty(), List.of(), AutoscalingStatus.empty()); else cluster = cluster.withConfiguration(exclusive, min, max); return with(cluster); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/AutoscalingStatus.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/AutoscalingStatus.java new file mode 100644 index 00000000000..c40408c9109 --- /dev/null +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/AutoscalingStatus.java @@ -0,0 +1,69 @@ +package com.yahoo.vespa.hosted.provision.applications; + +import com.yahoo.vespa.hosted.provision.autoscale.Autoscaler; + +import java.util.Objects; + +/** + * The current autoscaling status of a cluster. + * A value object. + * + * @author bratseth + */ +public class AutoscalingStatus { + + public enum Status { + + /** No status is available: Aautoscaling is disabled, or a brand new application. */ + unavailable, + + /** Autoscaling is not taking any action at the moment due to recent changes or a lack of data */ + waiting, + + /** The cluster is ideally scaled to the current load */ + ideal, + + /** The cluster should be rescaled further, but no better configuration is allowed by the current limits */ + insufficient, + + /** Rescaling of this cluster has been scheduled */ + rescaling + + }; + + private final Status status; + private final String description; + + public AutoscalingStatus(Status status, String description) { + this.status = status; + this.description = description; + } + + public Status status() { return status; } + public String description() { return description; } + + public static AutoscalingStatus empty() { return new AutoscalingStatus(Status.unavailable, ""); } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if ( ! ( o instanceof AutoscalingStatus)) return false; + + AutoscalingStatus other = (AutoscalingStatus)o; + if ( other.status != this.status ) return false; + if ( ! other.description.equals(this.description) ) return false; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(status, description); + } + + @Override + public String toString() { + return "autoscaling status: " + status + + ( description.isEmpty() ? "" : " (" + description + ")"); + } + +} diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java index 59b70ff1ef0..d4bbe6adc1b 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java @@ -5,7 +5,6 @@ import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.vespa.hosted.provision.autoscale.Autoscaler; -import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -31,7 +30,7 @@ public class Cluster { /** The maxScalingEvents last scaling events of this, sorted by increasing time (newest last) */ private final List scalingEvents; - private final String autoscalingStatus; + private final AutoscalingStatus autoscalingStatus; public Cluster(ClusterSpec.Id id, boolean exclusive, @@ -40,7 +39,7 @@ public class Cluster { Optional suggestedResources, Optional targetResources, List scalingEvents, - String autoscalingStatus) { + AutoscalingStatus autoscalingStatus) { this.id = Objects.requireNonNull(id); this.exclusive = exclusive; this.min = Objects.requireNonNull(minResources); @@ -95,8 +94,8 @@ public class Cluster { return Optional.of(scalingEvents.get(scalingEvents.size() - 1)); } - /** The latest autoscaling status of this cluster, or empty (never null) if none */ - public String autoscalingStatus() { return autoscalingStatus; } + /** The latest autoscaling status of this cluster, or unknown (never null) if none */ + public AutoscalingStatus autoscalingStatus() { return autoscalingStatus; } public Cluster withConfiguration(boolean exclusive, ClusterResources min, ClusterResources max) { return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus); @@ -124,7 +123,7 @@ public class Cluster { return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus); } - public Cluster withAutoscalingStatus(String autoscalingStatus) { + public Cluster with(AutoscalingStatus autoscalingStatus) { if (autoscalingStatus.equals(this.autoscalingStatus)) return this; return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java index 1d0ba3da6c5..ccaf23c49ed 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Autoscaler.java @@ -7,6 +7,8 @@ import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.applications.Application; +import com.yahoo.vespa.hosted.provision.applications.AutoscalingStatus; +import com.yahoo.vespa.hosted.provision.applications.AutoscalingStatus.Status; import com.yahoo.vespa.hosted.provision.applications.Cluster; import java.time.Duration; @@ -52,7 +54,8 @@ public class Autoscaler { * @return scaling advice for this cluster */ public Advice autoscale(Application application, Cluster cluster, NodeList clusterNodes) { - if (cluster.minResources().equals(cluster.maxResources())) return Advice.none("Autoscaling is not enabled"); + if (cluster.minResources().equals(cluster.maxResources())) + return Advice.none(Status.unavailable, "Autoscaling is not enabled"); return autoscale(application, cluster, clusterNodes, Limits.of(cluster)); } @@ -65,17 +68,20 @@ public class Autoscaler { nodeRepository.clock()); if ( ! clusterIsStable(clusterNodes, nodeRepository)) - return Advice.none("Cluster change in progress"); + return Advice.none(Status.waiting, "Cluster change in progress"); if (scaledIn(clusterModel.scalingDuration(), cluster)) - return Advice.dontScale("Won't autoscale now: Less than " + clusterModel.scalingDuration() + " since last resource change"); + return Advice.dontScale(Status.waiting, + "Won't autoscale now: Less than " + clusterModel.scalingDuration() + " since last resource change"); if (clusterModel.nodeTimeseries().measurementsPerNode() < minimumMeasurementsPerNode(clusterModel.scalingDuration())) - return Advice.none("Collecting more data before making new scaling decisions: Need to measure for " + + return Advice.none(Status.waiting, + "Collecting more data before making new scaling decisions: Need to measure for " + clusterModel.scalingDuration() + " since the last resource change completed"); if (clusterModel.nodeTimeseries().nodesMeasured() != clusterNodes.size()) - return Advice.none("Collecting more data before making new scaling decisions: " + + return Advice.none(Status.waiting, + "Collecting more data before making new scaling decisions: " + "Have measurements from " + clusterModel.nodeTimeseries().nodesMeasured() + " nodes, but require from " + clusterNodes.size()); @@ -85,13 +91,18 @@ public class Autoscaler { Optional bestAllocation = allocationOptimizer.findBestAllocation(target, currentAllocation, clusterModel, limits); if (bestAllocation.isEmpty()) - return Advice.dontScale("No allocation improvements are possible within configured limits"); + return Advice.dontScale(Status.insufficient, "No allocations are possible within configured limits"); - if (similar(bestAllocation.get().realResources(), currentAllocation.realResources())) - return Advice.dontScale("Cluster is ideally scaled within configured limits"); + if (similar(bestAllocation.get().realResources(), currentAllocation.realResources())) { + if (bestAllocation.get().fulfilment() < 1) + return Advice.dontScale(Status.insufficient, "Configured limits prevents better scaling of this cluster"); + else + return Advice.dontScale(Status.ideal, "Cluster is ideally scaled"); + } if (isDownscaling(bestAllocation.get(), currentAllocation) && scaledIn(clusterModel.scalingDuration().multipliedBy(3), cluster)) - return Advice.dontScale("Waiting " + clusterModel.scalingDuration().multipliedBy(3) + + return Advice.dontScale(Status.waiting, + "Waiting " + clusterModel.scalingDuration().multipliedBy(3) + " since the last change before reducing resources"); return Advice.scaleTo(bestAllocation.get().advertisedResources()); @@ -154,9 +165,9 @@ public class Autoscaler { private final boolean present; private final Optional target; - private final String reason; + private final AutoscalingStatus reason; - private Advice(Optional target, boolean present, String reason) { + private Advice(Optional target, boolean present, AutoscalingStatus reason) { this.target = target; this.present = present; this.reason = Objects.requireNonNull(reason); @@ -175,12 +186,20 @@ public class Autoscaler { public boolean isPresent() { return present; } /** The reason for this advice */ - public String reason() { return reason; } + public AutoscalingStatus reason() { return reason; } + + private static Advice none(Status status, String description) { + return new Advice(Optional.empty(), false, new AutoscalingStatus(status, description)); + } + + private static Advice dontScale(Status status, String description) { + return new Advice(Optional.empty(), true, new AutoscalingStatus(status, description)); + } - private static Advice none(String reason) { return new Advice(Optional.empty(), false, reason); } - private static Advice dontScale(String reason) { return new Advice(Optional.empty(), true, reason); } private static Advice scaleTo(ClusterResources target) { - return new Advice(Optional.of(target), true, "Scheduled scaling to " + target + " due to load changes"); + return new Advice(Optional.of(target), true, + new AutoscalingStatus(AutoscalingStatus.Status.rescaling, + "Scheduled scaling to " + target + " due to load changes")); } @Override diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java index 7da6e0d3ebe..05eb878e1b1 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java @@ -14,16 +14,13 @@ import com.yahoo.vespa.hosted.provision.applications.Applications; import com.yahoo.vespa.hosted.provision.applications.Cluster; import com.yahoo.vespa.hosted.provision.autoscale.AllocatableClusterResources; import com.yahoo.vespa.hosted.provision.autoscale.Autoscaler; -import com.yahoo.vespa.hosted.provision.autoscale.MetricsDb; import com.yahoo.vespa.hosted.provision.autoscale.NodeMetricSnapshot; -import com.yahoo.vespa.hosted.provision.autoscale.NodeTimeseries; import com.yahoo.vespa.hosted.provision.node.History; import java.time.Duration; import java.time.Instant; import java.util.Map; import java.util.Optional; -import java.util.Set; /** * Maintainer making automatic scaling decisions @@ -81,7 +78,7 @@ public class AutoscalingMaintainer extends NodeRepositoryMaintainer { // 1. Update cluster info updatedCluster = updateCompletion(cluster.get(), clusterNodes) - .withAutoscalingStatus(advice.reason()) + .with(advice.reason()) .withTarget(advice.target()); applications().put(application.get().with(updatedCluster), lock); if (advice.isPresent() && advice.target().isPresent() && !cluster.get().targetResources().equals(advice.target())) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java index c8b928779b9..e3fb3379da1 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java @@ -10,6 +10,7 @@ import com.yahoo.slime.ObjectTraverser; import com.yahoo.slime.Slime; import com.yahoo.slime.SlimeUtils; import com.yahoo.vespa.hosted.provision.applications.Application; +import com.yahoo.vespa.hosted.provision.applications.AutoscalingStatus; import com.yahoo.vespa.hosted.provision.applications.Cluster; import com.yahoo.vespa.hosted.provision.applications.ScalingEvent; import com.yahoo.vespa.hosted.provision.applications.Status; @@ -55,6 +56,8 @@ public class ApplicationSerializer { private static final String nodeResourcesKey = "resources"; private static final String scalingEventsKey = "scalingEvents"; private static final String autoscalingStatusKey = "autoscalingStatus"; + private static final String autoscalingStatusObjectKey = "autoscalingStatusObject"; + private static final String descriptionKey = "description"; private static final String fromKey = "from"; private static final String toKey = "to"; private static final String generationKey = "generation"; @@ -118,7 +121,8 @@ public class ApplicationSerializer { cluster.suggestedResources().ifPresent(suggested -> toSlime(suggested, clusterObject.setObject(suggestedKey))); cluster.targetResources().ifPresent(target -> toSlime(target, clusterObject.setObject(targetResourcesKey))); scalingEventsToSlime(cluster.scalingEvents(), clusterObject.setArray(scalingEventsKey)); - clusterObject.setString(autoscalingStatusKey, cluster.autoscalingStatus()); + clusterObject.setString(autoscalingStatusKey, cluster.autoscalingStatus().description()); // TODO: Remove after June 2021 + toSlime(cluster.autoscalingStatus(), clusterObject.setObject(autoscalingStatusObjectKey)); } private static Cluster clusterFromSlime(String id, Inspector clusterObject) { @@ -129,7 +133,7 @@ public class ApplicationSerializer { optionalSuggestionFromSlime(clusterObject.field(suggestedKey)), optionalClusterResourcesFromSlime(clusterObject.field(targetResourcesKey)), scalingEventsFromSlime(clusterObject.field(scalingEventsKey)), - clusterObject.field(autoscalingStatusKey).asString()); + autoscalingStatusFromSlime(clusterObject.field(autoscalingStatusObjectKey), clusterObject)); } private static void toSlime(Cluster.Suggestion suggestion, Cursor suggestionObject) { @@ -188,6 +192,42 @@ public class ApplicationSerializer { optionalInstant(inspector.field(completionKey))); } + private static void toSlime(AutoscalingStatus status, Cursor object) { + object.setString(statusKey, toAutoscalingStatusCode(status.status())); + object.setString(descriptionKey, status.description()); + } + + private static AutoscalingStatus autoscalingStatusFromSlime(Inspector object, Inspector parent) { + // TODO: Remove this clause after June 2021 + if ( ! object.valid()) return new AutoscalingStatus(AutoscalingStatus.Status.unavailable, + parent.field(autoscalingStatusKey).asString()); + + return new AutoscalingStatus(fromAutoscalingStatusCode(object.field(statusKey).asString()), + object.field(descriptionKey).asString()); + } + + private static String toAutoscalingStatusCode(AutoscalingStatus.Status status) { + switch (status) { + case unavailable : return "unavailable"; + case waiting : return "waiting"; + case ideal : return "ideal"; + case insufficient : return "insufficient"; + case rescaling : return "rescaling"; + default : throw new IllegalArgumentException("Unknown autoscaling status " + status); + } + } + + private static AutoscalingStatus.Status fromAutoscalingStatusCode(String code) { + switch (code) { + case "unavailable" : return AutoscalingStatus.Status.unavailable; + case "waiting" : return AutoscalingStatus.Status.waiting; + case "ideal" : return AutoscalingStatus.Status.ideal; + case "insufficient" : return AutoscalingStatus.Status.insufficient; + case "rescaling" : return AutoscalingStatus.Status.rescaling; + default : throw new IllegalArgumentException("Unknown autoscaling status '" + code + "'"); + } + } + private static Optional optionalInstant(Inspector inspector) { return inspector.valid() ? Optional.of(Instant.ofEpochMilli(inspector.asLong())) : Optional.empty(); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java index 6d06dc31a42..cb965e87739 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java @@ -13,6 +13,7 @@ import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.NodeMutex; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.applications.Application; +import com.yahoo.vespa.hosted.provision.applications.AutoscalingStatus; import com.yahoo.vespa.hosted.provision.applications.ScalingEvent; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.node.Allocation; @@ -117,7 +118,8 @@ class Activator { } if (cluster.targetResources().isPresent() && cluster.targetResources().get().justNumbers().equals(currentResources.justNumbers())) { - cluster = cluster.withAutoscalingStatus("Cluster is ideally scaled within configured limits"); + cluster = cluster.with(new AutoscalingStatus(AutoscalingStatus.Status.ideal, + "Cluster is ideally scaled within configured limits")); } if (cluster != modified.cluster(clusterEntry.getKey()).get()) modified = modified.with(cluster); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationSerializer.java index 9c6efd9efe6..e800f6c9c84 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationSerializer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/ApplicationSerializer.java @@ -73,7 +73,8 @@ public class ApplicationSerializer { cluster.targetResources().ifPresent(target -> toSlime(target, clusterObject.setObject("target"))); clusterModel.ifPresent(model -> clusterUtilizationToSlime(model, clusterObject.setObject("utilization"))); scalingEventsToSlime(cluster.scalingEvents(), clusterObject.setArray("scalingEvents")); - clusterObject.setString("autoscalingStatus", cluster.autoscalingStatus()); + clusterObject.setString("autoscalingStatusCode", cluster.autoscalingStatus().status().name()); + clusterObject.setString("autoscalingStatus", cluster.autoscalingStatus().description()); clusterModel.ifPresent(model -> clusterObject.setLong("scalingDuration", model.scalingDuration().toMillis())); clusterModel.ifPresent(model -> clusterObject.setDouble("maxQueryGrowthRate", model.maxQueryGrowthRate())); clusterModel.ifPresent(model -> clusterObject.setDouble("currentQueryFractionOfMax", model.queryFractionOfMax())); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodeSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodeSerializer.java index b72d021e4f5..706c36b35ac 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodeSerializer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodeSerializer.java @@ -1,9 +1,7 @@ // Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.provision.restapi; -import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; -import com.yahoo.slime.Cursor; import com.yahoo.vespa.hosted.provision.Node; /** diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java index c6575931c6d..af1bd2aa231 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModelTest.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.NodeResources; import com.yahoo.test.ManualClock; import com.yahoo.vespa.hosted.provision.applications.Application; +import com.yahoo.vespa.hosted.provision.applications.AutoscalingStatus; import com.yahoo.vespa.hosted.provision.applications.Cluster; import com.yahoo.vespa.hosted.provision.applications.Status; import org.junit.Test; @@ -79,7 +80,7 @@ public class ClusterModelTest { Optional.empty(), Optional.empty(), List.of(), - ""); + AutoscalingStatus.empty()); } /** Creates the given number of measurements, spaced 5 minutes between, using the given function */ diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java index 9cac6430d6e..61ff494679f 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializerTest.java @@ -6,6 +6,7 @@ import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.NodeResources; import com.yahoo.vespa.hosted.provision.applications.Application; +import com.yahoo.vespa.hosted.provision.applications.AutoscalingStatus; import com.yahoo.vespa.hosted.provision.applications.Cluster; import com.yahoo.vespa.hosted.provision.applications.ScalingEvent; import com.yahoo.vespa.hosted.provision.applications.Status; @@ -35,7 +36,7 @@ public class ApplicationSerializerTest { Optional.empty(), Optional.empty(), List.of(), - "")); + AutoscalingStatus.empty())); var minResources = new NodeResources(1, 2, 3, 4); clusters.add(new Cluster(ClusterSpec.Id.from("c2"), true, @@ -50,7 +51,7 @@ public class ApplicationSerializerTest { 7L, Instant.ofEpochMilli(12345L), Optional.of(Instant.ofEpochMilli(67890L)))), - "Autoscaling status")); + new AutoscalingStatus(AutoscalingStatus.Status.insufficient, "Autoscaling status"))); Application original = new Application(ApplicationId.from("myTenant", "myApplication", "myInstance"), Status.initial().withCurrentReadShare(0.3).withMaxReadShare(0.5), clusters); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application1.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application1.json index 1083930e294..ca0c548c1be 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application1.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application1.json @@ -104,6 +104,7 @@ "at" : 123 } ], + "autoscalingStatusCode": "unavailable", "autoscalingStatus": "", "scalingDuration": 600000, "maxQueryGrowthRate": 0.1, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application2.json index 61e0569d349..c7eaa4af974 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application2.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/application2.json @@ -80,6 +80,7 @@ "at" : 123 } ], + "autoscalingStatusCode": "unavailable", "autoscalingStatus" : "", "scalingDuration": 43200000, "maxQueryGrowthRate": 0.1, -- cgit v1.2.3 From 2aaead7fd8822caf1984a8c5efe5ff4bb4292838 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Fri, 4 Jun 2021 16:17:00 +0200 Subject: Propagate autoscalingStatusCode --- .../vespa/hosted/controller/api/integration/configserver/Cluster.java | 4 ++++ .../hosted/controller/api/integration/noderepository/ClusterData.java | 3 +++ .../hosted/controller/restapi/application/ApplicationApiHandler.java | 1 + 3 files changed, 8 insertions(+) diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java index b3317c7f268..ccfd3241810 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java @@ -23,6 +23,7 @@ public class Cluster { private final Optional suggested; private final Utilization utilization; private final List scalingEvents; + private final String autoscalingStatusCode; private final String autoscalingStatus; private final Duration scalingDuration; private final double maxQueryGrowthRate; @@ -37,6 +38,7 @@ public class Cluster { Optional suggested, Utilization utilization, List scalingEvents, + String autoscalingStatusCode, String autoscalingStatus, Duration scalingDuration, double maxQueryGrowthRate, @@ -50,6 +52,7 @@ public class Cluster { this.suggested = suggested; this.utilization = utilization; this.scalingEvents = scalingEvents; + this.autoscalingStatusCode = autoscalingStatusCode; this.autoscalingStatus = autoscalingStatus; this.scalingDuration = scalingDuration; this.maxQueryGrowthRate = maxQueryGrowthRate; @@ -65,6 +68,7 @@ public class Cluster { public Optional suggested() { return suggested; } public Utilization utilization() { return utilization; } public List scalingEvents() { return scalingEvents; } + public String autoscalingStatusCode() { return autoscalingStatusCode; } public String autoscalingStatus() { return autoscalingStatus; } public Duration scalingDuration() { return scalingDuration; } public double maxQueryGrowthRate() { return maxQueryGrowthRate; } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java index b6163809f26..6f9b2b496bf 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java @@ -35,6 +35,8 @@ public class ClusterData { public ClusterUtilizationData utilization; @JsonProperty("scalingEvents") public List scalingEvents; + @JsonProperty("autoscalingStatusCode") + public String autoscalingStatusCode; @JsonProperty("autoscalingStatus") public String autoscalingStatus; @JsonProperty("scalingDuration") @@ -55,6 +57,7 @@ public class ClusterData { utilization == null ? Cluster.Utilization.empty() : utilization.toClusterUtilization(), scalingEvents == null ? List.of() : scalingEvents.stream().map(data -> data.toScalingEvent()).collect(Collectors.toList()), + autoscalingStatusCode, autoscalingStatus, scalingDuration == null ? Duration.ofMillis(0) : Duration.ofMillis(scalingDuration), maxQueryGrowthRate == null ? -1 : maxQueryGrowthRate, diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index d7b805c0949..d1cbe8e14b4 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -901,6 +901,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { cluster.suggested().ifPresent(suggested -> toSlime(suggested, clusterObject.setObject("suggested"))); utilizationToSlime(cluster.utilization(), clusterObject.setObject("utilization")); scalingEventsToSlime(cluster.scalingEvents(), clusterObject.setArray("scalingEvents")); + clusterObject.setString("autoscalingStatusCode", cluster.autoscalingStatusCode()); clusterObject.setString("autoscalingStatus", cluster.autoscalingStatus()); clusterObject.setLong("scalingDuration", cluster.scalingDuration().toMillis()); clusterObject.setDouble("maxQueryGrowthRate", cluster.maxQueryGrowthRate()); -- cgit v1.2.3 From a90ef9a25f43c4bf44c8b7757387ba7e3f2b3dc6 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Fri, 4 Jun 2021 16:28:17 +0200 Subject: Update tests --- .../yahoo/vespa/hosted/controller/integration/ConfigServerMock.java | 3 ++- .../controller/restapi/application/responses/application-clusters.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java index c3a527a0bd9..4203051965b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java @@ -122,7 +122,8 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer current, Instant.ofEpochMilli(1234), Optional.of(Instant.ofEpochMilli(2234)))), - "the autoscaling status", + "ideal", + "Cluster is ideally scaled", Duration.ofMinutes(6), 0.7, 0.3); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json index cd1fe5acf6a..fc40a9ce692 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json @@ -97,7 +97,8 @@ "completion": 2234 } ], - "autoscalingStatus": "the autoscaling status", + "autoscalingStatusCode": "ideal", + "autoscalingStatus": "Cluster is ideally scaled", "scalingDuration": 360000, "maxQueryGrowthRate": 0.7, "currentQueryFractionOfMax":0.3 -- cgit v1.2.3