From bdd5039b768f2ad055e362ba5d1bb3dc9fb30d51 Mon Sep 17 00:00:00 2001 From: Ola Aunrønning Date: Fri, 4 Oct 2019 11:54:11 +0200 Subject: Add zone dimension to metering data --- .../api/integration/resource/ResourceSnapshot.java | 14 +++++++-- .../maintenance/ResourceMeterMaintainer.java | 35 +++++++++++++++------- .../maintenance/ResourceMeterMaintainerTest.java | 25 +++++++++------- .../restapi/application/ApplicationApiTest.java | 6 ++-- 4 files changed, 53 insertions(+), 27 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 a378bcb63bd..5ee6df9f034 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 @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.controller.api.integration.resource; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; import java.time.Instant; @@ -17,14 +18,16 @@ public class ResourceSnapshot { private final ApplicationId applicationId; private final ResourceAllocation resourceAllocation; private final Instant timestamp; + private final ZoneId zoneId; - public ResourceSnapshot(ApplicationId applicationId, double cpuCores, double memoryGb, double diskGb, Instant timestamp) { + public ResourceSnapshot(ApplicationId applicationId, double cpuCores, double memoryGb, double diskGb, Instant timestamp, ZoneId zoneId) { this.applicationId = applicationId; this.resourceAllocation = new ResourceAllocation(cpuCores, memoryGb, diskGb); this.timestamp = timestamp; + this.zoneId = zoneId; } - public static ResourceSnapshot from(List nodes, Instant timestamp) { + public static ResourceSnapshot from(List nodes, Instant timestamp, ZoneId zoneId) { Set applicationIds = nodes.stream() .filter(node -> node.owner().isPresent()) .map(node -> node.owner().get()) @@ -37,7 +40,8 @@ public class ResourceSnapshot { nodes.stream().mapToDouble(Node::vcpu).sum(), nodes.stream().mapToDouble(Node::memoryGb).sum(), nodes.stream().mapToDouble(Node::diskGb).sum(), - timestamp + timestamp, + zoneId ); } @@ -61,4 +65,8 @@ public class ResourceSnapshot { return timestamp; } + public ZoneId getZoneId() { + return zoneId; + } + } 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 c700ddac51c..95b95bed70a 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 @@ -3,6 +3,8 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.zone.ZoneApi; +import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.jdisc.Metric; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; @@ -47,32 +49,43 @@ public class ResourceMeterMaintainer extends Maintainer { @Override protected void maintain() { - Collection resourceSnapshots = getResourceSnapshots(allocatedNodes()); + + Collection resourceSnapshots = getAllResourceSnapshots(); meteringClient.consume(resourceSnapshots); metric.set(METERING_LAST_REPORTED, clock.millis() / 1000, metric.createContext(Collections.emptyMap())); // total metered resource usage, for alerting on drastic changes metric.set(METERING_TOTAL_REPORTED, - resourceSnapshots.stream().mapToDouble(r -> r.getCpuCores() + r.getMemoryGb() + r.getDiskGb()).sum(), + resourceSnapshots.stream() + .mapToDouble(r -> r.getCpuCores() + r.getMemoryGb() + r.getDiskGb()).sum(), metric.createContext(Collections.emptyMap())); } - private List allocatedNodes() { + private Collection getAllResourceSnapshots() { return controller().zoneRegistry().zones() .ofCloud(CloudName.from("aws")) .reachable().zones().stream() - .flatMap(zone -> nodeRepository.list(zone.getId()).stream()) - .filter(node -> node.owner().isPresent()) - .filter(node -> ! node.owner().get().tenant().value().equals("hosted-vespa")) + .map(ZoneApi::getId) + .map(zoneId -> { + List nodes = nodeRepository.list(zoneId).stream() + .filter(node -> node.owner().isPresent()) + .filter(node -> ! node.owner().get().tenant().value().equals("hosted-vespa")) + .collect(Collectors.toList()); + return createResourceSnapshotsFromNodes(zoneId, nodes); + }) + .flatMap(Collection::stream) .collect(Collectors.toList()); } - private Collection getResourceSnapshots(List nodes) { + private Collection createResourceSnapshotsFromNodes(ZoneId zoneId, List nodes) { return nodes.stream() - .collect(Collectors.groupingBy(node -> node.owner().get(), - Collectors.collectingAndThen(Collectors.toList(), - nodeList -> ResourceSnapshot.from(nodeList, - clock.instant())) + .collect(Collectors.groupingBy(node -> + node.owner().get(), + Collectors.collectingAndThen(Collectors.toList(), + nodeList -> ResourceSnapshot.from( + nodeList, + clock.instant(), + zoneId)) )).values(); } 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 f28ce83e643..0245e7475f7 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 @@ -11,7 +11,6 @@ import org.junit.Test; import java.time.Duration; import java.util.Collection; -import java.util.List; import static org.junit.Assert.assertEquals; @@ -27,20 +26,14 @@ public class ResourceMeterMaintainerTest { @Test public void testMaintainer() { - var awsZone = ZoneApiMock.newBuilder().withId("prod.aws-us-east-1").withCloud("aws").build(); - tester.zoneRegistry().setZones( - ZoneApiMock.newBuilder().withId("prod.us-east-3").build(), - ZoneApiMock.newBuilder().withId("prod.us-west-1").build(), - ZoneApiMock.newBuilder().withId("prod.us-central-1").build(), - awsZone); - tester.configServer().nodeRepository().addFixedNodes(awsZone.getId()); + setUpZones(); ResourceMeterMaintainer resourceMeterMaintainer = new ResourceMeterMaintainer(tester.controller(), Duration.ofMinutes(5), new JobControl(tester.curator()), metrics, snapshotConsumer); resourceMeterMaintainer.maintain(); Collection consumedResources = snapshotConsumer.consumedResources(); // The mocked repository contains two applications, so we should also consume two ResourceSnapshots - assertEquals(2, consumedResources.size()); + assertEquals(4, 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(); @@ -53,7 +46,19 @@ public class ResourceMeterMaintainerTest { 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); + assertEquals(2224.0d, (Double) metrics.getMetric("metering_total_reported"), DELTA); } + private void setUpZones() { + ZoneApiMock nonAwsZone = ZoneApiMock.newBuilder().withId("test.region-1").build(); + ZoneApiMock awsZone1 = ZoneApiMock.newBuilder().withId("prod.region-2").withCloud("aws").build(); + ZoneApiMock awsZone2 = ZoneApiMock.newBuilder().withId("test.region-3").withCloud("aws").build(); + tester.zoneRegistry().setZones( + nonAwsZone, + awsZone1, + awsZone2); + tester.configServer().nodeRepository().addFixedNodes(nonAwsZone.getId()); + tester.configServer().nodeRepository().addFixedNodes(awsZone1.getId()); + tester.configServer().nodeRepository().addFixedNodes(awsZone2.getId()); + } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java index 836e3c07763..0d1a0643f0c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java @@ -943,9 +943,9 @@ public class ApplicationApiTest extends ControllerContainerTest { ResourceAllocation lastMonth = new ResourceAllocation(24, 48, 2000); ApplicationId applicationId = ApplicationId.from("doesnotexist", "doesnotexist", "default"); Map> snapshotHistory = Map.of(applicationId, List.of( - new ResourceSnapshot(applicationId, 1, 2,3, Instant.ofEpochMilli(123)), - new ResourceSnapshot(applicationId, 1, 2,3, Instant.ofEpochMilli(246)), - new ResourceSnapshot(applicationId, 1, 2,3, Instant.ofEpochMilli(492)))); + new ResourceSnapshot(applicationId, 1, 2,3, Instant.ofEpochMilli(123), ZoneId.defaultId()), + new ResourceSnapshot(applicationId, 1, 2,3, Instant.ofEpochMilli(246), ZoneId.defaultId()), + new ResourceSnapshot(applicationId, 1, 2,3, Instant.ofEpochMilli(492), ZoneId.defaultId()))); mockMeteringClient.setMeteringInfo(new MeteringInfo(thisMonth, lastMonth, currentSnapshot, snapshotHistory)); -- cgit v1.2.3