diff options
author | Ola Aunrønning <olaa@verizonmedia.com> | 2019-07-26 17:15:23 +0200 |
---|---|---|
committer | Ola Aunrønning <olaa@verizonmedia.com> | 2019-07-29 11:44:53 +0200 |
commit | 850bda1b8afb91f81170b664e8fcd82d1faa5c35 (patch) | |
tree | 4b296e01f826a0596816830547409beefde5a3c6 | |
parent | 5d705fcb5eba5a6c394a86bd55165fb909ae98c2 (diff) |
Refactoring resource metering
5 files changed, 94 insertions, 50 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java index 7f7a6b758d5..29c0948e828 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java @@ -1,27 +1,86 @@ // Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.api.integration.resource; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; + import java.time.Instant; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * @author olaa */ public class ResourceSnapshot { - private final ResourceAllocation resourceAllocation; - private final Instant timestamp; + private ApplicationId applicationId; + private double cpuCores; + private double memoryGb; + private double diskGb; + private Instant timestamp; - public ResourceSnapshot(ResourceAllocation resourceAllocation, Instant timestamp) { - this.resourceAllocation = resourceAllocation; + public ResourceSnapshot(ApplicationId applicationId, double cpuCores, double memoryGb, double diskGb, Instant timestamp) { + this.applicationId = applicationId; + this.cpuCores = cpuCores; + this.memoryGb = memoryGb; + this.diskGb = diskGb; this.timestamp = timestamp; } - public ResourceAllocation getResourceAllocation() { - return resourceAllocation; + public static ResourceSnapshot from(List<NodeRepositoryNode> nodes, Instant timestamp) { + Set<ApplicationId> applicationIds = nodes.stream() + .map(n -> ApplicationId.from(n.getOwner().tenant, n.getOwner().application, n.getOwner().instance)) + .collect(Collectors.toSet()); + + if (applicationIds.size() != 1) throw new IllegalArgumentException("List of nodes can only represent one application"); + + return new ResourceSnapshot( + applicationIds.iterator().next(), + nodes.stream().mapToDouble(NodeRepositoryNode::getMinCpuCores).sum(), + nodes.stream().mapToDouble(NodeRepositoryNode::getMinMainMemoryAvailableGb).sum(), + nodes.stream().mapToDouble(NodeRepositoryNode::getMinDiskAvailableGb).sum(), + timestamp + ); + } + + public ApplicationId getApplicationId() { + return applicationId; + } + + public double getCpuCores() { + return cpuCores; + } + + public double getMemoryGb() { + return memoryGb; + } + + public double getDiskGb() { + return diskGb; } public Instant getTimestamp() { return timestamp; } + public void setApplicationId(ApplicationId applicationId) { + this.applicationId = applicationId; + } + + public void setCpuCores(double cpuCores) { + this.cpuCores = cpuCores; + } + + public void setMemoryGb(double memoryGb) { + this.memoryGb = memoryGb; + } + + public void setDiskGb(double diskGb) { + this.diskGb = diskGb; + } + + public void setTimestamp(Instant timestamp) { + this.timestamp = timestamp; + } } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java index f7f3eddb482..bb6830770e2 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshotConsumer.java @@ -1,9 +1,7 @@ // Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.api.integration.resource; -import com.yahoo.config.provision.ApplicationId; - -import java.util.Map; +import java.util.List; /** * Consumes a snapshot of resourses allocated/used per application. @@ -12,5 +10,7 @@ import java.util.Map; */ public interface ResourceSnapshotConsumer { - public void consume(Map<ApplicationId, ResourceSnapshot> resources); + public void consume(List<ResourceSnapshot> resources); + + public List<ResourceSnapshot> getResourceSnapshots(String tenantName, String applicationName); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java index d5d7b63e933..2cd3aef1903 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockResourceSnapshotConsumer.java @@ -1,25 +1,29 @@ // Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.api.integration.stubs; -import com.yahoo.config.provision.ApplicationId; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshotConsumer; -import java.util.Map; +import java.util.List; /** * @author olaa */ public class MockResourceSnapshotConsumer implements ResourceSnapshotConsumer { - private Map<ApplicationId, ResourceSnapshot> resources; + private List<ResourceSnapshot> resources; @Override - public void consume(Map<ApplicationId, ResourceSnapshot> resources){ + public void consume(List<ResourceSnapshot> resources){ this.resources = resources; } - public Map<ApplicationId, ResourceSnapshot> consumedResources() { - return resources; + @Override + public List<ResourceSnapshot> getResourceSnapshots(String tenantName, String applicationName) { + throw new UnsupportedOperationException(); + } + + public List<ResourceSnapshot> consumedResources() { + return this.resources; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java index c1fa02a554c..6b9fdf475e0 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java @@ -10,22 +10,17 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeReposi import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeOwner; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeState; -import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshotConsumer; import java.time.Clock; import java.time.Duration; -import java.time.Instant; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.function.Predicate; import java.util.stream.Collectors; /** * Creates a ResourceSnapshot per application, which is then passed on to a ResourceSnapshotConsumer - * TODO: Write JSON blob of node repo somewhere * * @author olaa */ @@ -57,22 +52,12 @@ public class ResourceMeterMaintainer extends Maintainer { @Override protected void maintain() { List<NodeRepositoryNode> nodes = getNodes(); - Map<ApplicationId, ResourceAllocation> resourceAllocationByApplication = getResourceAllocationByApplication(nodes); - - // For now, we're only interested in resource allocation - Instant timeStamp = clock.instant(); - Map<ApplicationId, ResourceSnapshot> resourceSnapshots = resourceAllocationByApplication.entrySet().stream() - .collect(Collectors.toMap( - e -> e.getKey(), - e -> new ResourceSnapshot(e.getValue(), timeStamp)) - ); - + List<ResourceSnapshot> resourceSnapshots = getResourceSnapshots(nodes); resourceSnapshotConsumer.consume(resourceSnapshots); metric.set(metering_last_reported, clock.millis() / 1000, metric.createContext(Collections.emptyMap())); - metric.set(metering_total_reported, resourceSnapshots.values().stream() - .map(ResourceSnapshot::getResourceAllocation) + metric.set(metering_total_reported, resourceSnapshots.stream() .mapToDouble(r -> r.getCpuCores() + r.getMemoryGb() + r.getDiskGb()) // total metered resource usage, for alerting on drastic changes .sum() , metric.createContext(Collections.emptyMap())); @@ -88,11 +73,12 @@ public class ResourceMeterMaintainer extends Maintainer { .collect(Collectors.toList()); } - private Map<ApplicationId, ResourceAllocation> getResourceAllocationByApplication(List<NodeRepositoryNode> nodes) { + private List<ResourceSnapshot> getResourceSnapshots(List<NodeRepositoryNode> nodes) { return nodes.stream() .collect(Collectors.groupingBy( node -> applicationIdFromNodeOwner(node.getOwner()), - Collectors.collectingAndThen(Collectors.toList(), ResourceAllocation::from))); + Collectors.collectingAndThen(Collectors.toList(), nodeList -> ResourceSnapshot.from(nodeList, clock.instant())) + )).values().stream().collect(Collectors.toList()); } private ApplicationId applicationIdFromNodeOwner(NodeOwner owner) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java index 540efcba3b8..ad4511e7f11 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java @@ -1,10 +1,8 @@ // Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.maintenance; -import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.config.provision.ApplicationId; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot; import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockResourceSnapshotConsumer; import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryClientMock; @@ -13,10 +11,7 @@ import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import org.junit.Test; import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Map; import static org.junit.Assert.*; @@ -41,20 +36,20 @@ public class ResourceMeterMaintainerTest { ResourceMeterMaintainer resourceMeterMaintainer = new ResourceMeterMaintainer(tester.controller(), Duration.ofMinutes(5), new JobControl(tester.curator()), nodeRepository, tester.clock(), metrics, snapshotConsumer); resourceMeterMaintainer.maintain(); - Map<ApplicationId, ResourceSnapshot> consumedResources = snapshotConsumer.consumedResources(); + List<ResourceSnapshot> consumedResources = snapshotConsumer.consumedResources(); + // The mocked repository contains two applications, so we should also consume two ResourceSnapshots assertEquals(2, consumedResources.size()); + ResourceSnapshot app1 = consumedResources.stream().filter(snapshot -> snapshot.getApplicationId().equals(ApplicationId.from("tenant1", "app1", "default"))).findFirst().orElseThrow(); + ResourceSnapshot app2 = consumedResources.stream().filter(snapshot -> snapshot.getApplicationId().equals(ApplicationId.from("tenant2", "app2", "default"))).findFirst().orElseThrow(); - ResourceSnapshot app1 = consumedResources.get(ApplicationId.from("tenant1", "app1", "default")); - ResourceSnapshot app2 = consumedResources.get(ApplicationId.from("tenant2", "app2", "default")); + assertEquals(24, app1.getCpuCores(), DELTA); + assertEquals(24, app1.getMemoryGb(), DELTA); + assertEquals(500, app1.getDiskGb(), DELTA); - assertEquals(24, app1.getResourceAllocation().getCpuCores(), DELTA); - assertEquals(24, app1.getResourceAllocation().getMemoryGb(), DELTA); - assertEquals(500, app1.getResourceAllocation().getDiskGb(), DELTA); - - assertEquals(40, app2.getResourceAllocation().getCpuCores(), DELTA); - assertEquals(24, app2.getResourceAllocation().getMemoryGb(), DELTA); - assertEquals(500, app2.getResourceAllocation().getDiskGb(), DELTA); + assertEquals(40, app2.getCpuCores(), DELTA); + assertEquals(24, app2.getMemoryGb(), DELTA); + assertEquals(500, app2.getDiskGb(), DELTA); assertEquals(tester.clock().millis()/1000, metrics.getMetric("metering_last_reported")); assertEquals(1112.0d, (Double) metrics.getMetric("metering_total_reported"), DELTA); |