diff options
author | Valerij Fredriksen <freva@users.noreply.github.com> | 2021-03-24 12:23:48 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-24 12:23:48 +0100 |
commit | a75edf56f1aa4279f2c46b6c32d28741f63386af (patch) | |
tree | 57f099a4d0f7841fd37bf5aa0876f2b0f9b9b805 | |
parent | 6087d30ecabf7c80a9c1df8b74fe0b7873c9a70a (diff) | |
parent | 02f56edcb96817ed95462935f85428d6036d5514 (diff) |
Merge pull request #17147 from vespa-engine/bratseth/application-stats
Bratseth/application stats
20 files changed, 311 insertions, 221 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java index 7f563b876a7..0548bc7520f 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java @@ -316,4 +316,6 @@ public class NodeResources { return value; } + public static NodeResources zero() { return new NodeResources(0, 0, 0, 0); } + } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepoStats.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepoStats.java index e11a57f04df..ca18028ad5a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepoStats.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepoStats.java @@ -1,11 +1,20 @@ // 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; +import com.yahoo.collections.Pair; +import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.NodeType; import com.yahoo.vespa.hosted.provision.autoscale.Load; import com.yahoo.vespa.hosted.provision.autoscale.NodeMetricSnapshot; +import com.yahoo.vespa.hosted.provision.autoscale.NodeTimeseries; import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -18,10 +27,12 @@ public class NodeRepoStats { private final Load load; private final Load activeLoad; + private final List<ApplicationStats> applicationStats; - private NodeRepoStats(Load load, Load activeLoad) { + private NodeRepoStats(Load load, Load activeLoad, List<ApplicationStats> applicationStats) { this.load = load; this.activeLoad = activeLoad; + this.applicationStats = List.copyOf(applicationStats); } /** @@ -33,36 +44,71 @@ public class NodeRepoStats { /** Returns the current average utilization in this node repo over all active nodes. */ public Load activeLoad() { return activeLoad; } + /** Returns stats on each application, sorted by decreasing unutilized allocation measured in dollars per hour. */ + public List<ApplicationStats> applicationStats() { return applicationStats; } + public static NodeRepoStats computeOver(NodeRepository nodeRepository) { NodeList allNodes = nodeRepository.nodes().list(); + List<NodeTimeseries> allNodeTimeseries = nodeRepository.metricsDb().getNodeTimeseries(Duration.ofHours(1), Set.of()); + + Pair<Load, Load> load = computeLoad(allNodes, allNodeTimeseries); + List<ApplicationStats> applicationStats = computeApplicationStats(allNodes, allNodeTimeseries); + return new NodeRepoStats(load.getFirst(), load.getSecond(), applicationStats); + } - NodeResources totalActiveResources = new NodeResources(0, 0, 0, 0); - double cpu = 0, memory = 0, disk = 0; - for (var nodeTimeseries : nodeRepository.metricsDb().getNodeTimeseries(Duration.ofHours(1), Set.of())) { + private static Pair<Load, Load> computeLoad(NodeList allNodes, List<NodeTimeseries> allNodeTimeseries) { + NodeResources totalActiveResources = NodeResources.zero(); + Load load = Load.zero(); + for (var nodeTimeseries : allNodeTimeseries) { Optional<Node> node = allNodes.node(nodeTimeseries.hostname()); if (node.isEmpty() || node.get().state() != Node.State.active) continue; Optional<NodeMetricSnapshot> snapshot = nodeTimeseries.last(); if (snapshot.isEmpty()) continue; - cpu += snapshot.get().cpu() * node.get().resources().vcpu(); - memory += snapshot.get().memory() * node.get().resources().memoryGb(); - disk += snapshot.get().disk() * node.get().resources().diskGb(); + load = load.add(snapshot.get().load().multiply(node.get().resources())); totalActiveResources = totalActiveResources.add(node.get().resources().justNumbers()); } - NodeResources totalHostResources = new NodeResources(0, 0, 0, 0); - for (var host : allNodes.hosts()) { + NodeResources totalHostResources = NodeResources.zero(); + for (var host : allNodes.hosts()) totalHostResources = totalHostResources.add(host.resources().justNumbers()); + + return new Pair<>(load.divide(totalHostResources), load.divide(totalActiveResources)); + } + + private static List<ApplicationStats> computeApplicationStats(NodeList allNodes, + List<NodeTimeseries> allNodeTimeseries) { + List<ApplicationStats> applicationStats = new ArrayList<>(); + Map<String, NodeMetricSnapshot> snapshotsByHost = byHost(allNodeTimeseries); + for (var applicationNodes : allNodes.state(Node.State.active) + .nodeType(NodeType.tenant) + .matching(node -> ! node.allocation().get().owner().instance().isTester()) + .groupingBy(node -> node.allocation().get().owner()).entrySet()) { + + NodeResources totalResources = NodeResources.zero(); + NodeResources totalUtilizedResources = NodeResources.zero(); + for (var node : applicationNodes.getValue()) { + var snapshot = snapshotsByHost.get(node.hostname()); + if (snapshot == null) continue; + + totalResources = totalResources.add(node.resources().justNumbers()); + totalUtilizedResources = totalUtilizedResources.add(snapshot.load().scaled(node.resources().justNumbers())); + } + applicationStats.add(new ApplicationStats(applicationNodes.getKey(), + Load.byDividing(totalUtilizedResources, totalResources), + totalResources.cost(), + totalUtilizedResources.cost())); } + Collections.sort(applicationStats); + return applicationStats; + } - Load load = new Load(divide(cpu, totalHostResources.vcpu()), - divide(memory, totalHostResources.memoryGb()), - divide(disk, totalHostResources.diskGb())); - Load activeLoad = new Load(divide(cpu, totalActiveResources.vcpu()), - divide(memory, totalActiveResources.memoryGb()), - divide(disk, totalActiveResources.diskGb())); - return new NodeRepoStats(load, activeLoad); + private static Map<String, NodeMetricSnapshot> byHost(List<NodeTimeseries> allNodeTimeseries) { + Map<String, NodeMetricSnapshot> snapshots = new HashMap<>(); + for (var nodeTimeseries : allNodeTimeseries) + nodeTimeseries.last().ifPresent(last -> snapshots.put(nodeTimeseries.hostname(), last)); + return snapshots; } private static double divide(double a, double b) { @@ -70,4 +116,37 @@ public class NodeRepoStats { return a / b; } + public static class ApplicationStats implements Comparable<ApplicationStats> { + + private final ApplicationId id; + private final Load load; + private final double cost; + private final double utilizedCost; + + public ApplicationStats(ApplicationId id, Load load, double cost, double utilizedCost) { + this.id = id; + this.load = load; + this.cost = cost; + this.utilizedCost = utilizedCost; + } + + public ApplicationId id() { return id; } + public Load load() { return load; } + + /** The standard cost of this application */ + public double cost() { return cost; } + + /** The amount of that cost which is currently utilized */ + public double utilizedCost() { return utilizedCost; } + + /** Cost - utilizedCost */ + public double unutilizedCost() { return cost - utilizedCost; } + + @Override + public int compareTo(NodeRepoStats.ApplicationStats other) { + return -Double.compare(this.unutilizedCost(), other.unutilizedCost()); + } + + } + } 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 a06ad89e299..79b09348d21 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 @@ -96,18 +96,18 @@ public class AllocationOptimizer { // Memory and disk: Scales with group size // The fixed cost portion of cpu does not scale with changes to the node count - double queryCpuPerGroup = fixedCpuCostFraction * target.nodeCpu() + - (1 - fixedCpuCostFraction) * target.nodeCpu() * current.groupSize() / groupSize; + double queryCpuPerGroup = fixedCpuCostFraction * target.resources().vcpu() + + (1 - fixedCpuCostFraction) * target.resources().vcpu() * current.groupSize() / groupSize; double queryCpu = queryCpuPerGroup * current.groups() / groups; - double writeCpu = target.nodeCpu() * current.groupSize() / groupSize; + double writeCpu = target.resources().vcpu() * current.groupSize() / groupSize; cpu = clusterModel.queryCpuFraction() * queryCpu + (1 - clusterModel.queryCpuFraction()) * writeCpu; - memory = target.nodeMemory() * current.groupSize() / groupSize; - disk = target.nodeDisk() * current.groupSize() / groupSize; + memory = target.resources().memoryGb() * current.groupSize() / groupSize; + disk = target.resources().diskGb() * current.groupSize() / groupSize; } else { - cpu = target.nodeCpu() * current.nodes() / nodes; - memory = target.nodeMemory(); - disk = target.nodeDisk(); + cpu = target.resources().vcpu() * current.nodes() / nodes; + memory = target.resources().memoryGb(); + disk = target.resources().diskGb(); } // Combine the scaled resource values computed here 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 acf227e3de2..e3622c8f076 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 @@ -96,15 +96,10 @@ public class ClusterModel { return queryFractionOfMax = clusterTimeseries().queryFractionOfMax(scalingDuration(), clock); } - public double averageLoad(Resource resource) { return nodeTimeseries().averageLoad(resource); } - - public double idealLoad(Resource resource) { - switch (resource) { - case cpu : return idealCpuLoad(); - case memory : return idealMemoryLoad; - case disk : return idealDiskLoad; - default : throw new IllegalStateException("No ideal load defined for " + resource); - } + public Load averageLoad() { return nodeTimeseries().averageLoad(); } + + public Load idealLoad() { + return new Load(idealCpuLoad(), idealMemoryLoad, idealDiskLoad); } /** Ideal cpu load must take the application traffic fraction into account */ diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterNodesTimeseries.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterNodesTimeseries.java index c097abd8208..a7396f29d92 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterNodesTimeseries.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterNodesTimeseries.java @@ -42,21 +42,17 @@ public class ClusterNodesTimeseries { /** Returns the number of nodes measured in this */ public int nodesMeasured() { return timeseries.size(); } - /** Returns the average load of this resource in this */ - public double averageLoad(Resource resource) { - int measurementCount = timeseries.stream().mapToInt(m -> m.size()).sum(); - if (measurementCount == 0) return 0; - double measurementSum = timeseries.stream().flatMap(m -> m.asList().stream()).mapToDouble(m -> value(resource, m)).sum(); - return measurementSum / measurementCount; - } - - private double value(Resource resource, NodeMetricSnapshot snapshot) { - switch (resource) { - case cpu: return snapshot.cpu(); - case memory: return snapshot.memory(); - case disk: return snapshot.disk(); - default: throw new IllegalArgumentException("Got an unknown resource " + resource); + /** Returns the average load in this */ + public Load averageLoad() { + Load total = Load.zero(); + int count = 0; + for (var nodeTimeseries : timeseries) { + for (var snapshot : nodeTimeseries.asList()) { + total = total.add(snapshot.load()); + count++; + } } + return total.divide(count); } private List<NodeTimeseries> filter(List<NodeTimeseries> timeseries, Predicate<NodeMetricSnapshot> filter) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Load.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Load.java index 1a13c1bb6d8..1e400bd2627 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Load.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Load.java @@ -1,6 +1,8 @@ // 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.autoscale; +import com.yahoo.config.provision.NodeResources; + /** * The load of a node or system, measured as fractions of max (1.0) in three dimensions. * @@ -20,11 +22,41 @@ public class Load { public double memory() { return memory; } public double disk() { return disk; } + public Load add(Load other) { + return new Load(cpu + other.cpu(), memory + other.memory(), disk + other.disk()); + } + + public Load multiply(NodeResources resources) { + return new Load(cpu * resources.vcpu(), memory * resources.memoryGb(), disk * resources.diskGb()); + } + + public Load multiply(double factor) { + return new Load(cpu * factor, memory * factor, disk * factor); + } + + public Load divide(NodeResources resources) { + return new Load(divide(cpu, resources.vcpu()), divide(memory, resources.memoryGb()), divide(disk, resources.diskGb())); + } + + public Load divide(Load divisor) { + return new Load(divide(cpu, divisor.cpu()), divide(memory, divisor.memory()), divide(disk, divisor.disk())); + } + + public Load divide(double divisor) { + return new Load(divide(cpu, divisor), divide(memory, divisor), divide(disk, divisor)); + } + + public NodeResources scaled(NodeResources resources) { + return resources.withVcpu(cpu * resources.vcpu()) + .withMemoryGb(memory * resources.memoryGb()) + .withDiskGb(disk * resources.diskGb()); + } + private double requireNormalized(double value, String name) { if (Double.isNaN(value)) - throw new IllegalArgumentException(name + " must be a number between 0 and 1, but is NaN"); - if (value < 0 || value > 1) - throw new IllegalArgumentException(name + " must be between 0 and 1, but is " + value); + throw new IllegalArgumentException(name + " must be a number but is NaN"); + if (value < 0) + throw new IllegalArgumentException(name + " must be zero or lager, but is " + value); return value; } @@ -35,4 +67,13 @@ public class Load { public static Load zero() { return new Load(0, 0, 0); } + private static double divide(double a, double b) { + if (a == 0 && b == 0) return 0; + return a / b; + } + + public static Load byDividing(NodeResources a, NodeResources b) { + return new Load(divide(a.vcpu(), b.vcpu()), divide(a.memoryGb(), b.memoryGb()), divide(a.diskGb(), b.diskGb())); + } + } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsResponse.java index b3cf6c1e962..210388db7b8 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsResponse.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsResponse.java @@ -67,9 +67,9 @@ public class MetricsResponse { consumeServiceMetrics(nodeObject.field("services"), nodeValues); nodeMetrics.add(new Pair<>(hostname, new NodeMetricSnapshot(at, - Metric.cpu.from(nodeValues), - Metric.memory.from(nodeValues), - Metric.disk.from(nodeValues), + new Load(Metric.cpu.from(nodeValues), + Metric.memory.from(nodeValues), + Metric.disk.from(nodeValues)), (long)Metric.generation.from(nodeValues), Metric.inService.from(nodeValues) > 0, clusterIsStable(node.get(), applicationNodes, nodeRepository), diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricSnapshot.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricSnapshot.java index be9f7bd4819..6329d350642 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricSnapshot.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricSnapshot.java @@ -12,21 +12,17 @@ public class NodeMetricSnapshot implements Comparable<NodeMetricSnapshot> { private final Instant at; - private final double cpu; - private final double memory; - private final double disk; + private final Load load; private final long generation; private final boolean inService; private final boolean stable; private final double queryRate; - public NodeMetricSnapshot(Instant at, double cpu, double memory, double disk, + public NodeMetricSnapshot(Instant at, Load load, long generation, boolean inService, boolean stable, double queryRate) { this.at = at; - this.cpu = cpu; - this.memory = memory; - this.disk = disk; + this.load = load; this.generation = generation; this.inService = inService; this.stable = stable; @@ -34,9 +30,7 @@ public class NodeMetricSnapshot implements Comparable<NodeMetricSnapshot> { } public Instant at() { return at; } - public double cpu() { return cpu; } - public double memory() { return memory; } - public double disk() { return disk; } + public Load load() { return load; } /** Queries per second */ public double queryRate() { return queryRate; } @@ -53,10 +47,8 @@ public class NodeMetricSnapshot implements Comparable<NodeMetricSnapshot> { } @Override - public String toString() { return "metrics at " + at + ":" + - " cpu: " + cpu + - " memory: " + memory + - " disk: " + disk + + public String toString() { return "metrics at " + at + ": " + + load + " generation: " + generation + " inService: " + inService + " stable: " + stable + diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java index 459a7919bbe..c933e16041a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDb.java @@ -113,9 +113,9 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb { TableWriter.Row row = writer.newRow(atMillis * 1000); // in microseconds row.putStr(0, snapshot.getFirst()); // (1 is timestamp) - row.putFloat(2, (float)snapshot.getSecond().cpu()); - row.putFloat(3, (float)snapshot.getSecond().memory()); - row.putFloat(4, (float)snapshot.getSecond().disk()); + row.putFloat(2, (float)snapshot.getSecond().load().cpu()); + row.putFloat(3, (float)snapshot.getSecond().load().memory()); + row.putFloat(4, (float)snapshot.getSecond().load().disk()); row.putLong(5, snapshot.getSecond().generation()); row.putBool(6, snapshot.getSecond().inService()); row.putBool(7, snapshot.getSecond().stable()); @@ -358,9 +358,9 @@ public class QuestMetricsDb extends AbstractComponent implements MetricsDb { if (hostnames.isEmpty() || hostnames.contains(hostname)) { snapshots.put(hostname, new NodeMetricSnapshot(Instant.ofEpochMilli(record.getTimestamp(1) / 1000), - record.getFloat(2), - record.getFloat(3), - record.getFloat(4), + new Load(record.getFloat(2), + record.getFloat(3), + record.getFloat(4)), record.getLong(5), record.getBool(6), record.getBool(7), diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Resource.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Resource.java deleted file mode 100644 index c639ad1f779..00000000000 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Resource.java +++ /dev/null @@ -1,30 +0,0 @@ -// 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.autoscale; - -import com.yahoo.config.provision.NodeResources; - -/** - * A resource subject to autoscaling - * - * @author bratseth - */ -public enum Resource { - - /** Cpu utilization ratio */ - cpu { - double valueFrom(NodeResources resources) { return resources.vcpu(); } - }, - - /** Memory utilization ratio */ - memory { - double valueFrom(NodeResources resources) { return resources.memoryGb(); } - }, - - /** Disk utilization ratio */ - disk { - double valueFrom(NodeResources resources) { return resources.diskGb(); } - }; - - abstract double valueFrom(NodeResources resources); - -} diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ResourceTarget.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ResourceTarget.java index b1a1e86b08d..3b8b7f08684 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ResourceTarget.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ResourceTarget.java @@ -1,6 +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.autoscale; +import com.yahoo.config.provision.NodeResources; import com.yahoo.vespa.hosted.provision.applications.Application; import java.time.Clock; @@ -18,56 +19,34 @@ public class ResourceTarget { private final boolean adjustForRedundancy; /** The target real resources per node, assuming the node assignment where this was decided */ - private final double cpu, memory, disk; + private final NodeResources resources; - private ResourceTarget(double cpu, double memory, double disk, boolean adjustForRedundancy) { - this.cpu = cpu; - this.memory = memory; - this.disk = disk; + private ResourceTarget(NodeResources resources, boolean adjustForRedundancy) { + this.resources = resources; this.adjustForRedundancy = adjustForRedundancy; } /** Are the target resources given by this including redundancy or not */ public boolean adjustForRedundancy() { return adjustForRedundancy; } - /** Returns the target cpu per node, in terms of the current allocation */ - public double nodeCpu() { return cpu; } - - /** Returns the target memory per node, in terms of the current allocation */ - public double nodeMemory() { return memory; } - - /** Returns the target disk per node, in terms of the current allocation */ - public double nodeDisk() { return disk; } + /** Returns the target resources per node in terms of the current allocation */ + public NodeResources resources() { return resources; } @Override public String toString() { - return "target " + - (adjustForRedundancy ? "(with redundancy adjustment) " : "") + - "[vcpu " + cpu + ", memoryGb " + memory + ", diskGb " + disk + "]"; - } - - private static double nodeUsage(Resource resource, double load, AllocatableClusterResources current) { - return load * resource.valueFrom(current.realResources().nodeResources()); + return "target " + resources + (adjustForRedundancy ? "(with redundancy adjustment) " : ""); } /** Create a target of achieving ideal load given a current load */ public static ResourceTarget idealLoad(ClusterModel clusterModel, AllocatableClusterResources current) { - return new ResourceTarget(nodeUsage(Resource.cpu, clusterModel.averageLoad(Resource.cpu), current) - / clusterModel.idealLoad(Resource.cpu), - nodeUsage(Resource.memory, clusterModel.averageLoad(Resource.memory), current) - / clusterModel.idealLoad(Resource.memory), - nodeUsage(Resource.disk, clusterModel.averageLoad(Resource.disk), current) - / clusterModel.idealLoad(Resource.disk), - true); + var loadAdjustment = clusterModel.averageLoad().divide(clusterModel.idealLoad()); + return new ResourceTarget(loadAdjustment.scaled(current.realResources().nodeResources()), true); } /** Crete a target of preserving a current allocation */ public static ResourceTarget preserve(AllocatableClusterResources current) { - return new ResourceTarget(current.realResources().nodeResources().vcpu(), - current.realResources().nodeResources().memoryGb(), - current.realResources().nodeResources().diskGb(), - false); + return new ResourceTarget(current.realResources().nodeResources(), false); } } 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 176bf195f1f..692d757f41d 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 @@ -1,27 +1,18 @@ // 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.ApplicationId; import com.yahoo.config.provision.ClusterResources; import com.yahoo.slime.Cursor; import com.yahoo.slime.Slime; -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.Cluster; import com.yahoo.vespa.hosted.provision.applications.ScalingEvent; import com.yahoo.vespa.hosted.provision.autoscale.ClusterModel; -import com.yahoo.vespa.hosted.provision.autoscale.ClusterNodesTimeseries; -import com.yahoo.vespa.hosted.provision.autoscale.ClusterTimeseries; import com.yahoo.vespa.hosted.provision.autoscale.MetricsDb; -import com.yahoo.vespa.hosted.provision.autoscale.Resource; -import com.yahoo.vespa.hosted.provision.autoscale.ResourceTarget; import java.net.URI; -import java.time.Clock; -import java.time.Duration; -import java.util.Collection; import java.util.List; /** @@ -94,12 +85,12 @@ public class ApplicationSerializer { } private static void clusterUtilizationToSlime(ClusterModel clusterModel, Cursor utilizationObject) { - utilizationObject.setDouble("cpu", clusterModel.averageLoad(Resource.cpu)); - utilizationObject.setDouble("idealCpu", clusterModel.idealLoad(Resource.cpu)); - utilizationObject.setDouble("memory", clusterModel.averageLoad(Resource.memory)); - utilizationObject.setDouble("idealMemory", clusterModel.idealLoad(Resource.memory)); - utilizationObject.setDouble("disk", clusterModel.averageLoad(Resource.disk)); - utilizationObject.setDouble("idealDisk", clusterModel.idealLoad(Resource.disk)); + utilizationObject.setDouble("cpu", clusterModel.averageLoad().cpu()); + utilizationObject.setDouble("idealCpu", clusterModel.idealLoad().cpu()); + utilizationObject.setDouble("memory", clusterModel.averageLoad().memory()); + utilizationObject.setDouble("idealMemory", clusterModel.idealLoad().memory()); + utilizationObject.setDouble("disk", clusterModel.averageLoad().disk()); + utilizationObject.setDouble("idealDisk", clusterModel.idealLoad().disk()); } private static void scalingEventsToSlime(List<ScalingEvent> scalingEvents, Cursor scalingEventsArray) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepoStatsTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepoStatsTest.java index 5d136016a71..44376fc103c 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepoStatsTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepoStatsTest.java @@ -2,15 +2,24 @@ package com.yahoo.vespa.hosted.provision; import com.yahoo.collections.Pair; +import com.yahoo.component.Version; +import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.ClusterResources; +import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.RegionName; +import com.yahoo.config.provision.Zone; import com.yahoo.vespa.hosted.provision.autoscale.Load; import com.yahoo.vespa.hosted.provision.autoscale.NodeMetricSnapshot; +import com.yahoo.vespa.hosted.provision.provisioning.ProvisioningTester; import org.junit.Test; import java.time.Duration; import java.util.List; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * @author bratseth @@ -24,6 +33,7 @@ public class NodeRepoStatsTest { var tester = new NodeRepositoryTester(); assertLoad(Load.zero(), tester.nodeRepository().computeStats().load()); assertLoad(Load.zero(), tester.nodeRepository().computeStats().activeLoad()); + assertTrue(tester.nodeRepository().computeStats().applicationStats().isEmpty()); } @Test @@ -34,33 +44,83 @@ public class NodeRepoStatsTest { tester.addHost("host3", "small"); assertLoad(Load.zero(), tester.nodeRepository().computeStats().load()); assertLoad(Load.zero(), tester.nodeRepository().computeStats().activeLoad()); + assertTrue(tester.nodeRepository().computeStats().applicationStats().isEmpty()); } @Test - public void testNodesAndMetrics() { - var tester = new NodeRepositoryTester(); - tester.addHost("host1", "default"); - tester.addHost("host2", "default"); - tester.addHost("host3", "small"); - tester.addNode("node1", "host1", new NodeResources(0.2, 0.5, 4, 1)); - tester.addNode("node2", "host1", new NodeResources(0.3, 1.0, 8, 1)); - tester.addNode("node3", "host3", new NodeResources(0.3, 1.5, 12, 1)); - tester.setNodeState("node1", Node.State.active); - tester.setNodeState("node2", Node.State.active); - tester.setNodeState("node3", Node.State.active); - assertLoad(Load.zero(), tester.nodeRepository().computeStats().load()); - assertLoad(Load.zero(), tester.nodeRepository().computeStats().activeLoad()); + public void testStats() { + var hostResources = new NodeResources(10, 100, 1000, 10, + NodeResources.DiskSpeed.fast, NodeResources.StorageType.local); + + ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); + tester.makeReadyHosts(10, hostResources).activateTenantHosts(); + + var app1 = ProvisioningTester.applicationId("app1"); + var app2 = ProvisioningTester.applicationId("app2"); + var app3 = ProvisioningTester.applicationId("app3"); + var cluster1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("cluster1")).vespaVersion(Version.fromString("7")).build(); + var cluster2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("cluster2")).vespaVersion(Version.fromString("7")).build(); + var small = new NodeResources(1, 10, 100, 1, NodeResources.DiskSpeed.any); + var large = new NodeResources(2, 20, 200, 1); - var before = tester.clock().instant(); - tester.clock().advance(Duration.ofMinutes(5)); + // Deploy apps + var hostsApp1 = tester.prepare(app1, cluster1, Capacity.from(new ClusterResources(6, 1, small))); + hostsApp1.addAll(tester.prepare(app1, cluster2, Capacity.from(new ClusterResources(4, 1, large)))); + tester.activate(app1, hostsApp1); + tester.activate(app2, cluster1, Capacity.from(new ClusterResources(8, 1, small))); + tester.activate(app3, cluster1, Capacity.from(new ClusterResources(5, 1, large))); + + // Add metrics + double loadApp1Cluster1 = 0.2; + double loadApp1Cluster2 = 0.3; + double loadApp2 = 0.4; + double loadApp3 = 0.5; var now = tester.clock().instant(); - tester.nodeRepository().metricsDb().addNodeMetrics( - List.of(new Pair<>("node1", new NodeMetricSnapshot(before, 0, 0, 0, 1, true, true, 1.0)), - new Pair<>("node1", new NodeMetricSnapshot(now, 0.5, 0.1, 0.8, 1, true, true, 1.0)), - new Pair<>("node2", new NodeMetricSnapshot(now, 0.1, 0.8, 0.1, 1, true, true, 1.0)), - new Pair<>("node3", new NodeMetricSnapshot(now, 1.0, 0.1, 0.2, 1, true, true, 1.0)))); - assertLoad(new Load(0.0860, 0.1000, 0.0256), tester.nodeRepository().computeStats().load()); - assertLoad(new Load(0.5375, 0.3333, 0.2667), tester.nodeRepository().computeStats().activeLoad()); + for (Node node : tester.nodeRepository().nodes().list(Node.State.active)) { + double loadFactor; + var allocation = node.allocation().get(); + if (allocation.owner().equals(app1)) { + if (allocation.membership().cluster().id().equals(cluster1.id())) + loadFactor = loadApp1Cluster1; + else + loadFactor = loadApp1Cluster2; + } + else if (allocation.owner().equals(app2)) { + loadFactor = loadApp2; + } + else { + loadFactor = loadApp3; + } + var snapshot = new NodeMetricSnapshot(now, new Load(1.0, 0.9, 0.8).multiply(loadFactor), 1, true, true, 1.0 ); + tester.nodeRepository().metricsDb().addNodeMetrics(List.of(new Pair<>(node.hostname(), snapshot))); + } + + var stats = tester.nodeRepository().computeStats(); + + assertLoad(new Load(0.6180,0.5562,0.4944), stats.load()); + assertLoad(new Load(0.4682,0.4214,0.3745), stats.activeLoad()); + + var app1Stats = stats.applicationStats().get(0); + var app2Stats = stats.applicationStats().get(2); + var app3Stats = stats.applicationStats().get(1); + + assertEquals(app1, app1Stats.id()); + assertEquals(2.940, app1Stats.cost(), delta); + assertEquals(0.702, app1Stats.utilizedCost(), delta); + assertEquals(2.238, app1Stats.unutilizedCost(), delta); + assertLoad(new Load(0.2571, 0.2314, 0.2057), app1Stats.load()); + + assertEquals(app2, app2Stats.id()); + assertEquals(1.680, app2Stats.cost(), delta); + assertEquals(0.624, app2Stats.utilizedCost(), delta); + assertEquals(1.056, app2Stats.unutilizedCost(), delta); + assertLoad(new Load(.40, 0.36, 0.32), app2Stats.load()); + + assertEquals(app3, app3Stats.id()); + assertEquals(2.100, app3Stats.cost(), delta); + assertEquals(0.975, app3Stats.utilizedCost(), delta); + assertEquals(1.125, app3Stats.unutilizedCost(), delta); + assertLoad(new Load(0.5, 0.45, 0.40), app3Stats.load()); } private static void assertLoad(Load expected, Load actual) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java index a5d8256439f..5fe6023e5af 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java @@ -138,14 +138,12 @@ class AutoscalingTester { for (int i = 0; i < count; i++) { clock().advance(Duration.ofMinutes(5)); for (Node node : nodes) { - float cpu = value * oneExtraNodeFactor; - float memory = (float) ClusterModel.idealMemoryLoad * otherResourcesLoad * oneExtraNodeFactor; - float disk = (float) ClusterModel.idealDiskLoad * otherResourcesLoad * oneExtraNodeFactor; + Load load = new Load(value, + ClusterModel.idealMemoryLoad * otherResourcesLoad, + ClusterModel.idealDiskLoad * otherResourcesLoad).multiply(oneExtraNodeFactor); nodeMetricsDb().addNodeMetrics(List.of(new Pair<>(node.hostname(), new NodeMetricSnapshot(clock().instant(), - cpu, - memory, - disk, + load, 0, true, true, @@ -174,11 +172,12 @@ class AutoscalingTester { float cpu = (float) 0.2 * otherResourcesLoad * oneExtraNodeFactor; float memory = value * oneExtraNodeFactor; float disk = (float) ClusterModel.idealDiskLoad * otherResourcesLoad * oneExtraNodeFactor; + Load load = new Load(0.2 * otherResourcesLoad, + value, + ClusterModel.idealDiskLoad * otherResourcesLoad).multiply(oneExtraNodeFactor); nodeMetricsDb().addNodeMetrics(List.of(new Pair<>(node.hostname(), new NodeMetricSnapshot(clock().instant(), - cpu, - memory, - disk, + load, 0, true, true, @@ -199,9 +198,7 @@ class AutoscalingTester { for (Node node : nodes) { nodeMetricsDb().addNodeMetrics(List.of(new Pair<>(node.hostname(), new NodeMetricSnapshot(clock().instant(), - cpu, - memory, - disk, + new Load(cpu, memory, disk), generation, inService, stable, 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 70550b0a7c3..550ecceee23 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 @@ -37,13 +37,13 @@ public class ClusterModelTest { var model1 = new ClusterModel(application.with(new Status(0.0, 1.0)), cluster, clock, Duration.ofMinutes(10), timeseries(cluster,100, t -> t == 0 ? 10000.0 : 0.0, t -> 0.0, clock)); - assertEquals(0.131, model1.idealLoad(Resource.cpu), delta); + assertEquals(0.131, model1.idealLoad().cpu(), delta); // Almost no current traffic share: Ideal load is low but capped var model2 = new ClusterModel(application.with(new Status(0.0001, 1.0)), cluster, clock, Duration.ofMinutes(10), timeseries(cluster,100, t -> t == 0 ? 10000.0 : 0.0, t -> 0.0, clock)); - assertEquals(0.131, model2.idealLoad(Resource.cpu), delta); + assertEquals(0.131, model2.idealLoad().cpu(), delta); } @Test @@ -58,13 +58,13 @@ public class ClusterModelTest { var model1 = new ClusterModel(application, cluster, clock, Duration.ofMinutes(10), timeseries(cluster,100, t -> t == 0 ? 10000.0 : 0.0, t -> 0.0, clock)); - assertEquals(0.275, model1.idealLoad(Resource.cpu), delta); + assertEquals(0.275, model1.idealLoad().cpu(), delta); - // Almost current traffic: Ideal load is low but capped + // Almost no current traffic: Ideal load is low but capped var model2 = new ClusterModel(application.with(new Status(0.0001, 1.0)), cluster, clock, Duration.ofMinutes(10), timeseries(cluster,100, t -> t == 0 ? 10000.0 : 0.0001, t -> 0.0, clock)); - assertEquals(0.275, model1.idealLoad(Resource.cpu), delta); + assertEquals(0.040, model2.idealLoad().cpu(), delta); } private Cluster cluster(NodeResources resources) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsV2MetricsFetcherTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsV2MetricsFetcherTest.java index ef85e228cc7..5f1a36e7b56 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsV2MetricsFetcherTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/MetricsV2MetricsFetcherTest.java @@ -51,14 +51,14 @@ public class MetricsV2MetricsFetcherTest { assertEquals(2, values.size()); assertEquals("host-1.yahoo.com", values.get(0).getFirst()); - assertEquals(0.162, values.get(0).getSecond().cpu(), delta); - assertEquals(0.231, values.get(0).getSecond().memory(), delta); - assertEquals(0.820, values.get(0).getSecond().disk(), delta); + assertEquals(0.162, values.get(0).getSecond().load().cpu(), delta); + assertEquals(0.231, values.get(0).getSecond().load().memory(), delta); + assertEquals(0.820, values.get(0).getSecond().load().disk(), delta); assertEquals("host-2.yahoo.com", values.get(1).getFirst()); - assertEquals(0.2, values.get(1).getSecond().cpu(), delta); - assertEquals(0.0, values.get(1).getSecond().memory(), delta); - assertEquals(0.4, values.get(1).getSecond().disk(), delta); + assertEquals(0.2, values.get(1).getSecond().load().cpu(), delta); + assertEquals(0.0, values.get(1).getSecond().load().memory(), delta); + assertEquals(0.4, values.get(1).getSecond().load().disk(), delta); assertEquals(45.0, values.get(1).getSecond().queryRate(), delta); } @@ -69,9 +69,9 @@ public class MetricsV2MetricsFetcherTest { httpClient.requestsReceived.get(1)); assertEquals(1, values.size()); assertEquals("host-3.yahoo.com", values.get(0).getFirst()); - assertEquals(0.10, values.get(0).getSecond().cpu(), delta); - assertEquals(0.15, values.get(0).getSecond().memory(), delta); - assertEquals(0.20, values.get(0).getSecond().disk(), delta); + assertEquals(0.10, values.get(0).getSecond().load().cpu(), delta); + assertEquals(0.15, values.get(0).getSecond().load().memory(), delta); + assertEquals(0.20, values.get(0).getSecond().load().disk(), delta); assertEquals(3, values.get(0).getSecond().generation(), delta); assertTrue(values.get(0).getSecond().stable()); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsDbTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsDbTest.java index 330038aa690..ae14b94e619 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsDbTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsDbTest.java @@ -40,9 +40,7 @@ public class NodeMetricsDbTest { Collection<Pair<String, NodeMetricSnapshot>> values = new ArrayList<>(); for (int i = 0; i < 40; i++) { values.add(new Pair<>(node0, new NodeMetricSnapshot(clock.instant(), - 0.9f, - 0.6f, - 0.6f, + new Load(0.9, 0.6, 0.6), 0, true, false, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDbTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDbTest.java index f465a57d76a..34243f4548f 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDbTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/QuestMetricsDbTest.java @@ -54,9 +54,9 @@ public class QuestMetricsDbTest { assertEquals(1000, nodeTimeSeries1.get(0).size()); NodeMetricSnapshot snapshot = nodeTimeSeries1.get(0).asList().get(0); assertEquals(startTime.plus(Duration.ofSeconds(1)), snapshot.at()); - assertEquals(0.1, snapshot.cpu(), delta); - assertEquals(0.2, snapshot.memory(), delta); - assertEquals(0.4, snapshot.disk(), delta); + assertEquals(0.1, snapshot.load().cpu(), delta); + assertEquals(0.2, snapshot.load().memory(), delta); + assertEquals(0.4, snapshot.load().disk(), delta); assertEquals(1, snapshot.generation(), delta); assertEquals(30, snapshot.queryRate(), delta); @@ -234,10 +234,8 @@ public class QuestMetricsDbTest { for (int i = 1; i <= countPerHost; i++) { for (String host : hosts) timeseries.add(new Pair<>(host, new NodeMetricSnapshot(clock.instant(), - i * 0.1, - i * 0.2, - i * 0.4, - i % 100, + new Load(i * 0.1, i * 0.2, i * 0.4), + i % 100, true, true, 30.0))); @@ -260,11 +258,8 @@ public class QuestMetricsDbTest { Collection<Pair<String, NodeMetricSnapshot>> timeseries = new ArrayList<>(); for (int i = 1; i <= countPerHost; i++) { for (String host : hosts) - timeseries.add(new Pair<>(host, new NodeMetricSnapshot(at, - i * 0.1, - i * 0.2, - i * 0.4, - i % 100, + timeseries.add(new Pair<>(host, new NodeMetricSnapshot(at, new Load(i * 0.1, i * 0.2, i * 0.4), + i % 100, true, false, 0.0))); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTester.java index 7313fc1797f..4ca12e4ab53 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTester.java @@ -17,6 +17,7 @@ import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.applications.Cluster; import com.yahoo.vespa.hosted.provision.autoscale.ClusterMetricSnapshot; +import com.yahoo.vespa.hosted.provision.autoscale.Load; import com.yahoo.vespa.hosted.provision.autoscale.NodeMetricSnapshot; import com.yahoo.vespa.hosted.provision.autoscale.MetricsDb; import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; @@ -74,9 +75,7 @@ public class AutoscalingMaintainerTester { for (Node node : nodes) nodeRepository().metricsDb().addNodeMetrics(List.of(new Pair<>(node.hostname(), new NodeMetricSnapshot(clock().instant(), - cpu, - mem, - disk, + new Load(cpu, mem, disk), generation, true, true, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java index 29a93c463ce..7ab21946379 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java @@ -17,10 +17,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.Cluster; -import com.yahoo.vespa.hosted.provision.autoscale.ClusterModel; +import com.yahoo.vespa.hosted.provision.autoscale.Load; import com.yahoo.vespa.hosted.provision.autoscale.NodeMetricSnapshot; -import com.yahoo.vespa.hosted.provision.autoscale.MetricsDb; -import com.yahoo.vespa.hosted.provision.autoscale.Resource; import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; import com.yahoo.vespa.hosted.provision.provisioning.ProvisioningTester; import org.junit.Test; @@ -124,9 +122,7 @@ public class ScalingSuggestionsMaintainerTest { for (Node node : nodes) nodeRepository.metricsDb().addNodeMetrics(List.of(new Pair<>(node.hostname(), new NodeMetricSnapshot(nodeRepository.clock().instant(), - cpu, - memory, - disk, + new Load(cpu, memory, disk), generation, true, true, |