From 612cba5886325c1b6ed627d7ca35fa2a2511e625 Mon Sep 17 00:00:00 2001 From: Harald Musum Date: Tue, 30 Nov 2021 17:03:07 +0100 Subject: Revert "Account for capacity policies when autoscaling" --- .../hosted/provision/applications/Application.java | 9 +++--- .../hosted/provision/applications/Cluster.java | 33 ++++++---------------- .../autoscale/AllocatableClusterResources.java | 26 +++++++---------- .../provision/autoscale/AllocationOptimizer.java | 4 +-- .../hosted/provision/autoscale/ClusterModel.java | 5 ---- .../persistence/ApplicationSerializer.java | 4 +-- .../provision/provisioning/CapacityPolicies.java | 12 ++++---- .../provisioning/NodeRepositoryProvisioner.java | 10 ++----- 8 files changed, 34 insertions(+), 69 deletions(-) (limited to 'node-repository/src/main/java/com/yahoo/vespa/hosted/provision') 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 df5044de05c..3c2ab8cead2 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Application.java @@ -2,9 +2,10 @@ package com.yahoo.vespa.hosted.provision.applications; import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.Optional; @@ -58,12 +59,12 @@ public class Application { * Returns an application with the given cluster having the min and max resource limits of the given cluster. * If the cluster has a target which is not inside the new limits, the target is removed. */ - public Application withCluster(ClusterSpec.Id id, boolean exclusive, Capacity requested) { + public Application withCluster(ClusterSpec.Id id, boolean exclusive, ClusterResources min, ClusterResources max) { Cluster cluster = clusters.get(id); if (cluster == null) - cluster = Cluster.create(id, exclusive, requested); + cluster = new Cluster(id, exclusive, min, max, Optional.empty(), Optional.empty(), List.of(), AutoscalingStatus.empty()); else - cluster = cluster.withConfiguration(exclusive, requested); + cluster = cluster.withConfiguration(exclusive, min, max); return with(cluster); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java index e0ccbe10b10..5478999e4fe 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/applications/Cluster.java @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.provision.applications; -import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.vespa.hosted.provision.autoscale.Autoscaler; @@ -26,7 +25,6 @@ public class Cluster { private final ClusterSpec.Id id; private final boolean exclusive; private final ClusterResources min, max; - private boolean required; private final Optional suggested; private final Optional target; @@ -38,7 +36,6 @@ public class Cluster { boolean exclusive, ClusterResources minResources, ClusterResources maxResources, - boolean required, Optional suggestedResources, Optional targetResources, List scalingEvents, @@ -47,7 +44,6 @@ public class Cluster { this.exclusive = exclusive; this.min = Objects.requireNonNull(minResources); this.max = Objects.requireNonNull(maxResources); - this.required = required; this.suggested = Objects.requireNonNull(suggestedResources); Objects.requireNonNull(targetResources); if (targetResources.isPresent() && ! targetResources.get().isWithin(minResources, maxResources)) @@ -60,20 +56,14 @@ public class Cluster { public ClusterSpec.Id id() { return id; } - /** Returns whether the nodes allocated to this cluster must be on host exclusively dedicated to this application */ - public boolean exclusive() { return exclusive; } - /** Returns the configured minimal resources in this cluster */ public ClusterResources minResources() { return min; } /** Returns the configured maximal resources in this cluster */ public ClusterResources maxResources() { return max; } - /** - * Returns whether the resources of this cluster are required to be within the specified min and max. - * Otherwise they may be adjusted by capacity policies. - */ - public boolean required() { return required; } + /** Returns whether the nodes allocated to this cluster must be on host exclusively dedicated to this application */ + public boolean exclusive() { return exclusive; } /** * Returns the computed resources (between min and max, inclusive) this cluster should @@ -107,18 +97,16 @@ public class Cluster { /** The latest autoscaling status of this cluster, or unknown (never null) if none */ public AutoscalingStatus autoscalingStatus() { return autoscalingStatus; } - public Cluster withConfiguration(boolean exclusive, Capacity capacity) { - return new Cluster(id, exclusive, - capacity.minResources(), capacity.maxResources(), capacity.isRequired(), - suggested, target, scalingEvents, autoscalingStatus); + public Cluster withConfiguration(boolean exclusive, ClusterResources min, ClusterResources max) { + return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus); } public Cluster withSuggested(Optional suggested) { - return new Cluster(id, exclusive, min, max, required, suggested, target, scalingEvents, autoscalingStatus); + return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus); } public Cluster withTarget(Optional target) { - return new Cluster(id, exclusive, min, max, required, suggested, target, scalingEvents, autoscalingStatus); + return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus); } /** Add or update (based on "at" time) a scaling event */ @@ -132,12 +120,12 @@ public class Cluster { scalingEvents.add(scalingEvent); prune(scalingEvents); - return new Cluster(id, exclusive, min, max, required, suggested, target, scalingEvents, autoscalingStatus); + return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus); } public Cluster with(AutoscalingStatus autoscalingStatus) { if (autoscalingStatus.equals(this.autoscalingStatus)) return this; - return new Cluster(id, exclusive, min, max, required, suggested, target, scalingEvents, autoscalingStatus); + return new Cluster(id, exclusive, min, max, suggested, target, scalingEvents, autoscalingStatus); } @Override @@ -168,11 +156,6 @@ public class Cluster { return -1; } - public static Cluster create(ClusterSpec.Id id, boolean exclusive, Capacity requested) { - return new Cluster(id, exclusive, requested.minResources(), requested.maxResources(), requested.isRequired(), - Optional.empty(), Optional.empty(), List.of(), AutoscalingStatus.empty()); - } - public static class Suggestion { private final ClusterResources resources; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java index 078b0621a99..f1e707be7b4 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java @@ -8,7 +8,6 @@ import com.yahoo.config.provision.NodeResources; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.NodeRepository; -import com.yahoo.vespa.hosted.provision.provisioning.CapacityPolicies; import com.yahoo.vespa.hosted.provision.provisioning.NodeResourceLimits; import java.util.List; @@ -55,14 +54,14 @@ public class AllocatableClusterResources { public AllocatableClusterResources(ClusterResources realResources, NodeResources advertisedResources, - ClusterResources idealResources, + NodeResources idealResources, ClusterSpec clusterSpec) { this.nodes = realResources.nodes(); this.groups = realResources.groups(); this.realResources = realResources.nodeResources(); this.advertisedResources = advertisedResources; this.clusterSpec = clusterSpec; - this.fulfilment = fulfilment(realResources, idealResources); + this.fulfilment = fulfilment(realResources.nodeResources(), idealResources); } /** @@ -100,10 +99,10 @@ public class AllocatableClusterResources { */ public double fulfilment() { return fulfilment; } - private static double fulfilment(ClusterResources realResources, ClusterResources idealResources) { - double vcpuFulfilment = Math.min(1, realResources.totalResources().vcpu() / idealResources.totalResources().vcpu()); - double memoryGbFulfilment = Math.min(1, realResources.totalResources().memoryGb() / idealResources.totalResources().memoryGb()); - double diskGbFulfilment = Math.min(1, realResources.totalResources().diskGb() / idealResources.totalResources().diskGb()); + private static double fulfilment(NodeResources realResources, NodeResources idealResources) { + double vcpuFulfilment = Math.min(1, realResources.vcpu() / idealResources.vcpu()); + double memoryGbFulfilment = Math.min(1, realResources.memoryGb() / idealResources.memoryGb()); + double diskGbFulfilment = Math.min(1, realResources.diskGb() / idealResources.diskGb()); return (vcpuFulfilment + memoryGbFulfilment + diskGbFulfilment) / 3; } @@ -139,25 +138,21 @@ public class AllocatableClusterResources { public static Optional from(ClusterResources wantedResources, ClusterSpec clusterSpec, Limits applicationLimits, - boolean required, NodeList hosts, NodeRepository nodeRepository) { - var capacityPolicies = new CapacityPolicies(nodeRepository); var systemLimits = new NodeResourceLimits(nodeRepository); boolean exclusive = clusterSpec.isExclusive(); - int actualNodes = capacityPolicies.decideSize(wantedResources.nodes(), required, true, false, clusterSpec); if ( !clusterSpec.isExclusive() && !nodeRepository.zone().getCloud().dynamicProvisioning()) { // We decide resources: Add overhead to what we'll request (advertised) to make sure real becomes (at least) cappedNodeResources var advertisedResources = nodeRepository.resourcesCalculator().realToRequest(wantedResources.nodeResources(), exclusive); advertisedResources = systemLimits.enlargeToLegal(advertisedResources, clusterSpec.type(), exclusive); // Ask for something legal advertisedResources = applicationLimits.cap(advertisedResources); // Overrides other conditions, even if it will then fail - advertisedResources = capacityPolicies.decideNodeResources(advertisedResources, required, clusterSpec); // Adjust to what we can request var realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, exclusive); // What we'll really get if ( ! systemLimits.isWithinRealLimits(realResources, clusterSpec.type())) return Optional.empty(); if (matchesAny(hosts, advertisedResources)) - return Optional.of(new AllocatableClusterResources(wantedResources.withNodes(actualNodes).with(realResources), + return Optional.of(new AllocatableClusterResources(wantedResources.with(realResources), advertisedResources, - wantedResources, + wantedResources.nodeResources(), clusterSpec)); else return Optional.empty(); @@ -168,7 +163,6 @@ public class AllocatableClusterResources { for (Flavor flavor : nodeRepository.flavors().getFlavors()) { // Flavor decide resources: Real resources are the worst case real resources we'll get if we ask for these advertised resources NodeResources advertisedResources = nodeRepository.resourcesCalculator().advertisedResourcesOf(flavor); - advertisedResources = capacityPolicies.decideNodeResources(advertisedResources, required, clusterSpec); // Adjust to what we can get NodeResources realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, exclusive); // Adjust where we don't need exact match to the flavor @@ -184,9 +178,9 @@ public class AllocatableClusterResources { if ( ! between(applicationLimits.min().nodeResources(), applicationLimits.max().nodeResources(), advertisedResources)) continue; if ( ! systemLimits.isWithinRealLimits(realResources, clusterSpec.type())) continue; - var candidate = new AllocatableClusterResources(wantedResources.withNodes(actualNodes).with(realResources), + var candidate = new AllocatableClusterResources(wantedResources.with(realResources), advertisedResources, - wantedResources, + wantedResources.nodeResources(), clusterSpec); if (best.isEmpty() || candidate.preferableTo(best.get())) best = Optional.of(candidate); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java index b8a80a9bd2b..6fd9801164a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java @@ -65,9 +65,7 @@ public class AllocationOptimizer { nodeResourcesWith(nodesAdjustedForRedundancy, groupsAdjustedForRedundancy, limits, target, current, clusterModel)); - var allocatableResources = AllocatableClusterResources.from(next, current.clusterSpec(), limits, - clusterModel.cluster().required(), - hosts, nodeRepository); + var allocatableResources = AllocatableClusterResources.from(next, current.clusterSpec(), limits, hosts, nodeRepository); if (allocatableResources.isEmpty()) continue; if (bestAllocation.isEmpty() || allocatableResources.get().preferableTo(bestAllocation.get())) diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java index 1001ab83cc0..35aafd3e0f4 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java @@ -32,7 +32,6 @@ public class ClusterModel { static final double idealDiskLoad = 0.6; private final Application application; - private final Cluster cluster; /** The current nodes of this cluster, or empty if this models a new cluster not yet deployed */ private final NodeList nodes; private final Clock clock; @@ -51,7 +50,6 @@ public class ClusterModel { MetricsDb metricsDb, Clock clock) { this.application = application; - this.cluster = cluster; this.nodes = clusterNodes; this.clock = clock; this.scalingDuration = computeScalingDuration(cluster, clusterSpec); @@ -67,7 +65,6 @@ public class ClusterModel { ClusterTimeseries clusterTimeseries, ClusterNodesTimeseries nodeTimeseries) { this.application = application; - this.cluster = cluster; this.nodes = null; this.clock = clock; @@ -76,8 +73,6 @@ public class ClusterModel { this.nodeTimeseries = nodeTimeseries; } - public Cluster cluster() { return cluster; } - /** Returns the predicted duration of a rescaling of this cluster */ public Duration scalingDuration() { return scalingDuration; } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java index 2289ba4a0ea..9cdb4c69b97 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/ApplicationSerializer.java @@ -48,7 +48,6 @@ public class ApplicationSerializer { private static final String exclusiveKey = "exclusive"; private static final String minResourcesKey = "min"; private static final String maxResourcesKey = "max"; - private static final String requiredKey = "required"; private static final String suggestedKey = "suggested"; private static final String resourcesKey = "resources"; private static final String targetResourcesKey = "target"; @@ -100,6 +99,7 @@ public class ApplicationSerializer { } private static Status statusFromSlime(Inspector statusObject) { + if ( ! statusObject.valid()) return Status.initial(); // TODO: Remove this line after March 2021 return new Status(statusObject.field(currentReadShareKey).asDouble(), statusObject.field(maxReadShareKey).asDouble()); } @@ -118,7 +118,6 @@ public class ApplicationSerializer { clusterObject.setBool(exclusiveKey, cluster.exclusive()); toSlime(cluster.minResources(), clusterObject.setObject(minResourcesKey)); toSlime(cluster.maxResources(), clusterObject.setObject(maxResourcesKey)); - clusterObject.setBool(requiredKey, cluster.required()); cluster.suggestedResources().ifPresent(suggested -> toSlime(suggested, clusterObject.setObject(suggestedKey))); cluster.targetResources().ifPresent(target -> toSlime(target, clusterObject.setObject(targetResourcesKey))); scalingEventsToSlime(cluster.scalingEvents(), clusterObject.setArray(scalingEventsKey)); @@ -131,7 +130,6 @@ public class ApplicationSerializer { clusterObject.field(exclusiveKey).asBool(), clusterResourcesFromSlime(clusterObject.field(minResourcesKey)), clusterResourcesFromSlime(clusterObject.field(maxResourcesKey)), - clusterObject.field(requiredKey).asBool(), optionalSuggestionFromSlime(clusterObject.field(suggestedKey)), optionalClusterResourcesFromSlime(clusterObject.field(targetResourcesKey)), scalingEventsFromSlime(clusterObject.field(scalingEventsKey)), diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java index 0c2c3c48df1..839bc21827c 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java @@ -29,11 +29,11 @@ public class CapacityPolicies { this.sharedHosts = type -> PermanentFlags.SHARED_HOST.bindTo(nodeRepository.flagSource()).value().isEnabled(type.name()); } - public int decideSize(int requested, boolean required, boolean canFail, boolean isTester, ClusterSpec cluster) { - if (isTester) return 1; + public int decideSize(int requested, Capacity capacity, ClusterSpec cluster, ApplicationId application) { + if (application.instance().isTester()) return 1; - ensureRedundancy(requested, cluster, canFail); - if (required) return requested; + ensureRedundancy(requested, cluster, capacity.canFail()); + if (capacity.isRequired()) return requested; switch(zone.environment()) { case dev : case test : return 1; case perf : return Math.min(requested, 3); @@ -43,11 +43,11 @@ public class CapacityPolicies { } } - public NodeResources decideNodeResources(NodeResources target, boolean required, ClusterSpec cluster) { + public NodeResources decideNodeResources(NodeResources target, Capacity capacity, ClusterSpec cluster) { if (target.isUnspecified()) target = defaultNodeResources(cluster.type()); - if (required) return target; + if (capacity.isRequired()) return target; // Dev does not cap the cpu or network of containers since usage is spotty: Allocate just a small amount exclusively if (zone.environment() == Environment.dev && !zone.getCloud().dynamicProvisioning()) diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java index b35b0a5e301..0ab04a1a73d 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java @@ -97,13 +97,9 @@ public class NodeRepositoryProvisioner implements Provisioner { NodeSpec nodeSpec; if (requested.type() == NodeType.tenant) { ClusterResources target = decideTargetResources(application, cluster, requested); - int nodeCount = capacityPolicies.decideSize(target.nodes(), - requested.isRequired(), - requested.canFail(), - application.instance().isTester(), - cluster); + int nodeCount = capacityPolicies.decideSize(target.nodes(), requested, cluster, application); groups = Math.min(target.groups(), nodeCount); // cannot have more groups than nodes - resources = capacityPolicies.decideNodeResources(target.nodeResources(), requested.isRequired(), cluster); + resources = capacityPolicies.decideNodeResources(target.nodeResources(), requested, cluster); boolean exclusive = capacityPolicies.decideExclusivity(requested, cluster.isExclusive()); nodeSpec = NodeSpec.from(nodeCount, resources, exclusive, requested.canFail()); logIfDownscaled(target.nodes(), nodeCount, cluster, logger); @@ -145,7 +141,7 @@ public class NodeRepositoryProvisioner implements Provisioner { private ClusterResources decideTargetResources(ApplicationId applicationId, ClusterSpec clusterSpec, Capacity requested) { try (Mutex lock = nodeRepository.nodes().lock(applicationId)) { var application = nodeRepository.applications().get(applicationId).orElse(Application.empty(applicationId)) - .withCluster(clusterSpec.id(), clusterSpec.isExclusive(), requested); + .withCluster(clusterSpec.id(), clusterSpec.isExclusive(), requested.minResources(), requested.maxResources()); nodeRepository.applications().put(application, lock); var cluster = application.cluster(clusterSpec.id()).get(); return cluster.targetResources().orElseGet(() -> currentResources(application, clusterSpec, cluster, requested)); -- cgit v1.2.3