diff options
author | Martin Polden <mpolden@mpolden.no> | 2020-11-27 11:46:17 +0100 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2020-12-01 10:50:16 +0100 |
commit | fa2196703ab3587fcda0735aeb4f9c2aefe55156 (patch) | |
tree | 89c96381c7afe1f7d45c8178f6eb60785d59808d | |
parent | 1deec4e9bf50ff882e39079ae61114bbefaa4b6f (diff) |
Use stateful property
6 files changed, 32 insertions, 35 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java index b1213b2da41..9eb4b796970 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 @@ -26,20 +26,19 @@ public class AllocatableClusterResources { private final NodeResources realResources; private final NodeResources advertisedResources; - private final ClusterSpec.Type clusterType; + private final ClusterSpec clusterSpec; private final double fulfilment; /** Fake allocatable resources from requested capacity */ public AllocatableClusterResources(ClusterResources requested, - ClusterSpec.Type clusterType, - boolean exclusive, + ClusterSpec clusterSpec, NodeRepository nodeRepository) { this.nodes = requested.nodes(); this.groups = requested.groups(); - this.realResources = nodeRepository.resourcesCalculator().requestToReal(requested.nodeResources(), exclusive); + this.realResources = nodeRepository.resourcesCalculator().requestToReal(requested.nodeResources(), clusterSpec.isExclusive()); this.advertisedResources = requested.nodeResources(); - this.clusterType = clusterType; + this.clusterSpec = clusterSpec; this.fulfilment = 1; } @@ -48,19 +47,19 @@ public class AllocatableClusterResources { this.groups = (int)nodes.stream().map(node -> node.allocation().get().membership().cluster().group()).distinct().count(); this.realResources = averageRealResourcesOf(nodes, nodeRepository, exclusive); // Average since we average metrics over nodes this.advertisedResources = nodes.get(0).resources(); - this.clusterType = nodes.get(0).allocation().get().membership().cluster().type(); + this.clusterSpec = nodes.get(0).allocation().get().membership().cluster(); this.fulfilment = 1; } public AllocatableClusterResources(ClusterResources realResources, NodeResources advertisedResources, NodeResources idealResources, - ClusterSpec.Type clusterType) { + ClusterSpec clusterSpec) { this.nodes = realResources.nodes(); this.groups = realResources.groups(); this.realResources = realResources.nodeResources(); this.advertisedResources = advertisedResources; - this.clusterType = clusterType; + this.clusterSpec = clusterSpec; this.fulfilment = fulfilment(realResources.nodeResources(), idealResources); } @@ -88,7 +87,7 @@ public class AllocatableClusterResources { return (int)Math.ceil((double)nodes / groups); } - public ClusterSpec.Type clusterType() { return clusterType; } + public ClusterSpec clusterSpec() { return clusterSpec; } public double cost() { return nodes * advertisedResources.cost(); } @@ -133,23 +132,23 @@ public class AllocatableClusterResources { } public static Optional<AllocatableClusterResources> from(ClusterResources wantedResources, - boolean exclusive, - ClusterSpec.Type clusterType, + ClusterSpec clusterSpec, Limits applicationLimits, NodeRepository nodeRepository) { var systemLimits = new NodeResourceLimits(nodeRepository); - if ( !exclusive && !nodeRepository.zone().getCloud().dynamicProvisioning()) { + boolean exclusive = clusterSpec.isExclusive(); + 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 NodeResources advertisedResources = nodeRepository.resourcesCalculator().realToRequest(wantedResources.nodeResources(), exclusive); - advertisedResources = systemLimits.enlargeToLegal(advertisedResources, clusterType, exclusive); // Attempt to ask for something legal + advertisedResources = systemLimits.enlargeToLegal(advertisedResources, clusterSpec.type(), exclusive); // Attempt to ask for something legal advertisedResources = applicationLimits.cap(advertisedResources); // Overrides other conditions, even if it will then fail NodeResources realResources = nodeRepository.resourcesCalculator().requestToReal(advertisedResources, exclusive); // ... thus, what we really get may change - if ( ! systemLimits.isWithinRealLimits(realResources, clusterType)) return Optional.empty(); + if ( ! systemLimits.isWithinRealLimits(realResources, clusterSpec.type())) return Optional.empty(); if (matchesAny(nodeRepository.flavors().getFlavors(), advertisedResources)) return Optional.of(new AllocatableClusterResources(wantedResources.with(realResources), advertisedResources, wantedResources.nodeResources(), - clusterType)); + clusterSpec)); else return Optional.empty(); } @@ -172,11 +171,11 @@ public class AllocatableClusterResources { } if ( ! between(applicationLimits.min().nodeResources(), applicationLimits.max().nodeResources(), advertisedResources)) continue; - if ( ! systemLimits.isWithinRealLimits(realResources, clusterType)) continue; + if ( ! systemLimits.isWithinRealLimits(realResources, clusterSpec.type())) continue; var candidate = new AllocatableClusterResources(wantedResources.with(realResources), advertisedResources, wantedResources.nodeResources(), - clusterType); + 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 e57011b0e4a..fb97e803a35 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 @@ -58,7 +58,7 @@ public class AllocationOptimizer { groups, nodeResourcesWith(nodesAdjustedForRedundancy, groupsAdjustedForRedundancy, limits, current, target)); - var allocatableResources = AllocatableClusterResources.from(next, exclusive, current.clusterType(), limits, nodeRepository); + var allocatableResources = AllocatableClusterResources.from(next, current.clusterSpec(), limits, nodeRepository); if (allocatableResources.isEmpty()) continue; if (bestAllocation.isEmpty() || allocatableResources.get().preferableTo(bestAllocation.get())) bestAllocation = allocatableResources; @@ -79,7 +79,7 @@ public class AllocationOptimizer { int groupSize = nodes / groups; - if (current.clusterType().isContent()) { // load scales with node share of content + if (current.clusterSpec().isStateful()) { // load scales with node share of content // The fixed cost portion of cpu does not scale with changes to the node count // TODO: Only for the portion of cpu consumed by queries double cpuPerGroup = fixedCpuCostFraction * target.nodeCpu() + 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 023eb5860ee..8fcba452d26 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 @@ -59,7 +59,7 @@ public class Autoscaler { } private Advice autoscale(Cluster cluster, List<Node> clusterNodes, Limits limits, boolean exclusive) { - ClusterSpec.Type clusterType = clusterNodes.get(0).allocation().get().membership().cluster().type(); + ClusterSpec clusterSpec = clusterNodes.get(0).allocation().get().membership().cluster(); if ( ! stable(clusterNodes, nodeRepository)) return Advice.none("Cluster change in progress"); @@ -69,7 +69,7 @@ public class Autoscaler { ClusterTimeseries clusterTimeseries = new ClusterTimeseries(cluster, clusterNodes, metricsDb, nodeRepository); int measurementsPerNode = clusterTimeseries.measurementsPerNode(); - if (measurementsPerNode < minimumMeasurementsPerNode(clusterType)) + if (measurementsPerNode < minimumMeasurementsPerNode(clusterSpec)) return Advice.none("Collecting more data before making new scaling decisions" + ": Has " + measurementsPerNode + " data points per node" + "(all: " + clusterTimeseries.measurementCount + @@ -124,14 +124,14 @@ public class Autoscaler { } private boolean recentlyScaled(Cluster cluster, List<Node> clusterNodes) { - Duration downscalingDelay = downscalingDelay(clusterNodes.get(0).allocation().get().membership().cluster().type()); + Duration downscalingDelay = downscalingDelay(clusterNodes.get(0).allocation().get().membership().cluster()); return cluster.lastScalingEvent().map(event -> event.at()).orElse(Instant.MIN) .isAfter(nodeRepository.clock().instant().minus(downscalingDelay)); } /** The duration of the window we need to consider to make a scaling decision. See also minimumMeasurementsPerNode */ - static Duration scalingWindow(ClusterSpec.Type clusterType) { - if (clusterType.isContent()) return Duration.ofHours(12); + static Duration scalingWindow(ClusterSpec cluster) { + if (cluster.isStateful()) return Duration.ofHours(12); return Duration.ofMinutes(30); } @@ -140,8 +140,8 @@ public class Autoscaler { } /** Measurements are currently taken once a minute. See also scalingWindow */ - static int minimumMeasurementsPerNode(ClusterSpec.Type clusterType) { - if (clusterType.isContent()) return 60; + static int minimumMeasurementsPerNode(ClusterSpec cluster) { + if (cluster.isStateful()) return 60; return 7; } @@ -149,8 +149,8 @@ public class Autoscaler { * We should wait a while before scaling down after a scaling event as a peak in usage * indicates more peaks may arrive in the near future. */ - static Duration downscalingDelay(ClusterSpec.Type clusterType) { - if (clusterType.isContent()) return Duration.ofHours(12); + static Duration downscalingDelay(ClusterSpec cluster) { + if (cluster.isStateful()) return Duration.ofHours(12); return Duration.ofHours(1); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterTimeseries.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterTimeseries.java index 1983162f121..2b4ba3fbbcb 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterTimeseries.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterTimeseries.java @@ -7,7 +7,6 @@ import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.applications.Cluster; import java.time.Instant; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,8 +32,8 @@ public class ClusterTimeseries { public ClusterTimeseries(Cluster cluster, List<Node> clusterNodes, MetricsDb db, NodeRepository nodeRepository) { this.clusterNodes = clusterNodes; - ClusterSpec.Type clusterType = clusterNodes.get(0).allocation().get().membership().cluster().type(); - var timeseries = db.getNodeTimeseries(nodeRepository.clock().instant().minus(Autoscaler.scalingWindow(clusterType)), + ClusterSpec clusterSpec = clusterNodes.get(0).allocation().get().membership().cluster(); + var timeseries = db.getNodeTimeseries(nodeRepository.clock().instant().minus(Autoscaler.scalingWindow(clusterSpec)), clusterNodes.stream().map(Node::hostname).collect(Collectors.toSet())); Map<String, Instant> startTimePerNode = metricStartTimes(cluster, clusterNodes, timeseries, nodeRepository); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java index 68e11c4c995..bc164dc37e0 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java @@ -203,7 +203,7 @@ class NodeAllocation { * Such nodes will be marked retired during finalization of the list of accepted nodes. * The conditions for this are: * - * This is a content or combined node. These must always be retired before being removed to allow the cluster to + * This is a stateful node. These must always be retired before being removed to allow the cluster to * migrate away data. * * This is a container node and it is not desired due to having the wrong flavor. In this case this @@ -218,7 +218,7 @@ class NodeAllocation { if (candidate.allocation().get().membership().retired()) return true; // don't second-guess if already retired if (! requestedNodes.considerRetiring()) return false; - return cluster.type().isContent() || + return cluster.isStateful() || (cluster.type() == ClusterSpec.Type.container && !hasCompatibleFlavor(candidate)); } 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 ede6f4ef250..a6d68243160 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 @@ -29,7 +29,6 @@ import com.yahoo.vespa.hosted.provision.autoscale.AllocatableClusterResources; import com.yahoo.vespa.hosted.provision.autoscale.AllocationOptimizer; import com.yahoo.vespa.hosted.provision.autoscale.Limits; import com.yahoo.vespa.hosted.provision.autoscale.ResourceTarget; -import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.node.Allocation; import com.yahoo.vespa.hosted.provision.node.filter.ApplicationFilter; import com.yahoo.vespa.hosted.provision.node.filter.NodeHostFilter; @@ -168,7 +167,7 @@ public class NodeRepositoryProvisioner implements Provisioner { boolean firstDeployment = nodes.isEmpty(); AllocatableClusterResources currentResources = firstDeployment // start at min, preserve current resources otherwise - ? new AllocatableClusterResources(requested.minResources(), clusterSpec.type(), clusterSpec.isExclusive(), nodeRepository) + ? new AllocatableClusterResources(requested.minResources(), clusterSpec, nodeRepository) : new AllocatableClusterResources(nodes, nodeRepository, clusterSpec.isExclusive()); return within(Limits.of(requested), clusterSpec.isExclusive(), currentResources, firstDeployment); } |