From 7783175c9a82f175f589610e0cdf61ccc3c993db Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Sun, 12 May 2024 18:12:02 +0200 Subject: Move exclusivity decisions to config-provisioning --- config-provisioning/pom.xml | 6 +++ .../com/yahoo/config/provision/Exclusivity.java | 44 ++++++++++++++++++++++ .../vespa/hosted/provision/NodeRepository.java | 36 ++++-------------- .../provision/autoscale/AllocatableResources.java | 4 +- .../provision/autoscale/AllocationOptimizer.java | 2 +- .../hosted/provision/autoscale/ClusterModel.java | 4 +- .../maintenance/HostCapacityMaintainer.java | 8 ++-- .../provision/provisioning/CapacityPolicies.java | 2 +- .../provision/provisioning/NodeAllocation.java | 6 +-- .../provisioning/NodeRepositoryProvisioner.java | 2 +- .../provision/provisioning/NodeResourceLimits.java | 4 +- .../hosted/provision/provisioning/Preparer.java | 6 +-- .../provision/testutils/MockNodeRepository.java | 2 + .../hosted/provision/NodeRepositoryTester.java | 5 ++- .../maintenance/CapacityCheckerTester.java | 5 ++- .../maintenance/SpareCapacityMaintainerTest.java | 8 +++- .../provision/provisioning/ProvisioningTester.java | 2 + 17 files changed, 95 insertions(+), 51 deletions(-) create mode 100644 config-provisioning/src/main/java/com/yahoo/config/provision/Exclusivity.java diff --git a/config-provisioning/pom.xml b/config-provisioning/pom.xml index 75047ac8efa..63fb2f30ba6 100644 --- a/config-provisioning/pom.xml +++ b/config-provisioning/pom.xml @@ -52,6 +52,12 @@ ${project.version} provided + + com.yahoo.vespa + flags + ${project.version} + provided + com.yahoo.vespa testutil diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Exclusivity.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Exclusivity.java new file mode 100644 index 00000000000..40122c17c3f --- /dev/null +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Exclusivity.java @@ -0,0 +1,44 @@ +package com.yahoo.config.provision; + +import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.flags.JacksonFlag; +import com.yahoo.vespa.flags.PermanentFlags; +import com.yahoo.vespa.flags.custom.SharedHost; + +/** + * A class which can be asked if allocations should be exclusive. + * + * @author bratseth + */ +public class Exclusivity { + + private final Zone zone; + private final JacksonFlag sharedHosts; + + public Exclusivity(Zone zone, FlagSource flagSource) { + this.zone = zone; + this.sharedHosts = PermanentFlags.SHARED_HOST.bindTo(flagSource); + } + + /** Returns whether nodes must be allocated to hosts that are exclusive to the cluster type. */ + public boolean clusterType(ClusterSpec cluster) { + return sharedHosts.value().hasClusterType(cluster.type().name()); + } + + /** Returns whether the nodes of this cluster must be running on hosts that are specifically provisioned for the application. */ + public boolean provisioning(ClusterSpec clusterSpec) { + return !zone.cloud().allowHostSharing() && clusterSpec.isExclusive(); + } + + /** + * Returns whether nodes are allocated exclusively in this instance given this cluster spec. + * Exclusive allocation requires that the wanted node resources matches the advertised resources of the node + * perfectly. + */ + public boolean allocation(ClusterSpec clusterSpec) { + return clusterSpec.isExclusive() || + ( clusterSpec.type().isContainer() && zone.system().isPublic() && !zone.environment().isTest() ) || + ( !zone.cloud().allowHostSharing() && !sharedHosts.value().supportsClusterType(clusterSpec.type().name())); + } + +} diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java index 539f3128091..06528ef557e 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java @@ -5,24 +5,20 @@ import com.yahoo.component.AbstractComponent; import com.yahoo.component.annotation.Inject; import com.yahoo.concurrent.maintenance.JobControl; import com.yahoo.config.provision.ApplicationTransaction; -import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.EndpointsChecker.HealthChecker; import com.yahoo.config.provision.EndpointsChecker.HealthCheckerProvider; +import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.Zone; import com.yahoo.config.provisioning.NodeRepositoryConfig; import com.yahoo.vespa.curator.Curator; import com.yahoo.vespa.flags.FlagSource; -import com.yahoo.vespa.flags.JacksonFlag; -import com.yahoo.vespa.flags.PermanentFlags; -import com.yahoo.vespa.flags.custom.SharedHost; import com.yahoo.vespa.hosted.provision.Node.State; import com.yahoo.vespa.hosted.provision.applications.Applications; import com.yahoo.vespa.hosted.provision.archive.ArchiveUriManager; import com.yahoo.vespa.hosted.provision.autoscale.MetricsDb; import com.yahoo.vespa.hosted.provision.lb.LoadBalancer; -import com.yahoo.vespa.hosted.provision.lb.LoadBalancerInstance; import com.yahoo.vespa.hosted.provision.lb.LoadBalancers; import com.yahoo.vespa.hosted.provision.maintenance.InfrastructureVersions; import com.yahoo.vespa.hosted.provision.node.Agent; @@ -55,6 +51,7 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr private final CuratorDb db; private final Clock clock; private final Zone zone; + private final Exclusivity exclusivity; private final Nodes nodes; private final NodeFlavors flavors; private final HostResourcesCalculator resourcesCalculator; @@ -73,7 +70,6 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr private final Orchestrator orchestrator; private final int spareCount; private final ProtoHealthChecker healthChecker; - private final JacksonFlag sharedHosts; /** * Creates a node repository from a zookeeper provider. @@ -85,6 +81,7 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr ProvisionServiceProvider provisionServiceProvider, Curator curator, Zone zone, + Exclusivity exclusivity, FlagSource flagSource, MetricsDb metricsDb, Orchestrator orchestrator) { @@ -93,6 +90,7 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr curator, Clock.systemUTC(), zone, + exclusivity, new DnsNameResolver(), DockerImage.fromString(config.containerImage()), optionalImage(config.tenantContainerImage()), @@ -113,6 +111,7 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr Curator curator, Clock clock, Zone zone, + Exclusivity exclusivity, NameResolver nameResolver, DockerImage containerImage, Optional tenantContainerImage, @@ -129,8 +128,9 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr this.flagSource = flagSource; this.db = new CuratorDb(flavors, curator, clock, useCuratorClientCache); - this.zone = zone; this.clock = clock; + this.zone = zone; + this.exclusivity = exclusivity; this.applications = new Applications(db); this.nodes = new Nodes(db, zone, clock, orchestrator, applications); this.flavors = flavors; @@ -147,7 +147,6 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr this.metricsDb = metricsDb; this.orchestrator = orchestrator; this.spareCount = spareCount; - this.sharedHosts = PermanentFlags.SHARED_HOST.bindTo(flagSource()); this.healthChecker = provisionServiceProvider.getHealthChecker(); nodes.rewrite(); } @@ -208,26 +207,7 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr /** The number of nodes we should ensure has free capacity for node failures whenever possible */ public int spareCount() { return spareCount; } - /** Returns whether nodes must be allocated to hosts that are exclusive to the cluster type. */ - public boolean exclusiveClusterType(ClusterSpec cluster) { - return sharedHosts.value().hasClusterType(cluster.type().name()); - } - - /** - * Returns whether nodes are allocated exclusively in this instance given this cluster spec. - * Exclusive allocation requires that the wanted node resources matches the advertised resources of the node - * perfectly. - */ - public boolean exclusiveAllocation(ClusterSpec clusterSpec) { - return clusterSpec.isExclusive() || - ( clusterSpec.type().isContainer() && zone.system().isPublic() && !zone.environment().isTest() ) || - ( !zone().cloud().allowHostSharing() && !sharedHosts.value().supportsClusterType(clusterSpec.type().name())); - } - - /** Whether the nodes of this cluster must be running on hosts that are specifically provisioned for the application. */ - public boolean exclusiveProvisioning(ClusterSpec clusterSpec) { - return !zone.cloud().allowHostSharing() && clusterSpec.isExclusive(); - } + public Exclusivity exclusiveAllocation() { return exclusivity; } /** * Returns ACLs for the children of the given host. diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java index 75a00fa951e..9ac9ffcf29a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java @@ -41,7 +41,7 @@ public class AllocatableResources { this.nodes = requested.nodes(); this.groups = requested.groups(); this.realResources = nodeRepository.resourcesCalculator().requestToReal(requested.nodeResources(), cloudAccount, - nodeRepository.exclusiveAllocation(clusterSpec), false); + nodeRepository.exclusiveAllocation().allocation(clusterSpec), false); this.advertisedResources = requested.nodeResources(); this.clusterSpec = clusterSpec; this.fulfilment = 1; @@ -178,7 +178,7 @@ public class AllocatableResources { ClusterModel model, NodeRepository nodeRepository) { var systemLimits = nodeRepository.nodeResourceLimits(); - boolean exclusive = nodeRepository.exclusiveAllocation(clusterSpec); + boolean exclusive = nodeRepository.exclusiveAllocation().allocation(clusterSpec); if (! exclusive) { // We decide resources: Add overhead to what we'll request (advertised) to make sure real becomes (at least) cappedNodeResources var allocatableResources = calculateAllocatableResources(wantedResources, 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 61d4ced1367..da5c99a7f68 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 @@ -95,7 +95,7 @@ public class AllocationOptimizer { /** Returns the max resources of a host one node may allocate. */ private NodeResources maxResourcesOf(NodeResources hostResources, ClusterModel model) { - if (nodeRepository.exclusiveAllocation(model.clusterSpec())) return hostResources; + if (nodeRepository.exclusiveAllocation().allocation(model.clusterSpec())) return hostResources; // static, shared hosts: Allocate at most half of the host cpu to simplify management return hostResources.withVcpu(hostResources.vcpu() / 2); } 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 74907feee41..5132597a373 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 @@ -171,7 +171,7 @@ public class ClusterModel { } public boolean isExclusive() { - return nodeRepository.exclusiveAllocation(clusterSpec); + return nodeRepository.exclusiveAllocation().allocation(clusterSpec); } /** Returns the relative load adjustment that should be made to this cluster given available measurements. */ @@ -441,7 +441,7 @@ public class ClusterModel { application.id()); return nodeRepository.resourcesCalculator().requestToReal(initialResources, cloudAccount(), - nodeRepository.exclusiveAllocation(clusterSpec), + nodeRepository.exclusiveAllocation().allocation(clusterSpec), false).memoryGb(); } else { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java index 98f0dfebc4a..5628c341bbe 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java @@ -223,7 +223,7 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer { } Version osVersion = nodeRepository().osVersions().targetFor(NodeType.host).orElse(Version.emptyVersion); List provisionIndices = nodeRepository().database().readProvisionIndices(count); - HostSharing sharingMode = nodeRepository().exclusiveAllocation(asSpec(clusterType, 0)) ? HostSharing.exclusive : HostSharing.shared; + HostSharing sharingMode = nodeRepository().exclusiveAllocation().allocation(asSpec(clusterType, 0)) ? HostSharing.exclusive : HostSharing.shared; HostProvisionRequest request = new HostProvisionRequest(provisionIndices, NodeType.host, nodeResources, ApplicationId.defaultId(), osVersion, sharingMode, clusterType.map(ClusterSpec.Type::valueOf), Optional.empty(), @@ -290,13 +290,13 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer { NodePrioritizer prioritizer = new NodePrioritizer(allNodes, application, cluster, nodeSpec, true, false, allocationContext, nodeRepository().nodes(), nodeRepository().resourcesCalculator(), nodeRepository().spareCount(), - nodeRepository().exclusiveAllocation(cluster)); + nodeRepository().exclusiveAllocation().allocation(cluster)); List nodeCandidates = prioritizer.collect() .stream() .filter(node -> node.violatesExclusivity(cluster, application, - nodeRepository().exclusiveClusterType(cluster), - nodeRepository().exclusiveAllocation(cluster), + nodeRepository().exclusiveAllocation().clusterType(cluster), + nodeRepository().exclusiveAllocation().allocation(cluster), false, nodeRepository().zone().cloud().allowHostSharing(), allNodes) diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java index 46dfb51a433..de38ae0d861 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java @@ -106,7 +106,7 @@ public class CapacityPolicies { if (clusterSpec.type() == ClusterSpec.Type.admin) { Architecture architecture = adminClusterArchitecture(applicationId); - if (nodeRepository.exclusiveAllocation(clusterSpec)) { + if (nodeRepository.exclusiveAllocation().allocation(clusterSpec)) { return smallestExclusiveResources().with(architecture); } 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 b149a9af2c2..52a8bbfcf2c 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 @@ -206,9 +206,9 @@ class NodeAllocation { private NodeCandidate.ExclusivityViolation violatesExclusivity(NodeCandidate candidate) { return candidate.violatesExclusivity(cluster, application, - nodeRepository.exclusiveClusterType(cluster), - nodeRepository.exclusiveAllocation(cluster), - nodeRepository.exclusiveProvisioning(cluster), + nodeRepository.exclusiveAllocation().clusterType(cluster), + nodeRepository.exclusiveAllocation().allocation(cluster), + nodeRepository.exclusiveAllocation().provisioning(cluster), nodeRepository.zone().cloud().allowHostSharing(), allNodes); } 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 7ac80dfbdb3..44d667954f0 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 @@ -278,7 +278,7 @@ public class NodeRepositoryProvisioner implements Provisioner { private IllegalArgumentException newNoAllocationPossible(ClusterSpec spec, Limits limits) { StringBuilder message = new StringBuilder("No allocation possible within ").append(limits); - if (nodeRepository.exclusiveAllocation(spec) && findNearestNodeResources(limits).isPresent()) + if (nodeRepository.exclusiveAllocation().allocation(spec) && findNearestNodeResources(limits).isPresent()) message.append(". Nearest allowed node resources: ").append(findNearestNodeResources(limits).get()); return new IllegalArgumentException(message.toString()); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java index 194207ca69d..a085d0a2e68 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java @@ -27,7 +27,7 @@ public class NodeResourceLimits { /** Validates the resources applications ask for (which are in "advertised" resource space) */ public void ensureWithinAdvertisedLimits(String type, NodeResources requested, ClusterSpec cluster) { - boolean exclusive = nodeRepository.exclusiveAllocation(cluster); + boolean exclusive = nodeRepository.exclusiveAllocation().allocation(cluster); if (! requested.vcpuIsUnspecified() && requested.vcpu() < minAdvertisedVcpu(cluster, exclusive)) illegal(type, "vcpu", "", cluster, requested.vcpu(), minAdvertisedVcpu(cluster, exclusive)); if (! requested.memoryGbIsUnspecified() && requested.memoryGb() < minAdvertisedMemoryGb(cluster, exclusive)) @@ -104,7 +104,7 @@ public class NodeResourceLimits { } private double minRealVcpu(ClusterSpec cluster) { - return minAdvertisedVcpu(cluster, nodeRepository.exclusiveAllocation(cluster)); + return minAdvertisedVcpu(cluster, nodeRepository.exclusiveAllocation().allocation(cluster)); } private static double minRealMemoryGb(ClusterSpec cluster) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java index 0206c3a4a26..dedd023b0e6 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java @@ -202,7 +202,7 @@ public class Preparer { nodeRepository.nodes(), nodeRepository.resourcesCalculator(), nodeRepository.spareCount(), - nodeRepository.exclusiveAllocation(cluster)); + nodeRepository.exclusiveAllocation().allocation(cluster)); allocation.offer(prioritizer.collect()); if (requested.type() == NodeType.tenant && !requested.canFail() && allocation.changes()) { // This should not happen and indicates a bug in the allocation code because boostrap redeployment @@ -237,8 +237,8 @@ public class Preparer { private HostSharing hostSharing(ClusterSpec cluster, NodeType hostType) { if ( hostType.isSharable()) - return nodeRepository.exclusiveProvisioning(cluster) ? HostSharing.provision : - nodeRepository.exclusiveAllocation(cluster) ? HostSharing.exclusive : + return nodeRepository.exclusiveAllocation().provisioning(cluster) ? HostSharing.provision : + nodeRepository.exclusiveAllocation().allocation(cluster) ? HostSharing.exclusive : HostSharing.any; else return HostSharing.any; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java index 9d9771b3b0f..17b7dd6c307 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java @@ -12,6 +12,7 @@ import com.yahoo.config.provision.ClusterInfo; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DockerImage; +import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.InstanceName; @@ -90,6 +91,7 @@ public class MockNodeRepository extends NodeRepository { curator, Clock.fixed(Instant.ofEpochMilli(123), ZoneId.of("Z")), zone, + new Exclusivity(zone, new InMemoryFlagSource()), new MockNameResolver().mockAnyLookup(), DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), Optional.empty(), diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java index 79215028dba..b9b3d9b1268 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.provision; import com.yahoo.config.provision.DockerImage; +import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeType; @@ -40,16 +41,18 @@ public class NodeRepositoryTester { clock = new ManualClock(); curator = new MockCurator(); curator.setZooKeeperEnsembleConnectionSpec("server1:1234,server2:5678"); + var flagSource = new InMemoryFlagSource(); nodeRepository = new NodeRepository(nodeFlavors, new EmptyProvisionServiceProvider(), curator, clock, zone, + new Exclusivity(zone, flagSource), new MockNameResolver().mockAnyLookup(), DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), Optional.empty(), Optional.empty(), - new InMemoryFlagSource(), + flagSource, new MemoryMetricsDb(clock), new OrchestratorMock(), true, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java index 1de8a766ea9..ca1e47f8ea2 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java @@ -1,6 +1,7 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.provision.maintenance; +import com.yahoo.config.provision.Exclusivity; import com.yahoo.json.Jackson; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -63,16 +64,18 @@ public class CapacityCheckerTester { CapacityCheckerTester() { Curator curator = new MockCurator(); NodeFlavors f = new NodeFlavors(new FlavorConfigBuilder().build()); + var flagSource = new InMemoryFlagSource(); nodeRepository = new NodeRepository(f, new EmptyProvisionServiceProvider(), curator, clock, zone, + new Exclusivity(zone, flagSource), new MockNameResolver().mockAnyLookup(), DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), Optional.empty(), Optional.empty(), - new InMemoryFlagSource(), + flagSource, new MemoryMetricsDb(clock), new OrchestratorMock(), true, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java index 9fefc9d34e1..78f5d8c9b65 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeResources; @@ -258,16 +259,19 @@ public class SpareCapacityMaintainerTest { private SpareCapacityMaintainerTester(int maxIterations) { NodeFlavors flavors = new NodeFlavors(new FlavorConfigBuilder().build()); ManualClock clock = new ManualClock(); + var zone = new Zone(Environment.prod, RegionName.from("us-east-3")); + var flagSource = new InMemoryFlagSource(); nodeRepository = new NodeRepository(flavors, new EmptyProvisionServiceProvider(), new MockCurator(), clock, - new Zone(Environment.prod, RegionName.from("us-east-3")), + zone, + new Exclusivity(zone, flagSource), new MockNameResolver().mockAnyLookup(), DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), Optional.empty(), Optional.empty(), - new InMemoryFlagSource(), + flagSource, new MemoryMetricsDb(clock), new OrchestratorMock(), true, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java index 4ec290dd7ba..8d6a378d223 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java @@ -14,6 +14,7 @@ import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.HostSpec; @@ -121,6 +122,7 @@ public class ProvisioningTester { curator, clock, zone, + new Exclusivity(zone, flagSource), nameResolver, containerImage, Optional.empty(), -- cgit v1.2.3 From 00b6a2b9cf73cd6ac4308ec6cf8e623c809623ee Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Sun, 12 May 2024 18:13:10 +0200 Subject: Simplify method name --- .../java/com/yahoo/vespa/hosted/provision/NodeRepository.java | 2 +- .../vespa/hosted/provision/autoscale/AllocatableResources.java | 4 ++-- .../vespa/hosted/provision/autoscale/AllocationOptimizer.java | 2 +- .../com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java | 4 ++-- .../hosted/provision/maintenance/HostCapacityMaintainer.java | 8 ++++---- .../vespa/hosted/provision/provisioning/CapacityPolicies.java | 2 +- .../yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java | 6 +++--- .../hosted/provision/provisioning/NodeRepositoryProvisioner.java | 2 +- .../vespa/hosted/provision/provisioning/NodeResourceLimits.java | 4 ++-- .../com/yahoo/vespa/hosted/provision/provisioning/Preparer.java | 6 +++--- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java index 06528ef557e..086f66fd16b 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java @@ -207,7 +207,7 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr /** The number of nodes we should ensure has free capacity for node failures whenever possible */ public int spareCount() { return spareCount; } - public Exclusivity exclusiveAllocation() { return exclusivity; } + public Exclusivity exclusivity() { return exclusivity; } /** * Returns ACLs for the children of the given host. diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java index 9ac9ffcf29a..98c5af2688a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableResources.java @@ -41,7 +41,7 @@ public class AllocatableResources { this.nodes = requested.nodes(); this.groups = requested.groups(); this.realResources = nodeRepository.resourcesCalculator().requestToReal(requested.nodeResources(), cloudAccount, - nodeRepository.exclusiveAllocation().allocation(clusterSpec), false); + nodeRepository.exclusivity().allocation(clusterSpec), false); this.advertisedResources = requested.nodeResources(); this.clusterSpec = clusterSpec; this.fulfilment = 1; @@ -178,7 +178,7 @@ public class AllocatableResources { ClusterModel model, NodeRepository nodeRepository) { var systemLimits = nodeRepository.nodeResourceLimits(); - boolean exclusive = nodeRepository.exclusiveAllocation().allocation(clusterSpec); + boolean exclusive = nodeRepository.exclusivity().allocation(clusterSpec); if (! exclusive) { // We decide resources: Add overhead to what we'll request (advertised) to make sure real becomes (at least) cappedNodeResources var allocatableResources = calculateAllocatableResources(wantedResources, 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 da5c99a7f68..9b7d724abb5 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 @@ -95,7 +95,7 @@ public class AllocationOptimizer { /** Returns the max resources of a host one node may allocate. */ private NodeResources maxResourcesOf(NodeResources hostResources, ClusterModel model) { - if (nodeRepository.exclusiveAllocation().allocation(model.clusterSpec())) return hostResources; + if (nodeRepository.exclusivity().allocation(model.clusterSpec())) return hostResources; // static, shared hosts: Allocate at most half of the host cpu to simplify management return hostResources.withVcpu(hostResources.vcpu() / 2); } 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 5132597a373..2173e9a4aa9 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 @@ -171,7 +171,7 @@ public class ClusterModel { } public boolean isExclusive() { - return nodeRepository.exclusiveAllocation().allocation(clusterSpec); + return nodeRepository.exclusivity().allocation(clusterSpec); } /** Returns the relative load adjustment that should be made to this cluster given available measurements. */ @@ -441,7 +441,7 @@ public class ClusterModel { application.id()); return nodeRepository.resourcesCalculator().requestToReal(initialResources, cloudAccount(), - nodeRepository.exclusiveAllocation().allocation(clusterSpec), + nodeRepository.exclusivity().allocation(clusterSpec), false).memoryGb(); } else { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java index 5628c341bbe..c9e920dc243 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java @@ -223,7 +223,7 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer { } Version osVersion = nodeRepository().osVersions().targetFor(NodeType.host).orElse(Version.emptyVersion); List provisionIndices = nodeRepository().database().readProvisionIndices(count); - HostSharing sharingMode = nodeRepository().exclusiveAllocation().allocation(asSpec(clusterType, 0)) ? HostSharing.exclusive : HostSharing.shared; + HostSharing sharingMode = nodeRepository().exclusivity().allocation(asSpec(clusterType, 0)) ? HostSharing.exclusive : HostSharing.shared; HostProvisionRequest request = new HostProvisionRequest(provisionIndices, NodeType.host, nodeResources, ApplicationId.defaultId(), osVersion, sharingMode, clusterType.map(ClusterSpec.Type::valueOf), Optional.empty(), @@ -290,13 +290,13 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer { NodePrioritizer prioritizer = new NodePrioritizer(allNodes, application, cluster, nodeSpec, true, false, allocationContext, nodeRepository().nodes(), nodeRepository().resourcesCalculator(), nodeRepository().spareCount(), - nodeRepository().exclusiveAllocation().allocation(cluster)); + nodeRepository().exclusivity().allocation(cluster)); List nodeCandidates = prioritizer.collect() .stream() .filter(node -> node.violatesExclusivity(cluster, application, - nodeRepository().exclusiveAllocation().clusterType(cluster), - nodeRepository().exclusiveAllocation().allocation(cluster), + nodeRepository().exclusivity().clusterType(cluster), + nodeRepository().exclusivity().allocation(cluster), false, nodeRepository().zone().cloud().allowHostSharing(), allNodes) diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java index de38ae0d861..79e65b21003 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java @@ -106,7 +106,7 @@ public class CapacityPolicies { if (clusterSpec.type() == ClusterSpec.Type.admin) { Architecture architecture = adminClusterArchitecture(applicationId); - if (nodeRepository.exclusiveAllocation().allocation(clusterSpec)) { + if (nodeRepository.exclusivity().allocation(clusterSpec)) { return smallestExclusiveResources().with(architecture); } 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 52a8bbfcf2c..433bd0bbefe 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 @@ -206,9 +206,9 @@ class NodeAllocation { private NodeCandidate.ExclusivityViolation violatesExclusivity(NodeCandidate candidate) { return candidate.violatesExclusivity(cluster, application, - nodeRepository.exclusiveAllocation().clusterType(cluster), - nodeRepository.exclusiveAllocation().allocation(cluster), - nodeRepository.exclusiveAllocation().provisioning(cluster), + nodeRepository.exclusivity().clusterType(cluster), + nodeRepository.exclusivity().allocation(cluster), + nodeRepository.exclusivity().provisioning(cluster), nodeRepository.zone().cloud().allowHostSharing(), allNodes); } 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 44d667954f0..596afc3cb6b 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 @@ -278,7 +278,7 @@ public class NodeRepositoryProvisioner implements Provisioner { private IllegalArgumentException newNoAllocationPossible(ClusterSpec spec, Limits limits) { StringBuilder message = new StringBuilder("No allocation possible within ").append(limits); - if (nodeRepository.exclusiveAllocation().allocation(spec) && findNearestNodeResources(limits).isPresent()) + if (nodeRepository.exclusivity().allocation(spec) && findNearestNodeResources(limits).isPresent()) message.append(". Nearest allowed node resources: ").append(findNearestNodeResources(limits).get()); return new IllegalArgumentException(message.toString()); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java index a085d0a2e68..ba22aa5a75a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java @@ -27,7 +27,7 @@ public class NodeResourceLimits { /** Validates the resources applications ask for (which are in "advertised" resource space) */ public void ensureWithinAdvertisedLimits(String type, NodeResources requested, ClusterSpec cluster) { - boolean exclusive = nodeRepository.exclusiveAllocation().allocation(cluster); + boolean exclusive = nodeRepository.exclusivity().allocation(cluster); if (! requested.vcpuIsUnspecified() && requested.vcpu() < minAdvertisedVcpu(cluster, exclusive)) illegal(type, "vcpu", "", cluster, requested.vcpu(), minAdvertisedVcpu(cluster, exclusive)); if (! requested.memoryGbIsUnspecified() && requested.memoryGb() < minAdvertisedMemoryGb(cluster, exclusive)) @@ -104,7 +104,7 @@ public class NodeResourceLimits { } private double minRealVcpu(ClusterSpec cluster) { - return minAdvertisedVcpu(cluster, nodeRepository.exclusiveAllocation().allocation(cluster)); + return minAdvertisedVcpu(cluster, nodeRepository.exclusivity().allocation(cluster)); } private static double minRealMemoryGb(ClusterSpec cluster) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java index dedd023b0e6..ec5c807c9b5 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java @@ -202,7 +202,7 @@ public class Preparer { nodeRepository.nodes(), nodeRepository.resourcesCalculator(), nodeRepository.spareCount(), - nodeRepository.exclusiveAllocation().allocation(cluster)); + nodeRepository.exclusivity().allocation(cluster)); allocation.offer(prioritizer.collect()); if (requested.type() == NodeType.tenant && !requested.canFail() && allocation.changes()) { // This should not happen and indicates a bug in the allocation code because boostrap redeployment @@ -237,8 +237,8 @@ public class Preparer { private HostSharing hostSharing(ClusterSpec cluster, NodeType hostType) { if ( hostType.isSharable()) - return nodeRepository.exclusiveAllocation().provisioning(cluster) ? HostSharing.provision : - nodeRepository.exclusiveAllocation().allocation(cluster) ? HostSharing.exclusive : + return nodeRepository.exclusivity().provisioning(cluster) ? HostSharing.provision : + nodeRepository.exclusivity().allocation(cluster) ? HostSharing.exclusive : HostSharing.any; else return HostSharing.any; -- cgit v1.2.3 From 4cdc7e11b72be1752e5624dd28d6654cd54475d9 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Sun, 12 May 2024 18:21:07 +0200 Subject: Make CapacityPolicies independent of NodeRepository --- .../vespa/hosted/provision/autoscale/ClusterModel.java | 9 ++++++--- .../yahoo/vespa/hosted/provision/autoscale/Limits.java | 2 +- .../hosted/provision/provisioning/CapacityPolicies.java | 15 ++++++++------- .../provision/provisioning/NodeRepositoryProvisioner.java | 2 +- .../vespa/hosted/provision/autoscale/AutoscalingTest.java | 5 ++++- .../provision/provisioning/DynamicProvisioningTester.java | 2 +- .../hosted/provision/provisioning/ProvisioningTester.java | 2 +- 7 files changed, 22 insertions(+), 15 deletions(-) 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 2173e9a4aa9..ece84cfd909 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 @@ -48,6 +48,7 @@ public class ClusterModel { private static final double fixedCpuCostFraction = 0.1; private final NodeRepository nodeRepository; + private final CapacityPolicies capacityPolicies; private final Application application; private final ClusterSpec clusterSpec; private final Cluster cluster; @@ -84,6 +85,7 @@ public class ClusterModel { MetricsDb metricsDb, Clock clock) { this.nodeRepository = nodeRepository; + this.capacityPolicies = new CapacityPolicies(nodeRepository.zone(), nodeRepository.exclusivity(), nodeRepository.flagSource()); this.application = application; this.clusterSpec = clusterSpec; this.cluster = cluster; @@ -108,6 +110,7 @@ public class ClusterModel { ClusterTimeseries clusterTimeseries, ClusterNodesTimeseries nodeTimeseries) { this.nodeRepository = nodeRepository; + this.capacityPolicies = new CapacityPolicies(nodeRepository.zone(), nodeRepository.exclusivity(), nodeRepository.flagSource()); this.application = application; this.clusterSpec = clusterSpec; this.cluster = cluster; @@ -436,9 +439,9 @@ public class ClusterModel { double averageReal() { if (nodes.isEmpty()) { // we're estimating - var initialResources = new CapacityPolicies(nodeRepository).specifyFully(cluster.minResources().nodeResources(), - clusterSpec, - application.id()); + var initialResources = capacityPolicies.specifyFully(cluster.minResources().nodeResources(), + clusterSpec, + application.id()); return nodeRepository.resourcesCalculator().requestToReal(initialResources, cloudAccount(), nodeRepository.exclusivity().allocation(clusterSpec), diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java index ab93e585c88..2cd06706d7f 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java @@ -68,7 +68,7 @@ public class Limits { public Limits fullySpecified(ClusterSpec clusterSpec, NodeRepository nodeRepository, ApplicationId applicationId) { if (this.isEmpty()) throw new IllegalStateException("Unspecified limits can not be made fully specified"); - var capacityPolicies = new CapacityPolicies(nodeRepository); + var capacityPolicies = new CapacityPolicies(nodeRepository.zone(), nodeRepository.exclusivity(), nodeRepository.flagSource()); return new Limits(capacityPolicies.specifyFully(min, clusterSpec, applicationId), capacityPolicies.specifyFully(max, clusterSpec, applicationId), groupSize); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java index 79e65b21003..bb9aaccd1e3 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java @@ -8,12 +8,13 @@ import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeResources.DiskSpeed; import com.yahoo.config.provision.Zone; +import com.yahoo.vespa.flags.FlagSource; import com.yahoo.vespa.flags.PermanentFlags; import com.yahoo.vespa.flags.StringFlag; -import com.yahoo.vespa.hosted.provision.NodeRepository; import java.util.Map; import java.util.TreeMap; @@ -30,14 +31,14 @@ import static java.util.Objects.requireNonNull; */ public class CapacityPolicies { - private final NodeRepository nodeRepository; private final Zone zone; + private final Exclusivity exclusivity; private final StringFlag adminClusterNodeArchitecture; - public CapacityPolicies(NodeRepository nodeRepository) { - this.nodeRepository = nodeRepository; - this.zone = nodeRepository.zone(); - this.adminClusterNodeArchitecture = PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(nodeRepository.flagSource()); + public CapacityPolicies(Zone zone, Exclusivity exclusivity, FlagSource flagSource) { + this.zone = zone; + this.exclusivity = exclusivity; + this.adminClusterNodeArchitecture = PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(flagSource); } public Capacity applyOn(Capacity capacity, ApplicationId application, boolean exclusive) { @@ -106,7 +107,7 @@ public class CapacityPolicies { if (clusterSpec.type() == ClusterSpec.Type.admin) { Architecture architecture = adminClusterArchitecture(applicationId); - if (nodeRepository.exclusivity().allocation(clusterSpec)) { + if (exclusivity.allocation(clusterSpec)) { return smallestExclusiveResources().with(architecture); } 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 596afc3cb6b..89c508f7fa8 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 @@ -66,7 +66,7 @@ public class NodeRepositoryProvisioner implements Provisioner { Metric metric) { this.nodeRepository = nodeRepository; this.allocationOptimizer = new AllocationOptimizer(nodeRepository); - this.capacityPolicies = new CapacityPolicies(nodeRepository); + this.capacityPolicies = new CapacityPolicies(nodeRepository.zone(), nodeRepository.exclusivity(), nodeRepository.flagSource()); this.zone = zone; this.loadBalancerProvisioner = provisionServiceProvider.getLoadBalancerService() .map(lbService -> new LoadBalancerProvisioner(nodeRepository, lbService)); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java index 8374c41ea25..91034aae0cf 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java @@ -389,7 +389,10 @@ public class AutoscalingTest { .build(); NodeResources defaultResources = - new CapacityPolicies(fixture.tester().nodeRepository()).specifyFully(NodeResources.unspecified(), fixture.clusterSpec, fixture.applicationId); + new CapacityPolicies(fixture.tester().nodeRepository().zone(), + fixture.tester().nodeRepository().exclusivity(), + fixture.tester().nodeRepository().flagSource()) + .specifyFully(NodeResources.unspecified(), fixture.clusterSpec, fixture.applicationId); fixture.tester().assertResources("Min number of nodes and default resources", 2, 1, defaultResources, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java index 183ff85da47..dde56700d0e 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java @@ -77,7 +77,7 @@ public class DynamicProvisioningTester { hostResourcesCalculator = resourcesCalculator; autoscaler = new Autoscaler(nodeRepository()); - capacityPolicies = new CapacityPolicies(provisioningTester.nodeRepository()); + capacityPolicies = new CapacityPolicies(zone, provisioningTester.nodeRepository().exclusivity(), flagSource); } public InMemoryProvisionLogger provisionLogger() { return provisioningTester.provisionLogger(); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java index 8d6a378d223..cbc2ccb61ac 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java @@ -133,7 +133,7 @@ public class ProvisioningTester { true, spareCount); this.provisioner = new NodeRepositoryProvisioner(nodeRepository, zone, provisionServiceProvider, new MockMetric()); - this.capacityPolicies = new CapacityPolicies(nodeRepository); + this.capacityPolicies = new CapacityPolicies(zone, nodeRepository.exclusivity(), flagSource); this.provisionLogger = new InMemoryProvisionLogger(); this.loadBalancerService = loadBalancerService; } -- cgit v1.2.3 From ea1bc491ddf17062112981a2131ced8444b9a70f Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Sun, 12 May 2024 18:26:21 +0200 Subject: Move CapacityPolicies to config-privisioning --- .../yahoo/config/provision/CapacityPolicies.java | 185 +++++++++++++++++++ .../hosted/provision/autoscale/ClusterModel.java | 2 +- .../vespa/hosted/provision/autoscale/Limits.java | 2 +- .../provision/provisioning/CapacityPolicies.java | 195 --------------------- .../provisioning/NodeRepositoryProvisioner.java | 1 + .../provision/provisioning/NodeResourceLimits.java | 1 + .../provision/autoscale/AutoscalingTest.java | 2 +- .../provisioning/DynamicProvisioningTester.java | 1 + .../provision/provisioning/ProvisioningTest.java | 24 ++- .../provision/provisioning/ProvisioningTester.java | 1 + 10 files changed, 210 insertions(+), 204 deletions(-) create mode 100644 config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java delete mode 100644 node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java b/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java new file mode 100644 index 00000000000..dec2984846e --- /dev/null +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java @@ -0,0 +1,185 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.provision; + +import com.yahoo.component.Version; +import com.yahoo.config.provision.NodeResources.DiskSpeed; +import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.flags.PermanentFlags; +import com.yahoo.vespa.flags.StringFlag; + +import java.util.Map; +import java.util.TreeMap; + +import static com.yahoo.config.provision.NodeResources.Architecture; +import static com.yahoo.vespa.flags.Dimension.INSTANCE_ID; +import static java.util.Objects.requireNonNull; + +/** + * Defines the policies for assigning cluster capacity in various environments. + * + * @author bratseth + */ +public class CapacityPolicies { + + private final Zone zone; + private final Exclusivity exclusivity; + private final StringFlag adminClusterNodeArchitecture; + + public CapacityPolicies(Zone zone, Exclusivity exclusivity, FlagSource flagSource) { + this.zone = zone; + this.exclusivity = exclusivity; + this.adminClusterNodeArchitecture = PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(flagSource); + } + + public Capacity applyOn(Capacity capacity, ApplicationId application, boolean exclusive) { + var min = applyOn(capacity.minResources(), capacity, application, exclusive); + var max = applyOn(capacity.maxResources(), capacity, application, exclusive); + var groupSize = capacity.groupSize().fromAtMost(max.nodes() / min.groups()) + .toAtLeast(min.nodes() / max.groups()); + return capacity.withLimits(min, max, groupSize); + } + + private ClusterResources applyOn(ClusterResources resources, Capacity capacity, ApplicationId application, boolean exclusive) { + int nodes = decideCount(resources.nodes(), capacity.isRequired(), application.instance().isTester()); + int groups = decideGroups(resources.nodes(), resources.groups(), nodes); + var nodeResources = decideNodeResources(resources.nodeResources(), capacity.isRequired(), exclusive); + return new ClusterResources(nodes, groups, nodeResources); + } + + private int decideCount(int requested, boolean required, boolean isTester) { + if (isTester) return 1; + + if (required) return requested; + return switch (zone.environment()) { + case dev, test -> 1; + case perf -> Math.min(requested, 3); + case staging -> requested <= 1 ? requested : Math.max(2, requested / 10); + case prod -> requested; + }; + } + + private int decideGroups(int requestedNodes, int requestedGroups, int decidedNodes) { + if (requestedNodes == decidedNodes) return requestedGroups; + int groups = Math.min(requestedGroups, decidedNodes); // cannot have more groups than nodes + while (groups > 1 && decidedNodes % groups != 0) + groups--; // Must be divisible by the number of groups + return groups; + } + + private NodeResources decideNodeResources(NodeResources target, boolean required, boolean exclusive) { + if (required || exclusive) return target; // Cannot downsize if resources are required, or exclusively allocated + if (target.isUnspecified()) return target; // Cannot be modified + + if (zone.environment() == Environment.dev && zone.cloud().allowHostSharing()) { + // Dev does not cap the cpu or network of containers since usage is spotty: Allocate just a small amount exclusively + target = target.withVcpu(0.1).withBandwidthGbps(0.1); + + // Allocate without GPU in dev + target = target.with(NodeResources.GpuResources.zero()); + } + + // Allow slow storage in zones which are not performance sensitive + if (zone.system().isCd() || zone.environment() == Environment.dev || zone.environment() == Environment.test) + target = target.with(NodeResources.DiskSpeed.any).with(NodeResources.StorageType.any).withBandwidthGbps(0.1); + + return target; + } + + public ClusterResources specifyFully(ClusterResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { + return resources.with(specifyFully(resources.nodeResources(), clusterSpec, applicationId)); + } + + public NodeResources specifyFully(NodeResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { + return resources.withUnspecifiedFieldsFrom(defaultResources(clusterSpec, applicationId).with(DiskSpeed.any)); + } + + private NodeResources defaultResources(ClusterSpec clusterSpec, ApplicationId applicationId) { + if (clusterSpec.type() == ClusterSpec.Type.admin) { + Architecture architecture = adminClusterArchitecture(applicationId); + + if (exclusivity.allocation(clusterSpec)) { + return smallestExclusiveResources().with(architecture); + } + + if (clusterSpec.id().value().equals("cluster-controllers")) { + return clusterControllerResources(clusterSpec, architecture).with(architecture); + } + + if (clusterSpec.id().value().equals("logserver")) { + return logserverResources(architecture).with(architecture); + } + + return versioned(clusterSpec, Map.of(new Version(0), smallestSharedResources())).with(architecture); + } + + if (clusterSpec.type() == ClusterSpec.Type.content) { + // When changing defaults here update cloud.vespa.ai/en/reference/services + return zone.cloud().dynamicProvisioning() + ? versioned(clusterSpec, Map.of(new Version(0), new NodeResources(2, 16, 300, 0.3))) + : versioned(clusterSpec, Map.of(new Version(0), new NodeResources(1.5, 8, 50, 0.3))); + } + else { + // When changing defaults here update cloud.vespa.ai/en/reference/services + return zone.cloud().dynamicProvisioning() + ? versioned(clusterSpec, Map.of(new Version(0), new NodeResources(2.0, 8, 50, 0.3))) + : versioned(clusterSpec, Map.of(new Version(0), new NodeResources(1.5, 8, 50, 0.3))); + } + } + + private NodeResources clusterControllerResources(ClusterSpec clusterSpec, Architecture architecture) { + // 1.32 fits floor(8/1.32) = 6 cluster controllers on each 8Gb host, and each will have + // 1.32-(0.7+0.6)*(1.32/8) = 1.1 Gb real memory given current taxes. + if (architecture == Architecture.x86_64) + return versioned(clusterSpec, Map.of(new Version(0), new NodeResources(0.25, 1.32, 10, 0.3))); + else + // arm64 nodes need more memory + return versioned(clusterSpec, Map.of(new Version(0), new NodeResources(0.25, 1.50, 10, 0.3))); + } + + private NodeResources logserverResources(Architecture architecture) { + if (zone.cloud().name() == CloudName.AZURE) + return new NodeResources(2, 4, 50, 0.3); + + if (zone.cloud().name() == CloudName.GCP) + return new NodeResources(1, 4, 50, 0.3); + + return architecture == Architecture.arm64 + ? new NodeResources(0.5, 2.5, 50, 0.3) + : new NodeResources(0.5, 2, 50, 0.3); + } + + private Architecture adminClusterArchitecture(ApplicationId instance) { + return Architecture.valueOf(adminClusterNodeArchitecture.with(INSTANCE_ID, instance.serializedForm()).value()); + } + + // The lowest amount of resources that can be exclusive allocated (i.e. a matching host flavor for this exists) + private NodeResources smallestExclusiveResources() { + return zone.cloud().name() == CloudName.AZURE || zone.cloud().name() == CloudName.GCP + ? new NodeResources(2, 8, 50, 0.3) + : new NodeResources(0.5, 8, 50, 0.3); + } + + // The lowest amount of resources that can be shared (i.e. a matching host flavor for this exists) + private NodeResources smallestSharedResources() { + return zone.cloud().name() == CloudName.GCP + ? new NodeResources(1, 4, 50, 0.3) + : new NodeResources(0.5, 2, 50, 0.3); + } + + /** Returns whether the nodes requested can share physical host with other applications */ + public ClusterSpec decideExclusivity(Capacity capacity, ClusterSpec requestedCluster) { + if (capacity.cloudAccount().isPresent()) return requestedCluster.withExclusivity(true); // Implicit exclusive + boolean exclusive = requestedCluster.isExclusive() && (capacity.isRequired() || zone.environment() == Environment.prod); + return requestedCluster.withExclusivity(exclusive); + } + + /** + * Returns the resources for the newest version not newer than that requested in the cluster spec. + */ + private static NodeResources versioned(ClusterSpec spec, Map resources) { + return requireNonNull(new TreeMap<>(resources).floorEntry(spec.vespaVersion()), + "no default resources applicable for " + spec + " among: " + resources) + .getValue(); + } + +} 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 ece84cfd909..5310ecaa1c2 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 @@ -8,7 +8,7 @@ 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.provisioning.CapacityPolicies; +import com.yahoo.config.provision.CapacityPolicies; import java.time.Clock; import java.time.Duration; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java index 2cd06706d7f..54e611eef18 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java @@ -9,7 +9,7 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.NodeResources; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.applications.Cluster; -import com.yahoo.vespa.hosted.provision.provisioning.CapacityPolicies; +import com.yahoo.config.provision.CapacityPolicies; import java.util.Objects; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java deleted file mode 100644 index bb9aaccd1e3..00000000000 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.provision.provisioning; - -import com.yahoo.component.Version; -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.Capacity; -import com.yahoo.config.provision.CloudName; -import com.yahoo.config.provision.ClusterResources; -import com.yahoo.config.provision.ClusterSpec; -import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.Exclusivity; -import com.yahoo.config.provision.NodeResources; -import com.yahoo.config.provision.NodeResources.DiskSpeed; -import com.yahoo.config.provision.Zone; -import com.yahoo.vespa.flags.FlagSource; -import com.yahoo.vespa.flags.PermanentFlags; -import com.yahoo.vespa.flags.StringFlag; - -import java.util.Map; -import java.util.TreeMap; - -import static com.yahoo.config.provision.NodeResources.Architecture; -import static com.yahoo.vespa.flags.Dimension.INSTANCE_ID; -import static java.util.Objects.requireNonNull; - -/** - * Defines the policies for assigning cluster capacity in various environments - * - * @author bratseth - * @see NodeResourceLimits - */ -public class CapacityPolicies { - - private final Zone zone; - private final Exclusivity exclusivity; - private final StringFlag adminClusterNodeArchitecture; - - public CapacityPolicies(Zone zone, Exclusivity exclusivity, FlagSource flagSource) { - this.zone = zone; - this.exclusivity = exclusivity; - this.adminClusterNodeArchitecture = PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(flagSource); - } - - public Capacity applyOn(Capacity capacity, ApplicationId application, boolean exclusive) { - var min = applyOn(capacity.minResources(), capacity, application, exclusive); - var max = applyOn(capacity.maxResources(), capacity, application, exclusive); - var groupSize = capacity.groupSize().fromAtMost(max.nodes() / min.groups()) - .toAtLeast(min.nodes() / max.groups()); - return capacity.withLimits(min, max, groupSize); - } - - private ClusterResources applyOn(ClusterResources resources, Capacity capacity, ApplicationId application, boolean exclusive) { - int nodes = decideCount(resources.nodes(), capacity.isRequired(), application.instance().isTester()); - int groups = decideGroups(resources.nodes(), resources.groups(), nodes); - var nodeResources = decideNodeResources(resources.nodeResources(), capacity.isRequired(), exclusive); - return new ClusterResources(nodes, groups, nodeResources); - } - - private int decideCount(int requested, boolean required, boolean isTester) { - if (isTester) return 1; - - if (required) return requested; - return switch (zone.environment()) { - case dev, test -> 1; - case perf -> Math.min(requested, 3); - case staging -> requested <= 1 ? requested : Math.max(2, requested / 10); - case prod -> requested; - }; - } - - private int decideGroups(int requestedNodes, int requestedGroups, int decidedNodes) { - if (requestedNodes == decidedNodes) return requestedGroups; - int groups = Math.min(requestedGroups, decidedNodes); // cannot have more groups than nodes - while (groups > 1 && decidedNodes % groups != 0) - groups--; // Must be divisible by the number of groups - return groups; - } - - private NodeResources decideNodeResources(NodeResources target, boolean required, boolean exclusive) { - if (required || exclusive) return target; // Cannot downsize if resources are required, or exclusively allocated - if (target.isUnspecified()) return target; // Cannot be modified - - if (zone.environment() == Environment.dev && zone.cloud().allowHostSharing()) { - // Dev does not cap the cpu or network of containers since usage is spotty: Allocate just a small amount exclusively - target = target.withVcpu(0.1).withBandwidthGbps(0.1); - - // Allocate without GPU in dev - target = target.with(NodeResources.GpuResources.zero()); - } - - // Allow slow storage in zones which are not performance sensitive - if (zone.system().isCd() || zone.environment() == Environment.dev || zone.environment() == Environment.test) - target = target.with(NodeResources.DiskSpeed.any).with(NodeResources.StorageType.any).withBandwidthGbps(0.1); - - return target; - } - - public ClusterResources specifyFully(ClusterResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { - return resources.with(specifyFully(resources.nodeResources(), clusterSpec, applicationId)); - } - - public NodeResources specifyFully(NodeResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { - return resources.withUnspecifiedFieldsFrom(defaultResources(clusterSpec, applicationId).with(DiskSpeed.any)); - } - - private NodeResources defaultResources(ClusterSpec clusterSpec, ApplicationId applicationId) { - if (clusterSpec.type() == ClusterSpec.Type.admin) { - Architecture architecture = adminClusterArchitecture(applicationId); - - if (exclusivity.allocation(clusterSpec)) { - return smallestExclusiveResources().with(architecture); - } - - if (clusterSpec.id().value().equals("cluster-controllers")) { - return clusterControllerResources(clusterSpec, architecture).with(architecture); - } - - if (clusterSpec.id().value().equals("logserver")) { - return logserverResources(architecture).with(architecture); - } - - return versioned(clusterSpec, Map.of(new Version(0), smallestSharedResources())).with(architecture); - } - - if (clusterSpec.type() == ClusterSpec.Type.content) { - // When changing defaults here update cloud.vespa.ai/en/reference/services - return zone.cloud().dynamicProvisioning() - ? versioned(clusterSpec, Map.of(new Version(0), new NodeResources(2, 16, 300, 0.3))) - : versioned(clusterSpec, Map.of(new Version(0), new NodeResources(1.5, 8, 50, 0.3))); - } - else { - // When changing defaults here update cloud.vespa.ai/en/reference/services - return zone.cloud().dynamicProvisioning() - ? versioned(clusterSpec, Map.of(new Version(0), new NodeResources(2.0, 8, 50, 0.3))) - : versioned(clusterSpec, Map.of(new Version(0), new NodeResources(1.5, 8, 50, 0.3))); - } - } - - private NodeResources clusterControllerResources(ClusterSpec clusterSpec, Architecture architecture) { - // 1.32 fits floor(8/1.32) = 6 cluster controllers on each 8Gb host, and each will have - // 1.32-(0.7+0.6)*(1.32/8) = 1.1 Gb real memory given current taxes. - if (architecture == Architecture.x86_64) - return versioned(clusterSpec, Map.of(new Version(0), new NodeResources(0.25, 1.32, 10, 0.3))); - else - // arm64 nodes need more memory - return versioned(clusterSpec, Map.of(new Version(0), new NodeResources(0.25, 1.50, 10, 0.3))); - } - - private NodeResources logserverResources(Architecture architecture) { - if (zone.cloud().name() == CloudName.AZURE) - return new NodeResources(2, 4, 50, 0.3); - - if (zone.cloud().name() == CloudName.GCP) - return new NodeResources(1, 4, 50, 0.3); - - return architecture == Architecture.arm64 - ? new NodeResources(0.5, 2.5, 50, 0.3) - : new NodeResources(0.5, 2, 50, 0.3); - } - - private Architecture adminClusterArchitecture(ApplicationId instance) { - return Architecture.valueOf(adminClusterNodeArchitecture.with(INSTANCE_ID, instance.serializedForm()).value()); - } - - /** - * Returns the resources for the newest version not newer than that requested in the cluster spec. - */ - static NodeResources versioned(ClusterSpec spec, Map resources) { - return requireNonNull(new TreeMap<>(resources).floorEntry(spec.vespaVersion()), - "no default resources applicable for " + spec + " among: " + resources) - .getValue(); - } - - // The lowest amount of resources that can be exclusive allocated (i.e. a matching host flavor for this exists) - private NodeResources smallestExclusiveResources() { - return zone.cloud().name() == CloudName.AZURE || zone.cloud().name() == CloudName.GCP - ? new NodeResources(2, 8, 50, 0.3) - : new NodeResources(0.5, 8, 50, 0.3); - } - - // The lowest amount of resources that can be shared (i.e. a matching host flavor for this exists) - private NodeResources smallestSharedResources() { - return zone.cloud().name() == CloudName.GCP - ? new NodeResources(1, 4, 50, 0.3) - : new NodeResources(0.5, 2, 50, 0.3); - } - - /** Returns whether the nodes requested can share physical host with other applications */ - public ClusterSpec decideExclusivity(Capacity capacity, ClusterSpec requestedCluster) { - if (capacity.cloudAccount().isPresent()) return requestedCluster.withExclusivity(true); // Implicit exclusive - boolean exclusive = requestedCluster.isExclusive() && (capacity.isRequired() || zone.environment() == Environment.prod); - return requestedCluster.withExclusivity(exclusive); - } - -} 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 89c508f7fa8..44750988a3e 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 @@ -6,6 +6,7 @@ import com.yahoo.config.provision.ActivationContext; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationTransaction; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java index ba22aa5a75a..5eb8c2e7fd7 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.NodeResources; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java index 91034aae0cf..6316adae887 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java @@ -13,7 +13,7 @@ import com.yahoo.config.provision.NodeResources.DiskSpeed; import com.yahoo.config.provision.NodeResources.StorageType; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.Zone; -import com.yahoo.vespa.hosted.provision.provisioning.CapacityPolicies; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.vespa.hosted.provision.provisioning.DynamicProvisioningTester; import org.junit.Test; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java index dde56700d0e..45004ad41ec 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java index 7b690b880c2..9e6ae9f010c 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; @@ -46,12 +47,14 @@ import java.util.Map; import java.util.Optional; import java.util.Random; import java.util.Set; +import java.util.TreeMap; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import static java.time.temporal.ChronoUnit.MILLIS; +import static java.util.Objects.requireNonNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -1114,12 +1117,12 @@ public class ProvisioningTest { new Version("6"), new NodeResources(1, 1, 1, 1)); assertThrows(NullPointerException.class, - () -> CapacityPolicies.versioned(spec.vespaVersion("5.0").build(), resources)); - assertEquals(new NodeResources(1, 1, 1, 1), CapacityPolicies.versioned(spec.vespaVersion("6.0").build(), resources)); - assertEquals(new NodeResources(2, 2, 2, 2), CapacityPolicies.versioned(spec.vespaVersion("7.0").build(), resources)); - assertEquals(new NodeResources(2, 2, 2, 2), CapacityPolicies.versioned(spec.vespaVersion("7.1").build(), resources)); - assertEquals(new NodeResources(3, 3, 3, 3), CapacityPolicies.versioned(spec.vespaVersion("8.0").build(), resources)); - assertEquals(new NodeResources(3, 3, 3, 3), CapacityPolicies.versioned(spec.vespaVersion("9.0").build(), resources)); + () -> versioned(spec.vespaVersion("5.0").build(), resources)); + assertEquals(new NodeResources(1, 1, 1, 1), versioned(spec.vespaVersion("6.0").build(), resources)); + assertEquals(new NodeResources(2, 2, 2, 2), versioned(spec.vespaVersion("7.0").build(), resources)); + assertEquals(new NodeResources(2, 2, 2, 2), versioned(spec.vespaVersion("7.1").build(), resources)); + assertEquals(new NodeResources(3, 3, 3, 3), versioned(spec.vespaVersion("8.0").build(), resources)); + assertEquals(new NodeResources(3, 3, 3, 3), versioned(spec.vespaVersion("9.0").build(), resources)); } @Test @@ -1292,4 +1295,13 @@ public class ProvisioningTest { return new ClusterResources(nodes, groups, new NodeResources(vcpu, memory, disk, 0.1)); } + /** + * Returns the resources for the newest version not newer than that requested in the cluster spec. + */ + static NodeResources versioned(ClusterSpec spec, Map resources) { + return requireNonNull(new TreeMap<>(resources).floorEntry(spec.vespaVersion()), + "no default resources applicable for " + spec + " among: " + resources) + .getValue(); + } + } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java index cbc2ccb61ac..8b49e5e5223 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java @@ -8,6 +8,7 @@ import com.yahoo.config.provision.ApplicationMutex; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.ApplicationTransaction; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.Cloud; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.ClusterResources; -- cgit v1.2.3 From 317b1a3c8ca6ad88385931d8336b87d86b38b701 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Mon, 13 May 2024 14:51:13 -0500 Subject: Apply capacity to quota check --- config-model-api/abi-spec.json | 3 ++ .../com/yahoo/config/model/api/Provisioned.java | 21 ++++++++++++-- config-model/pom.xml | 6 ++++ .../com/yahoo/config/model/deploy/DeployState.java | 14 ++++++++++ .../model/provision/InMemoryProvisioner.java | 2 +- .../com/yahoo/vespa/model/VespaModelFactory.java | 10 +++++-- .../application/validation/QuotaValidator.java | 32 +++++++++++++++------- .../change/ResourcesReductionValidator.java | 4 +-- .../com/yahoo/vespa/model/ClusterInfoTest.java | 2 +- .../application/validation/QuotaValidatorTest.java | 6 ++++ .../container/xml/ContainerModelBuilderTest.java | 2 +- .../configserver/flags/ConfigServerFlagSource.java | 2 ++ .../configserver/flags/db/ZooKeeperFlagSource.java | 2 ++ .../vespa/config/server/ApplicationRepository.java | 2 +- .../server/provision/ProvisionerAdapter.java | 2 +- 15 files changed, 88 insertions(+), 22 deletions(-) diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json index 42e7e23dfcc..c0139c87349 100644 --- a/config-model-api/abi-spec.json +++ b/config-model-api/abi-spec.json @@ -1624,6 +1624,9 @@ ], "methods" : [ "public void ()", + "public void add(com.yahoo.config.provision.ClusterSpec, com.yahoo.config.provision.Capacity)", + "public java.util.Map clusters()", + "public java.util.Map capacities()", "public void add(com.yahoo.config.provision.ClusterSpec$Id, com.yahoo.config.provision.Capacity)", "public java.util.Map all()" ], diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/Provisioned.java b/config-model-api/src/main/java/com/yahoo/config/model/api/Provisioned.java index db0822c4c10..81e0cb58c7d 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/Provisioned.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/Provisioned.java @@ -16,13 +16,28 @@ import java.util.Map; */ public class Provisioned { - private final Map provisioned = new HashMap<>(); + private final Map clusters = new HashMap<>(); + private final Map capacities = new HashMap<>(); + + public void add(ClusterSpec cluster, Capacity capacity) { + clusters.put(cluster.id(), cluster); + capacities.put(cluster.id(), capacity); + } + + /** Returns an unmodifiable map of all the cluster requests recorded during build of the model this belongs to */ + public Map clusters() { return Collections.unmodifiableMap(clusters); } + + /** Returns an unmodifiable map of all the capacity provision requests recorded during build of the model this belongs to */ + public Map capacities() { return Collections.unmodifiableMap(capacities); } + + // TODO: Remove after June 2024 public void add(ClusterSpec.Id id, Capacity capacity) { - provisioned.put(id, capacity); + capacities.put(id, capacity); } /** Returns an unmodifiable map of all the provision requests recorded during build of the model this belongs to */ - public Map all() { return Collections.unmodifiableMap(provisioned); } + // TODO: Remove after June 2024 + public Map all() { return Collections.unmodifiableMap(capacities); } } diff --git a/config-model/pom.xml b/config-model/pom.xml index a0bda542d5f..751d3de63ef 100644 --- a/config-model/pom.xml +++ b/config-model/pom.xml @@ -24,6 +24,12 @@ ${project.version} provided + + com.yahoo.vespa + flags + ${project.version} + provided + com.yahoo.vespa provided-dependencies diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java index 57a75bd8a38..0e4e296f5a1 100644 --- a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java +++ b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java @@ -39,6 +39,8 @@ import com.yahoo.vespa.config.ConfigDefinition; import com.yahoo.vespa.config.ConfigDefinitionBuilder; import com.yahoo.vespa.config.ConfigDefinitionKey; import com.yahoo.vespa.documentmodel.DocumentModel; +import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.model.container.search.QueryProfiles; import com.yahoo.vespa.model.container.search.QueryProfilesBuilder; import com.yahoo.vespa.model.container.search.SemanticRules; @@ -79,6 +81,7 @@ public class DeployState implements ConfigDefinitionStore { private final Version vespaVersion; private final Set endpoints; private final Zone zone; // TODO: Zone is set separately both here and in properties + private final FlagSource flagSource; private final QueryProfiles queryProfiles; private final SemanticRules semanticRules; private final ImportedMlModels importedModels; @@ -118,6 +121,7 @@ public class DeployState implements ConfigDefinitionStore { Set endpoints, Collection modelImporters, Zone zone, + FlagSource flagSource, QueryProfiles queryProfiles, SemanticRules semanticRules, Instant now, @@ -143,6 +147,7 @@ public class DeployState implements ConfigDefinitionStore { this.configDefinitionRepo = configDefinitionRepo; this.endpoints = Set.copyOf(endpoints); this.zone = zone; + this.flagSource = flagSource; this.queryProfiles = queryProfiles; // TODO: Remove this by seeing how pagetemplates are propagated this.semanticRules = semanticRules; // TODO: Remove this by seeing how pagetemplates are propagated this.importedModels = importMlModels(applicationPackage, modelImporters, executor); @@ -273,6 +278,8 @@ public class DeployState implements ConfigDefinitionStore { /** Returns the zone in which this is currently running */ public Zone zone() { return zone; } + public FlagSource flagSource() { return flagSource; } + public QueryProfiles getQueryProfiles() { return queryProfiles; } public SemanticRules getSemanticRules() { return semanticRules; } @@ -330,6 +337,7 @@ public class DeployState implements ConfigDefinitionStore { private Set endpoints = Set.of(); private Collection modelImporters = List.of(); private Zone zone = Zone.defaultZone(); + private FlagSource flagSource = new InMemoryFlagSource(); private Instant now = Instant.now(); private Version wantedNodeVespaVersion = Vtag.currentVersion; private boolean accessLoggingEnabledByDefault = true; @@ -407,6 +415,11 @@ public class DeployState implements ConfigDefinitionStore { return this; } + public Builder flagSource(FlagSource flagSource) { + this.flagSource = flagSource; + return this; + } + public Builder now(Instant now) { this.now = now; return this; @@ -483,6 +496,7 @@ public class DeployState implements ConfigDefinitionStore { endpoints, modelImporters, zone, + flagSource, queryProfiles, semanticRules, now, diff --git a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java index befe57a97e4..da0fd265724 100644 --- a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java +++ b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java @@ -158,7 +158,7 @@ public class InMemoryProvisioner implements HostProvisioner { @Override public List prepare(ClusterSpec cluster, Capacity requested, ProvisionLogger logger) { - provisioned.add(cluster.id(), requested); + provisioned.add(cluster, requested); clusters.add(cluster); if (environment == Environment.dev && ! requested.isRequired()) { requested = requested.withLimits(requested.minResources().withNodes(1), diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java index c876976917b..efe83fb4e91 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java @@ -28,6 +28,8 @@ import com.yahoo.config.provision.QuotaExceededException; import com.yahoo.config.provision.TransientException; import com.yahoo.config.provision.Zone; import com.yahoo.vespa.config.VespaVersion; +import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.model.application.validation.Validation; import com.yahoo.vespa.model.application.validation.Validator; import org.xml.sax.SAXException; @@ -52,6 +54,7 @@ public class VespaModelFactory implements ModelFactory { private final ConfigModelRegistry configModelRegistry; private final Collection modelImporters; private final Zone zone; + private final FlagSource flagSource; private final Clock clock; private final Version version; private final List additionalValidators; @@ -60,7 +63,7 @@ public class VespaModelFactory implements ModelFactory { @Inject public VespaModelFactory(ComponentRegistry pluginRegistry, ComponentRegistry additionalValidators, - Zone zone) { + Zone zone, FlagSource flagSource) { this.version = new Version(VespaVersion.major, VespaVersion.minor, VespaVersion.micro); List> modelBuilders = new ArrayList<>(); for (ConfigModelPlugin plugin : pluginRegistry.allComponents()) { @@ -76,6 +79,7 @@ public class VespaModelFactory implements ModelFactory { new XGBoostImporter(), new LightGBMImporter()); this.zone = zone; + this.flagSource = flagSource; this.additionalValidators = List.copyOf(additionalValidators.allComponents()); this.clock = Clock.systemUTC(); @@ -84,7 +88,7 @@ public class VespaModelFactory implements ModelFactory { // For testing only protected VespaModelFactory(ConfigModelRegistry configModelRegistry) { this(new Version(VespaVersion.major, VespaVersion.minor, VespaVersion.micro), configModelRegistry, - Clock.systemUTC(), Zone.defaultZone()); + Clock.systemUTC(), Zone.defaultZone()); } private VespaModelFactory(Version version, ConfigModelRegistry configModelRegistry, Clock clock, Zone zone) { @@ -98,6 +102,7 @@ public class VespaModelFactory implements ModelFactory { this.modelImporters = List.of(); this.additionalValidators = List.of(); this.zone = zone; + this.flagSource = new InMemoryFlagSource(); this.clock = clock; } @@ -192,6 +197,7 @@ public class VespaModelFactory implements ModelFactory { .endpoints(modelContext.properties().endpoints()) .modelImporters(modelImporters) .zone(zone) + .flagSource(flagSource) .now(clock.instant()) .wantedNodeVespaVersion(modelContext.wantedNodeVespaVersion()) .wantedDockerImageRepo(modelContext.wantedDockerImageRepo()) diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java index 4d9386b5f19..0984770ef49 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java @@ -1,9 +1,12 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.application.validation; +import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.QuotaExceededException; import com.yahoo.config.provision.SystemName; @@ -31,25 +34,34 @@ public class QuotaValidator implements Validator { @Override public void validate(Context context) { + var zone = context.deployState().zone(); + var flagSource = context.deployState().flagSource(); + var capacityPolicies = new CapacityPolicies(zone, new Exclusivity(zone, flagSource), flagSource); var quota = context.deployState().getProperties().quota(); quota.maxClusterSize().ifPresent(maxClusterSize -> validateMaxClusterSize(maxClusterSize, context.model())); - quota.budgetAsDecimal().ifPresent(budget -> validateBudget(budget, context.model(), context.deployState().getProperties().zone())); + quota.budgetAsDecimal().ifPresent(budget -> validateBudget(budget, context, capacityPolicies)); } - private void validateBudget(BigDecimal budget, VespaModel model, Zone zone) { - var maxSpend = model.allClusters().stream() - .filter(id -> !adminClusterIds(model).contains(id)) - .map(id -> model.provisioned().all().getOrDefault(id, zeroCapacity)) - .mapToDouble(c -> c.maxResources().cost()) // TODO: This may be unspecified -> 0 - .sum(); + private void validateBudget(BigDecimal budget, Context context, + CapacityPolicies capacityPolicies) { + var zone = context.deployState().getProperties().zone(); + var application = context.model().applicationPackage().getApplicationId(); + + var maxSpend = 0.0; + for (var id : context.model().allClusters()) { + if (adminClusterIds(context.model()).contains(id)) continue; + var cluster = context.model().provisioned().clusters().get(id); + var capacity = context.model().provisioned().capacities().getOrDefault(id, zeroCapacity); + maxSpend += capacityPolicies.applyOn(capacity, application, cluster.isExclusive()).maxResources().cost(); + } - var actualSpend = model.allocatedHosts().getHosts().stream() + var actualSpend = context.model().allocatedHosts().getHosts().stream() .filter(hostSpec -> hostSpec.membership().get().cluster().type() != ClusterSpec.Type.admin) .mapToDouble(hostSpec -> hostSpec.advertisedResources().cost()) .sum(); if (Math.abs(actualSpend) < 0.01) { - log.warning("Deploying application " + model.applicationPackage().getApplicationId() + " with zero budget use. This is suspicious, but not blocked"); + log.warning("Deploying application " + application + " with zero budget use. This is suspicious, but not blocked"); return; } @@ -69,7 +81,7 @@ public class QuotaValidator implements Validator { /** Check that all clusters in the application do not exceed the quota max cluster size. */ private void validateMaxClusterSize(int maxClusterSize, VespaModel model) { - var invalidClusters = model.provisioned().all().entrySet().stream() + var invalidClusters = model.provisioned().capacities().entrySet().stream() .filter(entry -> entry.getValue() != null) .filter(entry -> { var cluster = entry.getValue(); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java index 5d7a8779005..42410dc3acf 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ResourcesReductionValidator.java @@ -60,9 +60,9 @@ public class ResourcesReductionValidator implements ChangeValidator { * This will always yield specified node resources on hosted instances and never on self-hosted instances. */ private ClusterResources clusterResources(ClusterSpec.Id id, VespaModel model) { - if ( ! model.provisioned().all().containsKey(id)) return null; + if ( ! model.provisioned().capacities().containsKey(id)) return null; - ClusterResources resources = model.provisioned().all().get(id).maxResources(); + ClusterResources resources = model.provisioned().capacities().get(id).maxResources(); if ( ! resources.nodeResources().isUnspecified()) return resources; var containerCluster = model.getContainerClusters().get(id.value()); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/ClusterInfoTest.java b/config-model/src/test/java/com/yahoo/vespa/model/ClusterInfoTest.java index 4df9f261dfe..7aa6eb76995 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/ClusterInfoTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/ClusterInfoTest.java @@ -258,7 +258,7 @@ public class ClusterInfoTest { .provisioned(provisioner.provisioned()) .build(); new VespaModel(new NullConfigModelRegistry(), deployState); - return deployState.provisioned().all(); + return deployState.provisioned().capacities(); } } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java index 89f81dfdaef..590433757c3 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/QuotaValidatorTest.java @@ -53,6 +53,12 @@ public class QuotaValidatorTest { } } + @Test + void test_deploy_within_quota_budget_because_in_dev() { + var tester = new ValidationTester(13, false, new TestProperties().setHostedVespa(true).setQuota(quota).setZone(devZone)); + tester.deploy(null, getServices(10), Environment.dev, null, CONTAINER_CLUSTER); + } + @Test void test_deploy_above_quota_budget_in_publiccd() { var tester = new ValidationTester(13, false, new TestProperties().setHostedVespa(true).setQuota(quota.withBudget(BigDecimal.ONE)).setZone(publicCdZone)); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java index e704da08d18..0e8ce4748b4 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java @@ -598,7 +598,7 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { .setCloudAccount(cloudAccount)) .build()); assertEquals(2, model.hostSystem().getHosts().size()); - assertEquals(List.of(cloudAccount), model.provisioned().all().values() + assertEquals(List.of(cloudAccount), model.provisioned().capacities().values() .stream() .map(capacity -> capacity.cloudAccount().get()) .toList()); diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/ConfigServerFlagSource.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/ConfigServerFlagSource.java index f30d329cf09..7a0b3695e7c 100644 --- a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/ConfigServerFlagSource.java +++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/ConfigServerFlagSource.java @@ -13,6 +13,7 @@ import java.nio.file.FileSystems; * @author hakonhall */ public class ConfigServerFlagSource extends OrderedFlagSource { + @Inject public ConfigServerFlagSource(FlagsDb flagsDb) { this(FileSystems.getDefault(), flagsDb); @@ -21,4 +22,5 @@ public class ConfigServerFlagSource extends OrderedFlagSource { ConfigServerFlagSource(FileSystem fileSystem, FlagsDb flagsDb) { super(new BootstrapFlagSource(fileSystem), new ZooKeeperFlagSource(flagsDb)); } + } diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/db/ZooKeeperFlagSource.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/db/ZooKeeperFlagSource.java index f68c4657d8a..e57088416a0 100644 --- a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/db/ZooKeeperFlagSource.java +++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/db/ZooKeeperFlagSource.java @@ -13,6 +13,7 @@ import java.util.Optional; * @author hakonhall */ public class ZooKeeperFlagSource implements FlagSource { + private final FlagsDb flagsDb; public ZooKeeperFlagSource(FlagsDb flagsDb) { @@ -23,4 +24,5 @@ public class ZooKeeperFlagSource implements FlagSource { public Optional fetch(FlagId id, FetchVector vector) { return flagsDb.getValue(id).flatMap(data -> data.resolve(vector)); } + } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java index 07a8179732f..22199dfc5b4 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java @@ -1136,7 +1136,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye public double getQuotaUsageRate(ApplicationId applicationId) { var application = getApplication(applicationId); - return application.getModel().provisioned().all().values().stream() + return application.getModel().provisioned().capacities().values().stream() .map(Capacity::maxResources)// TODO: This may be unspecified -> 0 .mapToDouble(resources -> resources.nodes() * resources.nodeResources().cost()) .sum(); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java b/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java index 956573ecbd4..27805b64e79 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java @@ -44,7 +44,7 @@ public class ProvisionerAdapter implements HostProvisioner { @Override public List prepare(ClusterSpec cluster, Capacity capacity, ProvisionLogger logger) { - provisioned.add(cluster.id(), capacity); + provisioned.add(cluster, capacity); return provisioner.prepare(applicationId, cluster, capacity, logger); } -- cgit v1.2.3 From ad9302e3978195b30e23acde574cef4054661c54 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Sat, 18 May 2024 08:04:44 +0200 Subject: Add dependency --- standalone-container/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/standalone-container/pom.xml b/standalone-container/pom.xml index 92faa1ae670..db1937eb82f 100644 --- a/standalone-container/pom.xml +++ b/standalone-container/pom.xml @@ -55,6 +55,12 @@ ${project.version} provided + + com.yahoo.vespa + flags + ${project.version} + provided + com.yahoo.vespa zkfacade -- cgit v1.2.3 From c533f3abd793cea96658d55dbb88ef66edb1c81a Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Sat, 18 May 2024 08:28:26 +0200 Subject: Add dependency --- application/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/application/pom.xml b/application/pom.xml index f5704541308..cbe04038df9 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -34,6 +34,12 @@ config-model-api ${project.version} + + com.yahoo.vespa + flags + ${project.version} + provided + com.yahoo.vespa http-utils -- cgit v1.2.3 From 922b703f5d73244b5be1b4362c0193b76a501409 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Sat, 18 May 2024 09:00:44 +0200 Subject: Preinstall flags --- standalone-container/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/standalone-container/pom.xml b/standalone-container/pom.xml index db1937eb82f..b8379fc8ef5 100644 --- a/standalone-container/pom.xml +++ b/standalone-container/pom.xml @@ -105,6 +105,7 @@ config-bundle-jar-with-dependencies.jar, config-model-api-jar-with-dependencies.jar, config-model-jar-with-dependencies.jar, + flags-jar-with-dependencies.jar, container-disc-jar-with-dependencies.jar, -- cgit v1.2.3 From dec25d5ab61a9e574a8b41853caa879adfccf894 Mon Sep 17 00:00:00 2001 From: Håkon Hallingstad Date: Sat, 18 May 2024 23:38:02 +0200 Subject: Remove flags dependency from config-model --- application/pom.xml | 6 --- config-model-api/abi-spec.json | 4 +- .../com/yahoo/config/model/api/ModelContext.java | 5 +++ config-model/pom.xml | 6 --- .../com/yahoo/config/model/deploy/DeployState.java | 14 ------- .../com/yahoo/vespa/model/VespaModelFactory.java | 10 +---- .../application/validation/QuotaValidator.java | 9 ++--- config-provisioning/pom.xml | 6 --- .../yahoo/config/provision/CapacityPolicies.java | 46 +++++++++------------- .../com/yahoo/config/provision/Exclusivity.java | 15 +++---- .../com/yahoo/config/provision/SharedHosts.java | 30 ++++++++++++++ .../config/server/deploy/ModelContextImpl.java | 9 +++++ .../vespa/hosted/provision/NodeRepository.java | 14 +++++++ .../hosted/provision/autoscale/ClusterModel.java | 8 ++-- .../vespa/hosted/provision/autoscale/Limits.java | 7 ++-- .../provisioning/NodeRepositoryProvisioner.java | 29 +++++++------- .../provision/testutils/MockNodeRepository.java | 3 +- .../hosted/provision/NodeRepositoryTester.java | 3 +- .../provision/autoscale/AutoscalingTest.java | 11 ++---- .../vespa/hosted/provision/autoscale/Fixture.java | 12 +++--- .../maintenance/CapacityCheckerTester.java | 6 +-- .../maintenance/SpareCapacityMaintainerTest.java | 6 +-- .../provisioning/DynamicProvisioningTest.java | 4 +- .../provisioning/DynamicProvisioningTester.java | 13 +++--- .../provision/provisioning/ProvisioningTester.java | 20 ++++++---- standalone-container/pom.xml | 7 ---- 26 files changed, 153 insertions(+), 150 deletions(-) create mode 100644 config-provisioning/src/main/java/com/yahoo/config/provision/SharedHosts.java diff --git a/application/pom.xml b/application/pom.xml index cbe04038df9..f5704541308 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -34,12 +34,6 @@ config-model-api ${project.version} - - com.yahoo.vespa - flags - ${project.version} - provided - com.yahoo.vespa http-utils diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json index c0139c87349..b86b5fd9b99 100644 --- a/config-model-api/abi-spec.json +++ b/config-model-api/abi-spec.json @@ -1326,7 +1326,9 @@ "public boolean alwaysMarkPhraseExpensive()", "public boolean sortBlueprintsByCost()", "public int persistenceThreadMaxFeedOpBatchSize()", - "public boolean logserverOtelCol()" + "public boolean logserverOtelCol()", + "public com.yahoo.config.provision.SharedHosts sharedHosts()", + "public com.yahoo.config.provision.NodeResources$Architecture adminClusterArchitecture()" ], "fields" : [ ] }, diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java index ad5ab5b7ee4..67735329287 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java @@ -12,6 +12,9 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DataplaneToken; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.HostName; +import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.NodeResources.Architecture; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.Zone; import java.io.File; @@ -114,6 +117,8 @@ public interface ModelContext { @ModelFeatureFlag(owners = {"baldersheim"}) default boolean sortBlueprintsByCost() { return false; } @ModelFeatureFlag(owners = {"vekterli"}) default int persistenceThreadMaxFeedOpBatchSize() { return 1; } @ModelFeatureFlag(owners = {"olaa"}) default boolean logserverOtelCol() { return false; } + @ModelFeatureFlag(owners = {"bratseth"}) default SharedHosts sharedHosts() { return SharedHosts.empty(); } + @ModelFeatureFlag(owners = {"bratseth"}) default Architecture adminClusterArchitecture() { return Architecture.x86_64; } } /** Warning: As elsewhere in this package, do not make backwards incompatible changes that will break old config models! */ diff --git a/config-model/pom.xml b/config-model/pom.xml index 751d3de63ef..a0bda542d5f 100644 --- a/config-model/pom.xml +++ b/config-model/pom.xml @@ -24,12 +24,6 @@ ${project.version} provided - - com.yahoo.vespa - flags - ${project.version} - provided - com.yahoo.vespa provided-dependencies diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java index 0e4e296f5a1..57a75bd8a38 100644 --- a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java +++ b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java @@ -39,8 +39,6 @@ import com.yahoo.vespa.config.ConfigDefinition; import com.yahoo.vespa.config.ConfigDefinitionBuilder; import com.yahoo.vespa.config.ConfigDefinitionKey; import com.yahoo.vespa.documentmodel.DocumentModel; -import com.yahoo.vespa.flags.FlagSource; -import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.model.container.search.QueryProfiles; import com.yahoo.vespa.model.container.search.QueryProfilesBuilder; import com.yahoo.vespa.model.container.search.SemanticRules; @@ -81,7 +79,6 @@ public class DeployState implements ConfigDefinitionStore { private final Version vespaVersion; private final Set endpoints; private final Zone zone; // TODO: Zone is set separately both here and in properties - private final FlagSource flagSource; private final QueryProfiles queryProfiles; private final SemanticRules semanticRules; private final ImportedMlModels importedModels; @@ -121,7 +118,6 @@ public class DeployState implements ConfigDefinitionStore { Set endpoints, Collection modelImporters, Zone zone, - FlagSource flagSource, QueryProfiles queryProfiles, SemanticRules semanticRules, Instant now, @@ -147,7 +143,6 @@ public class DeployState implements ConfigDefinitionStore { this.configDefinitionRepo = configDefinitionRepo; this.endpoints = Set.copyOf(endpoints); this.zone = zone; - this.flagSource = flagSource; this.queryProfiles = queryProfiles; // TODO: Remove this by seeing how pagetemplates are propagated this.semanticRules = semanticRules; // TODO: Remove this by seeing how pagetemplates are propagated this.importedModels = importMlModels(applicationPackage, modelImporters, executor); @@ -278,8 +273,6 @@ public class DeployState implements ConfigDefinitionStore { /** Returns the zone in which this is currently running */ public Zone zone() { return zone; } - public FlagSource flagSource() { return flagSource; } - public QueryProfiles getQueryProfiles() { return queryProfiles; } public SemanticRules getSemanticRules() { return semanticRules; } @@ -337,7 +330,6 @@ public class DeployState implements ConfigDefinitionStore { private Set endpoints = Set.of(); private Collection modelImporters = List.of(); private Zone zone = Zone.defaultZone(); - private FlagSource flagSource = new InMemoryFlagSource(); private Instant now = Instant.now(); private Version wantedNodeVespaVersion = Vtag.currentVersion; private boolean accessLoggingEnabledByDefault = true; @@ -415,11 +407,6 @@ public class DeployState implements ConfigDefinitionStore { return this; } - public Builder flagSource(FlagSource flagSource) { - this.flagSource = flagSource; - return this; - } - public Builder now(Instant now) { this.now = now; return this; @@ -496,7 +483,6 @@ public class DeployState implements ConfigDefinitionStore { endpoints, modelImporters, zone, - flagSource, queryProfiles, semanticRules, now, diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java index efe83fb4e91..c876976917b 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java @@ -28,8 +28,6 @@ import com.yahoo.config.provision.QuotaExceededException; import com.yahoo.config.provision.TransientException; import com.yahoo.config.provision.Zone; import com.yahoo.vespa.config.VespaVersion; -import com.yahoo.vespa.flags.FlagSource; -import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.model.application.validation.Validation; import com.yahoo.vespa.model.application.validation.Validator; import org.xml.sax.SAXException; @@ -54,7 +52,6 @@ public class VespaModelFactory implements ModelFactory { private final ConfigModelRegistry configModelRegistry; private final Collection modelImporters; private final Zone zone; - private final FlagSource flagSource; private final Clock clock; private final Version version; private final List additionalValidators; @@ -63,7 +60,7 @@ public class VespaModelFactory implements ModelFactory { @Inject public VespaModelFactory(ComponentRegistry pluginRegistry, ComponentRegistry additionalValidators, - Zone zone, FlagSource flagSource) { + Zone zone) { this.version = new Version(VespaVersion.major, VespaVersion.minor, VespaVersion.micro); List> modelBuilders = new ArrayList<>(); for (ConfigModelPlugin plugin : pluginRegistry.allComponents()) { @@ -79,7 +76,6 @@ public class VespaModelFactory implements ModelFactory { new XGBoostImporter(), new LightGBMImporter()); this.zone = zone; - this.flagSource = flagSource; this.additionalValidators = List.copyOf(additionalValidators.allComponents()); this.clock = Clock.systemUTC(); @@ -88,7 +84,7 @@ public class VespaModelFactory implements ModelFactory { // For testing only protected VespaModelFactory(ConfigModelRegistry configModelRegistry) { this(new Version(VespaVersion.major, VespaVersion.minor, VespaVersion.micro), configModelRegistry, - Clock.systemUTC(), Zone.defaultZone()); + Clock.systemUTC(), Zone.defaultZone()); } private VespaModelFactory(Version version, ConfigModelRegistry configModelRegistry, Clock clock, Zone zone) { @@ -102,7 +98,6 @@ public class VespaModelFactory implements ModelFactory { this.modelImporters = List.of(); this.additionalValidators = List.of(); this.zone = zone; - this.flagSource = new InMemoryFlagSource(); this.clock = clock; } @@ -197,7 +192,6 @@ public class VespaModelFactory implements ModelFactory { .endpoints(modelContext.properties().endpoints()) .modelImporters(modelImporters) .zone(zone) - .flagSource(flagSource) .now(clock.instant()) .wantedNodeVespaVersion(modelContext.wantedNodeVespaVersion()) .wantedDockerImageRepo(modelContext.wantedDockerImageRepo()) diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java index 0984770ef49..ea579aaf5d1 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/QuotaValidator.java @@ -1,7 +1,6 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.application.validation; -import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.ClusterResources; @@ -10,7 +9,6 @@ import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.QuotaExceededException; import com.yahoo.config.provision.SystemName; -import com.yahoo.config.provision.Zone; import com.yahoo.vespa.model.VespaModel; import com.yahoo.vespa.model.application.validation.Validation.Context; @@ -35,8 +33,9 @@ public class QuotaValidator implements Validator { @Override public void validate(Context context) { var zone = context.deployState().zone(); - var flagSource = context.deployState().flagSource(); - var capacityPolicies = new CapacityPolicies(zone, new Exclusivity(zone, flagSource), flagSource); + var exclusivity = new Exclusivity(zone, context.deployState().featureFlags().sharedHosts()); + var capacityPolicies = new CapacityPolicies(zone, exclusivity, context.model().applicationPackage().getApplicationId(), + context.deployState().featureFlags().adminClusterArchitecture()); var quota = context.deployState().getProperties().quota(); quota.maxClusterSize().ifPresent(maxClusterSize -> validateMaxClusterSize(maxClusterSize, context.model())); quota.budgetAsDecimal().ifPresent(budget -> validateBudget(budget, context, capacityPolicies)); @@ -52,7 +51,7 @@ public class QuotaValidator implements Validator { if (adminClusterIds(context.model()).contains(id)) continue; var cluster = context.model().provisioned().clusters().get(id); var capacity = context.model().provisioned().capacities().getOrDefault(id, zeroCapacity); - maxSpend += capacityPolicies.applyOn(capacity, application, cluster.isExclusive()).maxResources().cost(); + maxSpend += capacityPolicies.applyOn(capacity, cluster.isExclusive()).maxResources().cost(); } var actualSpend = context.model().allocatedHosts().getHosts().stream() diff --git a/config-provisioning/pom.xml b/config-provisioning/pom.xml index 63fb2f30ba6..75047ac8efa 100644 --- a/config-provisioning/pom.xml +++ b/config-provisioning/pom.xml @@ -52,12 +52,6 @@ ${project.version} provided - - com.yahoo.vespa - flags - ${project.version} - provided - com.yahoo.vespa testutil diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java b/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java index dec2984846e..818a448187c 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java @@ -3,15 +3,11 @@ package com.yahoo.config.provision; import com.yahoo.component.Version; import com.yahoo.config.provision.NodeResources.DiskSpeed; -import com.yahoo.vespa.flags.FlagSource; -import com.yahoo.vespa.flags.PermanentFlags; -import com.yahoo.vespa.flags.StringFlag; import java.util.Map; import java.util.TreeMap; import static com.yahoo.config.provision.NodeResources.Architecture; -import static com.yahoo.vespa.flags.Dimension.INSTANCE_ID; import static java.util.Objects.requireNonNull; /** @@ -23,24 +19,26 @@ public class CapacityPolicies { private final Zone zone; private final Exclusivity exclusivity; - private final StringFlag adminClusterNodeArchitecture; + private final ApplicationId applicationId; + private final Architecture adminClusterArchitecture; - public CapacityPolicies(Zone zone, Exclusivity exclusivity, FlagSource flagSource) { + public CapacityPolicies(Zone zone, Exclusivity exclusivity, ApplicationId applicationId, Architecture adminClusterArchitecture) { this.zone = zone; this.exclusivity = exclusivity; - this.adminClusterNodeArchitecture = PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(flagSource); + this.applicationId = applicationId; + this.adminClusterArchitecture = adminClusterArchitecture; } - public Capacity applyOn(Capacity capacity, ApplicationId application, boolean exclusive) { - var min = applyOn(capacity.minResources(), capacity, application, exclusive); - var max = applyOn(capacity.maxResources(), capacity, application, exclusive); + public Capacity applyOn(Capacity capacity, boolean exclusive) { + var min = applyOn(capacity.minResources(), capacity, exclusive); + var max = applyOn(capacity.maxResources(), capacity, exclusive); var groupSize = capacity.groupSize().fromAtMost(max.nodes() / min.groups()) .toAtLeast(min.nodes() / max.groups()); return capacity.withLimits(min, max, groupSize); } - private ClusterResources applyOn(ClusterResources resources, Capacity capacity, ApplicationId application, boolean exclusive) { - int nodes = decideCount(resources.nodes(), capacity.isRequired(), application.instance().isTester()); + private ClusterResources applyOn(ClusterResources resources, Capacity capacity, boolean exclusive) { + int nodes = decideCount(resources.nodes(), capacity.isRequired(), applicationId.instance().isTester()); int groups = decideGroups(resources.nodes(), resources.groups(), nodes); var nodeResources = decideNodeResources(resources.nodeResources(), capacity.isRequired(), exclusive); return new ClusterResources(nodes, groups, nodeResources); @@ -85,31 +83,29 @@ public class CapacityPolicies { return target; } - public ClusterResources specifyFully(ClusterResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { - return resources.with(specifyFully(resources.nodeResources(), clusterSpec, applicationId)); + public ClusterResources specifyFully(ClusterResources resources, ClusterSpec clusterSpec) { + return resources.with(specifyFully(resources.nodeResources(), clusterSpec)); } - public NodeResources specifyFully(NodeResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { - return resources.withUnspecifiedFieldsFrom(defaultResources(clusterSpec, applicationId).with(DiskSpeed.any)); + public NodeResources specifyFully(NodeResources resources, ClusterSpec clusterSpec) { + return resources.withUnspecifiedFieldsFrom(defaultResources(clusterSpec).with(DiskSpeed.any)); } - private NodeResources defaultResources(ClusterSpec clusterSpec, ApplicationId applicationId) { + private NodeResources defaultResources(ClusterSpec clusterSpec) { if (clusterSpec.type() == ClusterSpec.Type.admin) { - Architecture architecture = adminClusterArchitecture(applicationId); - if (exclusivity.allocation(clusterSpec)) { - return smallestExclusiveResources().with(architecture); + return smallestExclusiveResources().with(adminClusterArchitecture); } if (clusterSpec.id().value().equals("cluster-controllers")) { - return clusterControllerResources(clusterSpec, architecture).with(architecture); + return clusterControllerResources(clusterSpec, adminClusterArchitecture).with(adminClusterArchitecture); } if (clusterSpec.id().value().equals("logserver")) { - return logserverResources(architecture).with(architecture); + return logserverResources(adminClusterArchitecture).with(adminClusterArchitecture); } - return versioned(clusterSpec, Map.of(new Version(0), smallestSharedResources())).with(architecture); + return versioned(clusterSpec, Map.of(new Version(0), smallestSharedResources())).with(adminClusterArchitecture); } if (clusterSpec.type() == ClusterSpec.Type.content) { @@ -148,10 +144,6 @@ public class CapacityPolicies { : new NodeResources(0.5, 2, 50, 0.3); } - private Architecture adminClusterArchitecture(ApplicationId instance) { - return Architecture.valueOf(adminClusterNodeArchitecture.with(INSTANCE_ID, instance.serializedForm()).value()); - } - // The lowest amount of resources that can be exclusive allocated (i.e. a matching host flavor for this exists) private NodeResources smallestExclusiveResources() { return zone.cloud().name() == CloudName.AZURE || zone.cloud().name() == CloudName.GCP diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Exclusivity.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Exclusivity.java index 40122c17c3f..6aafb20bee2 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/Exclusivity.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Exclusivity.java @@ -1,10 +1,5 @@ package com.yahoo.config.provision; -import com.yahoo.vespa.flags.FlagSource; -import com.yahoo.vespa.flags.JacksonFlag; -import com.yahoo.vespa.flags.PermanentFlags; -import com.yahoo.vespa.flags.custom.SharedHost; - /** * A class which can be asked if allocations should be exclusive. * @@ -13,16 +8,16 @@ import com.yahoo.vespa.flags.custom.SharedHost; public class Exclusivity { private final Zone zone; - private final JacksonFlag sharedHosts; + private final SharedHosts sharedHost; - public Exclusivity(Zone zone, FlagSource flagSource) { + public Exclusivity(Zone zone, SharedHosts sharedHost) { this.zone = zone; - this.sharedHosts = PermanentFlags.SHARED_HOST.bindTo(flagSource); + this.sharedHost = sharedHost; } /** Returns whether nodes must be allocated to hosts that are exclusive to the cluster type. */ public boolean clusterType(ClusterSpec cluster) { - return sharedHosts.value().hasClusterType(cluster.type().name()); + return sharedHost.hasClusterType(cluster.type()); } /** Returns whether the nodes of this cluster must be running on hosts that are specifically provisioned for the application. */ @@ -38,7 +33,7 @@ public class Exclusivity { public boolean allocation(ClusterSpec clusterSpec) { return clusterSpec.isExclusive() || ( clusterSpec.type().isContainer() && zone.system().isPublic() && !zone.environment().isTest() ) || - ( !zone.cloud().allowHostSharing() && !sharedHosts.value().supportsClusterType(clusterSpec.type().name())); + ( !zone.cloud().allowHostSharing() && !sharedHost.supportsClusterType(clusterSpec.type())); } } diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/SharedHosts.java b/config-provisioning/src/main/java/com/yahoo/config/provision/SharedHosts.java new file mode 100644 index 00000000000..44a91d2861d --- /dev/null +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/SharedHosts.java @@ -0,0 +1,30 @@ +package com.yahoo.config.provision; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * @author hakonhall + */ +public interface SharedHosts { + static SharedHosts empty() { + return new SharedHosts() { + @Override public boolean supportsClusterType(ClusterSpec.Type clusterType) { return false; } + @Override public boolean hasClusterType(ClusterSpec.Type clusterType) { return false; } + }; + } + + static SharedHosts ofConstant(boolean supportsClusterType, boolean hasClusterType) { + return new SharedHosts() { + @Override public boolean supportsClusterType(ClusterSpec.Type clusterType) { return supportsClusterType; } + @Override public boolean hasClusterType(ClusterSpec.Type clusterType) { return hasClusterType; } + }; + } + + /** Whether there are any shared hosts specifically for the given cluster type, or without a cluster type restriction. */ + @JsonIgnore + boolean supportsClusterType(ClusterSpec.Type clusterType); + + /** Whether there are any shared hosts specifically for the given cluster type. */ + @JsonIgnore + boolean hasClusterType(ClusterSpec.Type clusterType); +} diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java index 7189ae12c66..e418a432366 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java @@ -25,6 +25,7 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DataplaneToken; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.HostName; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.Zone; import com.yahoo.container.jdisc.secretstore.SecretStore; import com.yahoo.vespa.config.server.tenant.SecretStoreExternalIdRetriever; @@ -34,6 +35,7 @@ import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.flags.PermanentFlags; import com.yahoo.vespa.flags.StringFlag; import com.yahoo.vespa.flags.UnboundFlag; +import com.yahoo.vespa.flags.custom.SharedHost; import java.io.File; import java.net.URI; @@ -208,6 +210,7 @@ public class ModelContextImpl implements ModelContext { private final int searchHandlerThreadpool; private final int persistenceThreadMaxFeedOpBatchSize; private final boolean logserverOtelCol; + private final SharedHosts sharedHosts; public FeatureFlags(FlagSource source, ApplicationId appId, Version version) { this.defaultTermwiseLimit = flagValue(source, appId, version, Flags.DEFAULT_TERM_WISE_LIMIT); @@ -252,6 +255,11 @@ public class ModelContextImpl implements ModelContext { this.sortBlueprintsByCost = flagValue(source, appId, version, Flags.SORT_BLUEPRINTS_BY_COST); this.persistenceThreadMaxFeedOpBatchSize = flagValue(source, appId, version, Flags.PERSISTENCE_THREAD_MAX_FEED_OP_BATCH_SIZE); this.logserverOtelCol = flagValue(source, appId, version, Flags.LOGSERVER_OTELCOL_AGENT); + SharedHost sharedHost = flagValue(source, appId, version, PermanentFlags.SHARED_HOST); + this.sharedHosts = new SharedHosts() { + @Override public boolean supportsClusterType(ClusterSpec.Type clusterType) { return sharedHost.supportsClusterType(clusterType.name()); } + @Override public boolean hasClusterType(ClusterSpec.Type clusterType) { return sharedHost.hasClusterType(clusterType.name()); } + }; } @Override public int heapSizePercentage() { return heapPercentage; } @@ -304,6 +312,7 @@ public class ModelContextImpl implements ModelContext { @Override public boolean sortBlueprintsByCost() { return sortBlueprintsByCost; } @Override public int persistenceThreadMaxFeedOpBatchSize() { return persistenceThreadMaxFeedOpBatchSize; } @Override public boolean logserverOtelCol() { return logserverOtelCol; } + @Override public SharedHosts sharedHosts() { return sharedHosts; } private static V flagValue(FlagSource source, ApplicationId appId, Version vespaVersion, UnboundFlag flag) { return flag.bindTo(source) diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java index 086f66fd16b..cc46fc381f0 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java @@ -4,16 +4,20 @@ package com.yahoo.vespa.hosted.provision; import com.yahoo.component.AbstractComponent; import com.yahoo.component.annotation.Inject; import com.yahoo.concurrent.maintenance.JobControl; +import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationTransaction; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.EndpointsChecker.HealthChecker; import com.yahoo.config.provision.EndpointsChecker.HealthCheckerProvider; import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.NodeFlavors; +import com.yahoo.config.provision.NodeResources.Architecture; import com.yahoo.config.provision.Zone; import com.yahoo.config.provisioning.NodeRepositoryConfig; import com.yahoo.vespa.curator.Curator; import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.flags.PermanentFlags; import com.yahoo.vespa.hosted.provision.Node.State; import com.yahoo.vespa.hosted.provision.applications.Applications; import com.yahoo.vespa.hosted.provision.archive.ArchiveUriManager; @@ -41,6 +45,8 @@ import java.time.Clock; import java.util.List; import java.util.Optional; +import static com.yahoo.vespa.flags.Dimension.INSTANCE_ID; + /** * The top level singleton in the node repo, providing access to all its state as child objects. * @@ -209,6 +215,14 @@ public class NodeRepository extends AbstractComponent implements HealthCheckerPr public Exclusivity exclusivity() { return exclusivity; } + public CapacityPolicies capacityPoliciesFor(ApplicationId applicationId) { + String adminClusterNodeArchitecture = PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE + .bindTo(flagSource) + .with(INSTANCE_ID, applicationId.serializedForm()) + .value(); + return new CapacityPolicies(zone, exclusivity, applicationId, Architecture.valueOf(adminClusterNodeArchitecture)); + } + /** * Returns ACLs for the children of the given host. * 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 5310ecaa1c2..504965f1992 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 @@ -85,7 +85,7 @@ public class ClusterModel { MetricsDb metricsDb, Clock clock) { this.nodeRepository = nodeRepository; - this.capacityPolicies = new CapacityPolicies(nodeRepository.zone(), nodeRepository.exclusivity(), nodeRepository.flagSource()); + this.capacityPolicies = nodeRepository.capacityPoliciesFor(application.id()); this.application = application; this.clusterSpec = clusterSpec; this.cluster = cluster; @@ -110,7 +110,7 @@ public class ClusterModel { ClusterTimeseries clusterTimeseries, ClusterNodesTimeseries nodeTimeseries) { this.nodeRepository = nodeRepository; - this.capacityPolicies = new CapacityPolicies(nodeRepository.zone(), nodeRepository.exclusivity(), nodeRepository.flagSource()); + this.capacityPolicies = nodeRepository.capacityPoliciesFor(application.id()); this.application = application; this.clusterSpec = clusterSpec; this.cluster = cluster; @@ -439,9 +439,7 @@ public class ClusterModel { double averageReal() { if (nodes.isEmpty()) { // we're estimating - var initialResources = capacityPolicies.specifyFully(cluster.minResources().nodeResources(), - clusterSpec, - application.id()); + var initialResources = capacityPolicies.specifyFully(cluster.minResources().nodeResources(), clusterSpec); return nodeRepository.resourcesCalculator().requestToReal(initialResources, cloudAccount(), nodeRepository.exclusivity().allocation(clusterSpec), diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java index 54e611eef18..51046df90af 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java @@ -9,7 +9,6 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.NodeResources; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.applications.Cluster; -import com.yahoo.config.provision.CapacityPolicies; import java.util.Objects; @@ -68,9 +67,9 @@ public class Limits { public Limits fullySpecified(ClusterSpec clusterSpec, NodeRepository nodeRepository, ApplicationId applicationId) { if (this.isEmpty()) throw new IllegalStateException("Unspecified limits can not be made fully specified"); - var capacityPolicies = new CapacityPolicies(nodeRepository.zone(), nodeRepository.exclusivity(), nodeRepository.flagSource()); - return new Limits(capacityPolicies.specifyFully(min, clusterSpec, applicationId), - capacityPolicies.specifyFully(max, clusterSpec, applicationId), + var capacityPolicies = nodeRepository.capacityPoliciesFor(applicationId); + return new Limits(capacityPolicies.specifyFully(min, clusterSpec), + capacityPolicies.specifyFully(max, clusterSpec), groupSize); } 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 44750988a3e..9a90a1670c3 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 @@ -54,7 +54,6 @@ public class NodeRepositoryProvisioner implements Provisioner { private final NodeRepository nodeRepository; private final AllocationOptimizer allocationOptimizer; - private final CapacityPolicies capacityPolicies; private final Zone zone; private final Preparer preparer; private final Activator activator; @@ -67,7 +66,6 @@ public class NodeRepositoryProvisioner implements Provisioner { Metric metric) { this.nodeRepository = nodeRepository; this.allocationOptimizer = new AllocationOptimizer(nodeRepository); - this.capacityPolicies = new CapacityPolicies(nodeRepository.zone(), nodeRepository.exclusivity(), nodeRepository.flagSource()); this.zone = zone; this.loadBalancerProvisioner = provisionServiceProvider.getLoadBalancerService() .map(lbService -> new LoadBalancerProvisioner(nodeRepository, lbService)); @@ -89,23 +87,24 @@ public class NodeRepositoryProvisioner implements Provisioner { " for application " + application + ", cluster " + cluster); validate(application, cluster, requested, logger); + var capacityPolicies = nodeRepository.capacityPoliciesFor(application); NodeResources resources; NodeSpec nodeSpec; if (requested.type() == NodeType.tenant) { cluster = capacityPolicies.decideExclusivity(requested, cluster); - Capacity actual = capacityPolicies.applyOn(requested, application, cluster.isExclusive()); - ClusterResources target = decideTargetResources(application, cluster, actual); + Capacity actual = capacityPolicies.applyOn(requested, cluster.isExclusive()); + ClusterResources target = decideTargetResources(application, cluster, actual, capacityPolicies); validate(actual, target, cluster, application); logIfDownscaled(requested.minResources().nodes(), actual.minResources().nodes(), cluster, logger); - resources = getNodeResources(cluster, target.nodeResources(), application); + resources = getNodeResources(cluster, target.nodeResources(), application, capacityPolicies); nodeSpec = NodeSpec.from(target.nodes(), target.groups(), resources, cluster.isExclusive(), actual.canFail(), requested.cloudAccount().orElse(nodeRepository.zone().cloud().account()), requested.clusterInfo().hostTTL()); } else { cluster = cluster.withExclusivity(true); - resources = getNodeResources(cluster, requested.minResources().nodeResources(), application); + resources = getNodeResources(cluster, requested.minResources().nodeResources(), application, capacityPolicies); nodeSpec = NodeSpec.from(requested.type(), nodeRepository.zone().cloud().account()); } return asSortedHosts(preparer.prepare(application, cluster, nodeSpec), @@ -134,8 +133,8 @@ public class NodeRepositoryProvisioner implements Provisioner { } } - private NodeResources getNodeResources(ClusterSpec cluster, NodeResources nodeResources, ApplicationId applicationId) { - return capacityPolicies.specifyFully(nodeResources, cluster, applicationId); + private NodeResources getNodeResources(ClusterSpec cluster, NodeResources nodeResources, ApplicationId applicationId, CapacityPolicies capacityPolicies) { + return capacityPolicies.specifyFully(nodeResources, cluster); } @Override @@ -167,13 +166,14 @@ public class NodeRepositoryProvisioner implements Provisioner { * Returns the target cluster resources, a value between the min and max in the requested capacity, * and updates the application store with the received min and max. */ - private ClusterResources decideTargetResources(ApplicationId applicationId, ClusterSpec clusterSpec, Capacity requested) { + private ClusterResources decideTargetResources(ApplicationId applicationId, ClusterSpec clusterSpec, Capacity requested, + CapacityPolicies capacityPolicies) { try (Mutex lock = nodeRepository.applications().lock(applicationId)) { var application = nodeRepository.applications().get(applicationId).orElse(Application.empty(applicationId)) .withCluster(clusterSpec.id(), clusterSpec.isExclusive(), requested); nodeRepository.applications().put(application, lock); var cluster = application.cluster(clusterSpec.id()).get(); - return cluster.target().resources().orElseGet(() -> currentResources(application, clusterSpec, cluster, requested)); + return cluster.target().resources().orElseGet(() -> currentResources(application, clusterSpec, cluster, requested, capacityPolicies)); } } @@ -181,7 +181,8 @@ public class NodeRepositoryProvisioner implements Provisioner { private ClusterResources currentResources(Application application, ClusterSpec clusterSpec, Cluster cluster, - Capacity requested) { + Capacity requested, + CapacityPolicies capacityPolicies) { NodeList nodes = nodeRepository.nodes().list(Node.State.active).owner(application.id()) .cluster(clusterSpec.id()) .not().retired() @@ -189,15 +190,15 @@ public class NodeRepositoryProvisioner implements Provisioner { boolean firstDeployment = nodes.isEmpty(); var current = firstDeployment // start at min, preserve current resources otherwise - ? new AllocatableResources(initialResourcesFrom(requested, clusterSpec, application.id()), clusterSpec, + ? new AllocatableResources(initialResourcesFrom(requested, clusterSpec, application.id(), capacityPolicies), clusterSpec, nodeRepository, requested.cloudAccount().orElse(CloudAccount.empty)) : new AllocatableResources(nodes, nodeRepository); var model = new ClusterModel(nodeRepository, application, clusterSpec, cluster, nodes, current, nodeRepository.metricsDb(), nodeRepository.clock()); return within(Limits.of(requested), model, firstDeployment); } - private ClusterResources initialResourcesFrom(Capacity requested, ClusterSpec clusterSpec, ApplicationId applicationId) { - return capacityPolicies.specifyFully(requested.minResources(), clusterSpec, applicationId); + private ClusterResources initialResourcesFrom(Capacity requested, ClusterSpec clusterSpec, ApplicationId applicationId, CapacityPolicies capacityPolicies) { + return capacityPolicies.specifyFully(requested.minResources(), clusterSpec); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java index 17b7dd6c307..c085a3ed27c 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java @@ -20,6 +20,7 @@ import com.yahoo.config.provision.IntRange; import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.WireguardKey; import com.yahoo.config.provision.WireguardKeyWithTimestamp; @@ -91,7 +92,7 @@ public class MockNodeRepository extends NodeRepository { curator, Clock.fixed(Instant.ofEpochMilli(123), ZoneId.of("Z")), zone, - new Exclusivity(zone, new InMemoryFlagSource()), + new Exclusivity(zone, SharedHosts.empty()), new MockNameResolver().mockAnyLookup(), DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), Optional.empty(), diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java index b9b3d9b1268..fb96d6f798e 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java @@ -6,6 +6,7 @@ import com.yahoo.config.provision.Exclusivity; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeType; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.Zone; import com.yahoo.config.provisioning.FlavorsConfig; import com.yahoo.test.ManualClock; @@ -47,7 +48,7 @@ public class NodeRepositoryTester { curator, clock, zone, - new Exclusivity(zone, flagSource), + new Exclusivity(zone, SharedHosts.empty()), new MockNameResolver().mockAnyLookup(), DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), Optional.empty(), diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java index 6316adae887..e0c8199a882 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java @@ -13,7 +13,6 @@ import com.yahoo.config.provision.NodeResources.DiskSpeed; import com.yahoo.config.provision.NodeResources.StorageType; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.Zone; -import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.vespa.hosted.provision.provisioning.DynamicProvisioningTester; import org.junit.Test; @@ -162,7 +161,7 @@ public class AutoscalingTest { .awsProdSetup(false) .capacity(Capacity.from(min, max)) .initialResources(Optional.empty()) - .hostSharingFlag() + .hostSharing() .build(); fixture.tester().assertResources("Initial resources at min, since flag turns on host sharing", 7, 1, 2.0, 10.0, 384.0, @@ -178,7 +177,7 @@ public class AutoscalingTest { .clusterType(ClusterSpec.Type.container) .capacity(Capacity.from(min, max)) .initialResources(Optional.empty()) - .hostSharingFlag() + .hostSharing() .build(); fixture.tester().assertResources("Initial resources at min, since flag turns on host sharing", 1, 1, 0.5, 4.0, 10.0, @@ -389,10 +388,8 @@ public class AutoscalingTest { .build(); NodeResources defaultResources = - new CapacityPolicies(fixture.tester().nodeRepository().zone(), - fixture.tester().nodeRepository().exclusivity(), - fixture.tester().nodeRepository().flagSource()) - .specifyFully(NodeResources.unspecified(), fixture.clusterSpec, fixture.applicationId); + fixture.tester().nodeRepository().capacityPoliciesFor(fixture.applicationId) + .specifyFully(NodeResources.unspecified(), fixture.clusterSpec); fixture.tester().assertResources("Min number of nodes and default resources", 2, 1, defaultResources, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Fixture.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Fixture.java index 4ce909fece3..f9f3ca9db8d 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Fixture.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/Fixture.java @@ -11,12 +11,10 @@ import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.RegionName; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.Zone; import com.yahoo.vespa.flags.InMemoryFlagSource; -import com.yahoo.vespa.flags.PermanentFlags; -import com.yahoo.vespa.flags.custom.HostResources; -import com.yahoo.vespa.flags.custom.SharedHost; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.applications.Application; @@ -51,7 +49,7 @@ public class Fixture { applicationId = builder.application; clusterSpec = builder.cluster; capacity = builder.capacity; - tester = new DynamicProvisioningTester(builder.zone, builder.resourceCalculator, builder.hostFlavors, builder.flagSource, hostCount); + tester = new DynamicProvisioningTester(builder.zone, builder.resourceCalculator, builder.hostFlavors, builder.flagSource, builder.sharedHosts, hostCount); var deployCapacity = initialResources.isPresent() ? Capacity.from(initialResources.get()) : capacity; tester.deploy(builder.application, builder.cluster, deployCapacity); this.loader = new Loader(this); @@ -178,6 +176,7 @@ public class Fixture { HostResourcesCalculator resourceCalculator = new DynamicProvisioningTester.MockHostResourcesCalculator(zone); final InMemoryFlagSource flagSource = new InMemoryFlagSource(); int hostCount = 0; + SharedHosts sharedHosts = SharedHosts.empty(); public Fixture.Builder zone(Zone zone) { this.zone = zone; @@ -283,9 +282,8 @@ public class Fixture { return this; } - public Fixture.Builder hostSharingFlag() { - var resources = new HostResources(8.0, 32.0, 100.0, 10.0, "fast", "local", null, 6, "x86_64"); - flagSource.withJacksonFlag(PermanentFlags.SHARED_HOST.id(), new SharedHost(List.of(resources)), SharedHost.class); + public Fixture.Builder hostSharing() { + sharedHosts = SharedHosts.ofConstant(true, false); return this; } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java index ca1e47f8ea2..68303535f8c 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.provision.maintenance; import com.yahoo.config.provision.Exclusivity; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.json.Jackson; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -64,18 +65,17 @@ public class CapacityCheckerTester { CapacityCheckerTester() { Curator curator = new MockCurator(); NodeFlavors f = new NodeFlavors(new FlavorConfigBuilder().build()); - var flagSource = new InMemoryFlagSource(); nodeRepository = new NodeRepository(f, new EmptyProvisionServiceProvider(), curator, clock, zone, - new Exclusivity(zone, flagSource), + new Exclusivity(zone, SharedHosts.empty()), new MockNameResolver().mockAnyLookup(), DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), Optional.empty(), Optional.empty(), - flagSource, + new InMemoryFlagSource(), new MemoryMetricsDb(clock), new OrchestratorMock(), true, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java index 78f5d8c9b65..dd43a27b0c3 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java @@ -14,6 +14,7 @@ import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.ApplicationMutex; import com.yahoo.config.provision.RegionName; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.Zone; import com.yahoo.test.ManualClock; import com.yahoo.transaction.NestedTransaction; @@ -260,18 +261,17 @@ public class SpareCapacityMaintainerTest { NodeFlavors flavors = new NodeFlavors(new FlavorConfigBuilder().build()); ManualClock clock = new ManualClock(); var zone = new Zone(Environment.prod, RegionName.from("us-east-3")); - var flagSource = new InMemoryFlagSource(); nodeRepository = new NodeRepository(flavors, new EmptyProvisionServiceProvider(), new MockCurator(), clock, zone, - new Exclusivity(zone, flagSource), + new Exclusivity(zone, SharedHosts.empty()), new MockNameResolver().mockAnyLookup(), DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), Optional.empty(), Optional.empty(), - flagSource, + new InMemoryFlagSource(), new MemoryMetricsDb(clock), new OrchestratorMock(), true, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java index 78a34326949..3a9d03ed6b5 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java @@ -16,6 +16,7 @@ import com.yahoo.config.provision.NodeResources.DiskSpeed; import com.yahoo.config.provision.NodeResources.StorageType; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.RegionName; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.Zone; import com.yahoo.vespa.flags.InMemoryFlagSource; @@ -207,9 +208,8 @@ public class DynamicProvisioningTest { private void assertHostSharing(Environment environment, ClusterSpec.Type clusterType, boolean expectShared) { Zone zone = new Zone(Cloud.builder().dynamicProvisioning(true).allowHostSharing(false).build(), SystemName.Public, environment, RegionName.defaultName()); MockHostProvisioner hostProvisioner = new MockHostProvisioner(new NodeFlavors(ProvisioningTester.createConfig()).getFlavors(), nameResolver, 0); - ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone).hostProvisioner(hostProvisioner).nameResolver(nameResolver).build(); + ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone).hostProvisioner(hostProvisioner).nameResolver(nameResolver).sharedHosts(SharedHosts.ofConstant(true, false)).build(); tester.makeReadyHosts(2, new NodeResources(12, 12, 200, 12)); - tester.flagSource().withJacksonFlag(PermanentFlags.SHARED_HOST.id(), new SharedHost(List.of(new HostResources(4.0, 16.0, 50.0, 0.3, "fast", "local", null, 10, "x86_64"))), SharedHost.class); ApplicationId application = applicationId(); ClusterSpec cluster = ClusterSpec.request(clusterType, ClusterSpec.Id.from("default")).vespaVersion("6.42").build(); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java index 45004ad41ec..4cfb366f7d5 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java @@ -11,6 +11,7 @@ import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.Zone; import com.yahoo.test.ManualClock; import com.yahoo.transaction.Mutex; @@ -52,16 +53,15 @@ public class DynamicProvisioningTester { private final ProvisioningTester provisioningTester; private final Autoscaler autoscaler; private final HostResourcesCalculator hostResourcesCalculator; - private final CapacityPolicies capacityPolicies; - public DynamicProvisioningTester(Zone zone, HostResourcesCalculator resourcesCalculator, List hostFlavors, InMemoryFlagSource flagSource, int hostCount) { - this(zone, hostFlavors, resourcesCalculator, flagSource); + public DynamicProvisioningTester(Zone zone, HostResourcesCalculator resourcesCalculator, List hostFlavors, InMemoryFlagSource flagSource, SharedHosts sharedHosts, int hostCount) { + this(zone, hostFlavors, resourcesCalculator, flagSource, sharedHosts); for (Flavor flavor : hostFlavors) provisioningTester.makeReadyNodes(hostCount, flavor.name(), NodeType.host, 8); provisioningTester.activateTenantHosts(); } - private DynamicProvisioningTester(Zone zone, List flavors, HostResourcesCalculator resourcesCalculator, InMemoryFlagSource flagSource) { + private DynamicProvisioningTester(Zone zone, List flavors, HostResourcesCalculator resourcesCalculator, InMemoryFlagSource flagSource, SharedHosts sharedHosts) { MockHostProvisioner hostProvisioner = null; if (zone.cloud().dynamicProvisioning()) { hostProvisioner = new MockHostProvisioner(flavors); @@ -74,11 +74,11 @@ public class DynamicProvisioningTester { .resourcesCalculator(resourcesCalculator) .flagSource(flagSource) .hostProvisioner(hostProvisioner) + .sharedHosts(sharedHosts) .build(); hostResourcesCalculator = resourcesCalculator; autoscaler = new Autoscaler(nodeRepository()); - capacityPolicies = new CapacityPolicies(zone, provisioningTester.nodeRepository().exclusivity(), flagSource); } public InMemoryProvisionLogger provisionLogger() { return provisioningTester.provisionLogger(); } @@ -159,7 +159,8 @@ public class DynamicProvisioningTester { } public Autoscaling autoscale(ApplicationId applicationId, ClusterSpec cluster, Capacity capacity) { - capacity = capacityPolicies.applyOn(capacity, applicationId, capacityPolicies.decideExclusivity(capacity, cluster).isExclusive()); + var capacityPolicies = provisioningTester.nodeRepository().capacityPoliciesFor(applicationId); + capacity = capacityPolicies.applyOn(capacity, capacityPolicies.decideExclusivity(capacity, cluster).isExclusive()); Application application = nodeRepository().applications().get(applicationId).orElse(Application.empty(applicationId)) .withCluster(cluster.id(), false, capacity); try (Mutex lock = nodeRepository().applications().lock(applicationId)) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java index 8b49e5e5223..390da7c719a 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java @@ -26,6 +26,7 @@ import com.yahoo.config.provision.NodeResources.DiskSpeed; import com.yahoo.config.provision.NodeResources.StorageType; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.RegionName; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.Zone; @@ -94,7 +95,6 @@ public class ProvisioningTester { private final NodeRepository nodeRepository; private final HostProvisioner hostProvisioner; private final NodeRepositoryProvisioner provisioner; - private final CapacityPolicies capacityPolicies; private final InMemoryProvisionLogger provisionLogger; private final LoadBalancerServiceMock loadBalancerService; @@ -112,7 +112,8 @@ public class ProvisioningTester { LoadBalancerServiceMock loadBalancerService, FlagSource flagSource, int spareCount, - ManualClock clock) { + ManualClock clock, + SharedHosts sharedHosts) { this.curator = curator; this.nodeFlavors = nodeFlavors; this.clock = clock; @@ -123,7 +124,7 @@ public class ProvisioningTester { curator, clock, zone, - new Exclusivity(zone, flagSource), + new Exclusivity(zone, sharedHosts), nameResolver, containerImage, Optional.empty(), @@ -134,7 +135,6 @@ public class ProvisioningTester { true, spareCount); this.provisioner = new NodeRepositoryProvisioner(nodeRepository, zone, provisionServiceProvider, new MockMetric()); - this.capacityPolicies = new CapacityPolicies(zone, nodeRepository.exclusivity(), flagSource); this.provisionLogger = new InMemoryProvisionLogger(); this.loadBalancerService = loadBalancerService; } @@ -162,14 +162,13 @@ public class ProvisioningTester { public NodeRepositoryProvisioner provisioner() { return provisioner; } public HostProvisioner hostProvisioner() { return hostProvisioner; } public LoadBalancerServiceMock loadBalancerService() { return loadBalancerService; } - public CapacityPolicies capacityPolicies() { return capacityPolicies; } public NodeList getNodes(ApplicationId id, Node.State ... inState) { return nodeRepository.nodes().list(inState).owner(id); } public InMemoryFlagSource flagSource() { return (InMemoryFlagSource) nodeRepository.flagSource(); } public InMemoryProvisionLogger provisionLogger() { return provisionLogger; } public Node node(String hostname) { return nodeRepository.nodes().node(hostname).get(); } public int decideSize(Capacity capacity, ApplicationId application) { - return capacityPolicies.applyOn(capacity, application, false).minResources().nodes(); + return nodeRepository.capacityPoliciesFor(application).applyOn(capacity, false).minResources().nodes(); } public Node patchNode(Node node, UnaryOperator patcher) { @@ -664,6 +663,7 @@ public class ProvisioningTester { private int spareCount = 0; private ManualClock clock = new ManualClock(); private DockerImage defaultImage = DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"); + private SharedHosts sharedHosts = SharedHosts.empty(); public Builder curator(Curator curator) { this.curator = curator; @@ -745,6 +745,11 @@ public class ProvisioningTester { return this; } + public Builder sharedHosts(SharedHosts sharedHosts) { + this.sharedHosts = sharedHosts; + return this; + } + private FlagSource defaultFlagSource() { return new InMemoryFlagSource(); } @@ -761,7 +766,8 @@ public class ProvisioningTester { new LoadBalancerServiceMock(), Optional.ofNullable(flagSource).orElse(defaultFlagSource()), spareCount, - clock); + clock, + sharedHosts); } private static FlavorsConfig asConfig(List flavors) { diff --git a/standalone-container/pom.xml b/standalone-container/pom.xml index b8379fc8ef5..92faa1ae670 100644 --- a/standalone-container/pom.xml +++ b/standalone-container/pom.xml @@ -55,12 +55,6 @@ ${project.version} provided - - com.yahoo.vespa - flags - ${project.version} - provided - com.yahoo.vespa zkfacade @@ -105,7 +99,6 @@ config-bundle-jar-with-dependencies.jar, config-model-api-jar-with-dependencies.jar, config-model-jar-with-dependencies.jar, - flags-jar-with-dependencies.jar, container-disc-jar-with-dependencies.jar, -- cgit v1.2.3 From 708081bd3c30c1f974bf25f025ec7f97ca4aaa26 Mon Sep 17 00:00:00 2001 From: Håkon Hallingstad Date: Sun, 19 May 2024 10:12:18 +0200 Subject: Make flags depend on config-provisioning --- .../com/yahoo/config/provision/SharedHosts.java | 23 ++++++++-------------- .../config/server/deploy/ModelContextImpl.java | 7 +------ flags/pom.xml | 5 +++++ .../com/yahoo/vespa/flags/custom/SharedHost.java | 14 ++++++++----- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/SharedHosts.java b/config-provisioning/src/main/java/com/yahoo/config/provision/SharedHosts.java index 44a91d2861d..ec2660f45c8 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/SharedHosts.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/SharedHosts.java @@ -1,17 +1,17 @@ package com.yahoo.config.provision; -import com.fasterxml.jackson.annotation.JsonIgnore; - /** * @author hakonhall */ public interface SharedHosts { - static SharedHosts empty() { - return new SharedHosts() { - @Override public boolean supportsClusterType(ClusterSpec.Type clusterType) { return false; } - @Override public boolean hasClusterType(ClusterSpec.Type clusterType) { return false; } - }; - } + + /** Whether there are any shared hosts specifically for the given cluster type, or without a cluster type restriction. */ + boolean supportsClusterType(ClusterSpec.Type clusterType); + + /** Whether there are any shared hosts specifically for the given cluster type. */ + boolean hasClusterType(ClusterSpec.Type clusterType); + + static SharedHosts empty() { return ofConstant(false, false); } static SharedHosts ofConstant(boolean supportsClusterType, boolean hasClusterType) { return new SharedHosts() { @@ -20,11 +20,4 @@ public interface SharedHosts { }; } - /** Whether there are any shared hosts specifically for the given cluster type, or without a cluster type restriction. */ - @JsonIgnore - boolean supportsClusterType(ClusterSpec.Type clusterType); - - /** Whether there are any shared hosts specifically for the given cluster type. */ - @JsonIgnore - boolean hasClusterType(ClusterSpec.Type clusterType); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java index e418a432366..37ac67f2c5f 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java @@ -35,7 +35,6 @@ import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.flags.PermanentFlags; import com.yahoo.vespa.flags.StringFlag; import com.yahoo.vespa.flags.UnboundFlag; -import com.yahoo.vespa.flags.custom.SharedHost; import java.io.File; import java.net.URI; @@ -255,11 +254,7 @@ public class ModelContextImpl implements ModelContext { this.sortBlueprintsByCost = flagValue(source, appId, version, Flags.SORT_BLUEPRINTS_BY_COST); this.persistenceThreadMaxFeedOpBatchSize = flagValue(source, appId, version, Flags.PERSISTENCE_THREAD_MAX_FEED_OP_BATCH_SIZE); this.logserverOtelCol = flagValue(source, appId, version, Flags.LOGSERVER_OTELCOL_AGENT); - SharedHost sharedHost = flagValue(source, appId, version, PermanentFlags.SHARED_HOST); - this.sharedHosts = new SharedHosts() { - @Override public boolean supportsClusterType(ClusterSpec.Type clusterType) { return sharedHost.supportsClusterType(clusterType.name()); } - @Override public boolean hasClusterType(ClusterSpec.Type clusterType) { return sharedHost.hasClusterType(clusterType.name()); } - }; + this.sharedHosts = flagValue(source, appId, version, PermanentFlags.SHARED_HOST); } @Override public int heapSizePercentage() { return heapPercentage; } diff --git a/flags/pom.xml b/flags/pom.xml index 9f3a9d35831..c86b753b38d 100644 --- a/flags/pom.xml +++ b/flags/pom.xml @@ -43,6 +43,11 @@ ${project.version} provided + + com.yahoo.vespa + config-provisioning + ${project.version} + com.fasterxml.jackson.core jackson-databind diff --git a/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java b/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java index 66356d979a4..3f229862d7a 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java @@ -7,6 +7,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.SharedHosts; import com.yahoo.vespa.flags.PermanentFlags; import java.util.List; @@ -19,7 +21,7 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(value = JsonInclude.Include.NON_NULL) -public class SharedHost { +public class SharedHost implements SharedHosts { private final List resources; @@ -43,14 +45,16 @@ public class SharedHost { /** Whether there are any shared hosts specifically for the given cluster type, or without a cluster type restriction. */ @JsonIgnore - public boolean supportsClusterType(String clusterType) { - return resources.stream().anyMatch(resource -> resource.clusterType().map(clusterType::equalsIgnoreCase).orElse(true)); + @Override + public boolean supportsClusterType(ClusterSpec.Type clusterType) { + return resources.stream().anyMatch(resource -> resource.clusterType().map(type -> clusterType.name().equalsIgnoreCase(type)).orElse(true)); } /** Whether there are any shared hosts specifically for the given cluster type. */ @JsonIgnore - public boolean hasClusterType(String clusterType) { - return resources.stream().anyMatch(resource -> resource.clusterType().map(clusterType::equalsIgnoreCase).orElse(false)); + @Override + public boolean hasClusterType(ClusterSpec.Type clusterType) { + return resources.stream().anyMatch(resource -> resource.clusterType().map(type -> clusterType.name().equalsIgnoreCase(type)).orElse(false)); } @JsonIgnore -- cgit v1.2.3 From 107c802740ff383c9bd6796cc8c012ee86f54afc Mon Sep 17 00:00:00 2001 From: Håkon Hallingstad Date: Sun, 19 May 2024 10:34:01 +0200 Subject: Actually resolve the admin-cluster-node-architecture flag --- .../java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java index 37ac67f2c5f..c8d3574182a 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java @@ -25,6 +25,8 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DataplaneToken; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.HostName; +import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.NodeResources.Architecture; import com.yahoo.config.provision.SharedHosts; import com.yahoo.config.provision.Zone; import com.yahoo.container.jdisc.secretstore.SecretStore; @@ -210,6 +212,7 @@ public class ModelContextImpl implements ModelContext { private final int persistenceThreadMaxFeedOpBatchSize; private final boolean logserverOtelCol; private final SharedHosts sharedHosts; + private final Architecture adminClusterArchitecture; public FeatureFlags(FlagSource source, ApplicationId appId, Version version) { this.defaultTermwiseLimit = flagValue(source, appId, version, Flags.DEFAULT_TERM_WISE_LIMIT); @@ -255,6 +258,7 @@ public class ModelContextImpl implements ModelContext { this.persistenceThreadMaxFeedOpBatchSize = flagValue(source, appId, version, Flags.PERSISTENCE_THREAD_MAX_FEED_OP_BATCH_SIZE); this.logserverOtelCol = flagValue(source, appId, version, Flags.LOGSERVER_OTELCOL_AGENT); this.sharedHosts = flagValue(source, appId, version, PermanentFlags.SHARED_HOST); + this.adminClusterArchitecture = Architecture.valueOf(flagValue(source, appId, version, PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE)); } @Override public int heapSizePercentage() { return heapPercentage; } @@ -308,6 +312,7 @@ public class ModelContextImpl implements ModelContext { @Override public int persistenceThreadMaxFeedOpBatchSize() { return persistenceThreadMaxFeedOpBatchSize; } @Override public boolean logserverOtelCol() { return logserverOtelCol; } @Override public SharedHosts sharedHosts() { return sharedHosts; } + @Override public Architecture adminClusterArchitecture() { return adminClusterArchitecture; } private static V flagValue(FlagSource source, ApplicationId appId, Version vespaVersion, UnboundFlag flag) { return flag.bindTo(source) -- cgit v1.2.3 From cc2f70957f0b7fc791be1a6cbd03453d30da70a7 Mon Sep 17 00:00:00 2001 From: Håkon Hallingstad Date: Sun, 19 May 2024 12:24:53 +0200 Subject: config-provisioning must be provided --- flags/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/flags/pom.xml b/flags/pom.xml index c86b753b38d..38e946a62e8 100644 --- a/flags/pom.xml +++ b/flags/pom.xml @@ -47,6 +47,7 @@ com.yahoo.vespa config-provisioning ${project.version} + provided com.fasterxml.jackson.core -- cgit v1.2.3 From 12ea6e856952196c38fc47f6b9318900a046f2a0 Mon Sep 17 00:00:00 2001 From: Håkon Hallingstad Date: Sun, 19 May 2024 13:05:49 +0200 Subject: provided dependencies are not transitive --- configserver-flags/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configserver-flags/pom.xml b/configserver-flags/pom.xml index 5190a65770e..1bb49f3b422 100644 --- a/configserver-flags/pom.xml +++ b/configserver-flags/pom.xml @@ -42,6 +42,12 @@ ${project.version} provided + + com.yahoo.vespa + config-provisioning + ${project.version} + provided + com.yahoo.vespa annotations -- cgit v1.2.3