diff options
author | Øyvind Grønnesby <oyving@yahooinc.com> | 2022-04-06 09:02:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-06 09:02:51 +0200 |
commit | f70dd482c7f9dcafefb33cfb5d0de80153bcb9d2 (patch) | |
tree | 3993731a87689037f72ae961a83d59b8975adbc3 /controller-server | |
parent | 4dbf00faa83362bfe0f25ef1f2d689a4d58b05ab (diff) | |
parent | 51a4c78804b8d8f5bac4c4116a4f34f4375bcffa (diff) |
Merge pull request #21784 from vespa-engine/olaa/architecture-metering
Include architecture in metering
Diffstat (limited to 'controller-server')
9 files changed, 54 insertions, 105 deletions
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 acaf35133d7..d4905f7e20a 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 @@ -33,7 +33,9 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeoutException; +import java.util.function.Function; import java.util.logging.Level; +import java.util.stream.Collector; import java.util.stream.Collectors; /** @@ -150,14 +152,14 @@ public class ResourceMeterMaintainer extends ControllerMaintainer { .filter(this::unlessNodeOwnerIsSystemApplication) .filter(this::isNodeStateMeterable) .filter(this::isClusterTypeMeterable) + // Grouping by ApplicationId -> Architecture -> ResourceSnapshot .collect(Collectors.groupingBy(node -> - node.owner().get(), - Collectors.collectingAndThen(Collectors.toList(), - nodeList -> ResourceSnapshot.from( - nodeList, - clock.instant(), - zoneId)) - )).values(); + node.owner().get(), + groupSnapshotsByArchitecture(zoneId))) + .values() + .stream() + .flatMap(list -> list.values().stream()) + .collect(Collectors.toList()); } private boolean unlessNodeOwnerIsSystemApplication(Node node) { @@ -182,7 +184,7 @@ public class ResourceMeterMaintainer extends ControllerMaintainer { public static double cost(ClusterResources clusterResources, SystemName systemName) { NodeResources nr = clusterResources.nodeResources(); - return cost(new ResourceAllocation(nr.vcpu(), nr.memoryGb(), nr.diskGb()).multiply(clusterResources.nodes()), systemName); + return cost(new ResourceAllocation(nr.vcpu(), nr.memoryGb(), nr.diskGb(), nr.architecture()).multiply(clusterResources.nodes()), systemName); } private static double cost(ResourceAllocation allocation, SystemName systemName) { @@ -201,7 +203,7 @@ public class ResourceMeterMaintainer extends ControllerMaintainer { metric.createContext(Collections.emptyMap())); resourceSnapshots.forEach(snapshot -> { - var context = getMetricContext(snapshot.getApplicationId(), snapshot.getZoneId()); + var context = getMetricContext(snapshot); metric.set("metering.vcpu", snapshot.getCpuCores(), context); metric.set("metering.memoryGB", snapshot.getMemoryGb(), context); metric.set("metering.diskGB", snapshot.getDiskGb(), context); @@ -222,4 +224,29 @@ public class ResourceMeterMaintainer extends ControllerMaintainer { "zoneId", zoneId.value() )); } + + private Metric.Context getMetricContext(ResourceSnapshot snapshot) { + return metric.createContext(Map.of( + "tenant", snapshot.getApplicationId().tenant().value(), + "applicationId", snapshot.getApplicationId().toFullString(), + "zoneId", snapshot.getZoneId().value(), + "architecture", snapshot.getArchitecture() + )); + } + + private Collector<Node, ?, Map<NodeResources.Architecture, ResourceSnapshot>> groupSnapshotsByArchitecture(ZoneId zoneId) { + return Collectors.collectingAndThen( + Collectors.groupingBy(node -> node.resources().architecture()), + convertNodeListToResourceSnapshot(zoneId) + ); + } + + private Function<Map<NodeResources.Architecture, List<Node>>, Map<NodeResources.Architecture, ResourceSnapshot>> convertNodeListToResourceSnapshot(ZoneId zoneId) { + return nodeMap -> nodeMap.entrySet() + .stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> ResourceSnapshot.from(entry.getValue(), clock.instant(), zoneId)) + ); + } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/metric/CostCalculator.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/metric/CostCalculator.java index 089767dc586..b36b2b9cad8 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/metric/CostCalculator.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/metric/CostCalculator.java @@ -57,7 +57,7 @@ public class CostCalculator { Property property = propertyByTenantName.get(node.owner().get().tenant()); if (property == null) continue; var allocation = allocationByProperty.getOrDefault(property, ResourceAllocation.ZERO); - var nodeAllocation = new ResourceAllocation(node.resources().vcpu(), node.resources().memoryGb(), node.resources().diskGb()); + var nodeAllocation = new ResourceAllocation(node.resources().vcpu(), node.resources().memoryGb(), node.resources().diskGb(), node.resources().architecture()); allocationByProperty.put(property, allocation.plus(nodeAllocation)); totalAllocation = totalAllocation.plus(nodeAllocation); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index d9a38a5b578..9089bb27035 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -255,7 +255,6 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { if (path.matches("/application/v4/tenant/{tenant}/application/{application}/diff/{number}")) return applicationPackageDiff(path.get("tenant"), path.get("application"), path.get("number")); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/deploying")) return deploying(path.get("tenant"), path.get("application"), "default", request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/deploying/pin")) return deploying(path.get("tenant"), path.get("application"), "default", request); - if (path.matches("/application/v4/tenant/{tenant}/application/{application}/metering")) return metering(path.get("tenant"), path.get("application"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance")) return applications(path.get("tenant"), Optional.of(path.get("application")), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}")) return instance(path.get("tenant"), path.get("application"), path.get("instance"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/deploying")) return deploying(path.get("tenant"), path.get("application"), path.get("instance"), request); @@ -1762,68 +1761,6 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { return new SlimeJsonResponse(slime); } - private HttpResponse metering(String tenant, String application, HttpRequest request) { - - Slime slime = new Slime(); - Cursor root = slime.setObject(); - - MeteringData meteringData = controller.serviceRegistry() - .meteringService() - .getMeteringData(TenantName.from(tenant), ApplicationName.from(application)); - - ResourceAllocation currentSnapshot = meteringData.getCurrentSnapshot(); - Cursor currentRate = root.setObject("currentrate"); - currentRate.setDouble("cpu", currentSnapshot.getCpuCores()); - currentRate.setDouble("mem", currentSnapshot.getMemoryGb()); - currentRate.setDouble("disk", currentSnapshot.getDiskGb()); - - ResourceAllocation thisMonth = meteringData.getThisMonth(); - Cursor thismonth = root.setObject("thismonth"); - thismonth.setDouble("cpu", thisMonth.getCpuCores()); - thismonth.setDouble("mem", thisMonth.getMemoryGb()); - thismonth.setDouble("disk", thisMonth.getDiskGb()); - - ResourceAllocation lastMonth = meteringData.getLastMonth(); - Cursor lastmonth = root.setObject("lastmonth"); - lastmonth.setDouble("cpu", lastMonth.getCpuCores()); - lastmonth.setDouble("mem", lastMonth.getMemoryGb()); - lastmonth.setDouble("disk", lastMonth.getDiskGb()); - - - Map<ApplicationId, List<ResourceSnapshot>> history = meteringData.getSnapshotHistory(); - Cursor details = root.setObject("details"); - - Cursor detailsCpu = details.setObject("cpu"); - Cursor detailsMem = details.setObject("mem"); - Cursor detailsDisk = details.setObject("disk"); - - history.forEach((applicationId, resources) -> { - String instanceName = applicationId.instance().value(); - Cursor detailsCpuApp = detailsCpu.setObject(instanceName); - Cursor detailsMemApp = detailsMem.setObject(instanceName); - Cursor detailsDiskApp = detailsDisk.setObject(instanceName); - Cursor detailsCpuData = detailsCpuApp.setArray("data"); - Cursor detailsMemData = detailsMemApp.setArray("data"); - Cursor detailsDiskData = detailsDiskApp.setArray("data"); - - resources.forEach(resourceSnapshot -> { - Cursor cpu = detailsCpuData.addObject(); - cpu.setLong("unixms", resourceSnapshot.getTimestamp().toEpochMilli()); - cpu.setDouble("value", resourceSnapshot.getCpuCores()); - - Cursor mem = detailsMemData.addObject(); - mem.setLong("unixms", resourceSnapshot.getTimestamp().toEpochMilli()); - mem.setDouble("value", resourceSnapshot.getMemoryGb()); - - Cursor disk = detailsDiskData.addObject(); - disk.setLong("unixms", resourceSnapshot.getTimestamp().toEpochMilli()); - disk.setDouble("value", resourceSnapshot.getDiskGb()); - }); - }); - - return new SlimeJsonResponse(slime); - } - private HttpResponse deploying(String tenantName, String applicationName, String instanceName, HttpRequest request) { Instance instance = controller.applications().requireInstance(ApplicationId.from(tenantName, applicationName, instanceName)); Slime slime = new Slime(); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/MeteringResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/MeteringResponse.java index 850a9ef6107..33cd4948a7e 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/MeteringResponse.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/MeteringResponse.java @@ -32,6 +32,7 @@ public class MeteringResponse extends SlimeJsonResponse { object.setDouble("cpu", snapshot.getCpuCores()); object.setDouble("memory", snapshot.getMemoryGb()); object.setDouble("disk", snapshot.getDiskGb()); + object.setString("architecture", snapshot.getArchitecture().name()); }); return slime; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java index 80ee0988658..10193b48837 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java @@ -1,6 +1,7 @@ // Copyright Yahoo. 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.NodeResources; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.identifiers.Property; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation; @@ -38,7 +39,7 @@ public class CostReportMaintainerTest { "1970-01-01,Property3,128.0,96.0,2000.0,0.3333333333333333\n" + "1970-01-01,Property2,160.0,96.0,2000.0,0.3611111111111111", csv), - Map.of(new Property("Property3"), new ResourceAllocation(256, 192, 4000)) + Map.of(new Property("Property3"), new ResourceAllocation(256, 192, 4000, NodeResources.Architecture.getDefault())) ); 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 4acc9f91e79..6109890bae3 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 @@ -58,18 +58,18 @@ public class ResourceMeterMaintainerTest { .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().cost().getAsDouble()))); List<ResourceSnapshot> resourceSnapshots = List.of( - new ResourceSnapshot(app1, 12, 34, 56, Instant.EPOCH, z1), - new ResourceSnapshot(app1, 23, 45, 67, Instant.EPOCH, z2), - new ResourceSnapshot(app2, 34, 56, 78, Instant.EPOCH, z1)); + new ResourceSnapshot(app1, 12, 34, 56, NodeResources.Architecture.getDefault(), Instant.EPOCH, z1), + new ResourceSnapshot(app1, 23, 45, 67, NodeResources.Architecture.getDefault(), Instant.EPOCH, z2), + new ResourceSnapshot(app2, 34, 56, 78, NodeResources.Architecture.getDefault(), Instant.EPOCH, z1)); maintainer.updateDeploymentCost(resourceSnapshots); assertCost.accept(app1, Map.of(z1, 1.40, z2, 2.50)); assertCost.accept(app2, Map.of(z1, 3.59)); // Remove a region from app1 and add region to app2 resourceSnapshots = List.of( - new ResourceSnapshot(app1, 23, 45, 67, Instant.EPOCH, z2), - new ResourceSnapshot(app2, 34, 56, 78, Instant.EPOCH, z1), - new ResourceSnapshot(app2, 45, 67, 89, Instant.EPOCH, z2)); + new ResourceSnapshot(app1, 23, 45, 67, NodeResources.Architecture.getDefault(), Instant.EPOCH, z2), + new ResourceSnapshot(app2, 34, 56, 78, NodeResources.Architecture.getDefault(), Instant.EPOCH, z1), + new ResourceSnapshot(app2, 45, 67, 89, NodeResources.Architecture.getDefault(), Instant.EPOCH, z2)); maintainer.updateDeploymentCost(resourceSnapshots); assertCost.accept(app1, Map.of(z2, 2.50)); assertCost.accept(app2, Map.of(z1, 3.59, z2, 4.68)); 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 f94f87b0f46..cc162548f33 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 @@ -11,6 +11,7 @@ import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.AthenzService; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.InstanceName; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.zone.RoutingMethod; @@ -1069,27 +1070,6 @@ public class ApplicationApiTest extends ControllerContainerTest { new File("deploy-result.json")); } - @Test - public void testMeteringResponses() { - MockMeteringClient mockMeteringClient = tester.serviceRegistry().meteringService(); - - // Mock response for MeteringClient - ResourceAllocation currentSnapshot = new ResourceAllocation(1, 2, 3); - ResourceAllocation thisMonth = new ResourceAllocation(12, 24, 1000); - ResourceAllocation lastMonth = new ResourceAllocation(24, 48, 2000); - ApplicationId applicationId = ApplicationId.from("doesnotexist", "doesnotexist", "default"); - Map<ApplicationId, List<ResourceSnapshot>> snapshotHistory = Map.of(applicationId, List.of( - 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.setMeteringData(new MeteringData(thisMonth, lastMonth, currentSnapshot, snapshotHistory)); - - tester.assertResponse(request("/application/v4/tenant/doesnotexist/application/doesnotexist/metering", GET) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - new File("instance1-metering.json")); - } @Test public void testRemovingAllDeployments() { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java index 7bc01de2053..8a6244e19a0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.restapi.controller; import com.yahoo.application.container.handler.Request; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.test.ManualClock; @@ -158,8 +159,8 @@ public class ControllerApiTest extends ControllerContainerTest { Instant timestamp = Instant.ofEpochMilli(123456789); ZoneId zoneId = ZoneId.defaultId(); List<ResourceSnapshot> snapshots = List.of( - new ResourceSnapshot(applicationId, 12,48,1200, timestamp, zoneId), - new ResourceSnapshot(applicationId, 24, 96,2400, timestamp, zoneId) + new ResourceSnapshot(applicationId, 12,48,1200, NodeResources.Architecture.arm64, timestamp, zoneId), + new ResourceSnapshot(applicationId, 24, 96,2400, NodeResources.Architecture.x86_64, timestamp, zoneId) ); tester.controller().serviceRegistry().meteringService().consume(snapshots); tester.assertResponse( diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/metering.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/metering.json index b64e8f26a63..1008ada6def 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/metering.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/metering.json @@ -5,7 +5,8 @@ "zoneId": "prod.default", "cpu": 12.0, "memory": 48.0, - "disk": 1200.0 + "disk": 1200.0, + "architecture":"arm64" }, { "applicationId": "tenant.app.instance", @@ -13,6 +14,7 @@ "zoneId": "prod.default", "cpu": 24.0, "memory": 96.0, - "disk": 2400.0 + "disk": 2400.0, + "architecture":"x86_64" } ]
\ No newline at end of file |