diff options
author | Martin Polden <mpolden@mpolden.no> | 2022-05-19 16:05:05 +0200 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2022-05-19 16:20:52 +0200 |
commit | beebe77103244e456e0b6e53af190cd969093be7 (patch) | |
tree | daeeba7061637289cd7a85d1165ded62901ac3bb | |
parent | c649cd5637b141d4e45243824f07e18e6e03f951 (diff) |
Choose node resources with a matching host flavor when exclusive
When using a custom cloud account (always exclusive) we cannot choose a too
small flavor because there may not be any matching host flavor. This currently
works in our own zones because there is always a shared host that can be used
for admin nodes (feature flag is set in all zones) and there is no way to set
exclusivity requirement for those clusters.
4 files changed, 34 insertions, 21 deletions
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 cb5d8dd5042..36b32f0b099 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 @@ -63,7 +63,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 defaultResources = new CapacityPolicies(nodeRepository).defaultNodeResources(clusterSpec, applicationId); + var defaultResources = new CapacityPolicies(nodeRepository).defaultNodeResources(clusterSpec, applicationId, clusterSpec.isExclusive()); var specifiedMin = min.nodeResources().isUnspecified() ? min.with(defaultResources) : min; var specifiedMax = max.nodeResources().isUnspecified() ? max.with(defaultResources) : max; return new Limits(specifiedMin, specifiedMax); 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 4aeb6722ba9..12df01a7538 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 @@ -9,17 +9,17 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.Zone; -import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.flags.JacksonFlag; import com.yahoo.vespa.flags.PermanentFlags; +import com.yahoo.vespa.flags.StringFlag; +import com.yahoo.vespa.flags.custom.SharedHost; import com.yahoo.vespa.hosted.provision.NodeRepository; import java.util.Map; import java.util.TreeMap; -import java.util.function.Function; import static com.yahoo.config.provision.NodeResources.Architecture; import static com.yahoo.vespa.flags.FetchVector.Dimension.APPLICATION_ID; -import static com.yahoo.vespa.flags.PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE; import static java.util.Objects.requireNonNull; /** @@ -31,13 +31,13 @@ import static java.util.Objects.requireNonNull; public class CapacityPolicies { private final Zone zone; - private final Function<ClusterSpec.Type, Boolean> sharedHosts; - private final FlagSource flagSource; + private final JacksonFlag<SharedHost> sharedHosts; + private final StringFlag adminClusterNodeArchitecture; public CapacityPolicies(NodeRepository nodeRepository) { this.zone = nodeRepository.zone(); - this.sharedHosts = type -> PermanentFlags.SHARED_HOST.bindTo(nodeRepository.flagSource()).value().isEnabled(type.name()); - this.flagSource = nodeRepository.flagSource(); + this.sharedHosts = PermanentFlags.SHARED_HOST.bindTo(nodeRepository.flagSource()); + this.adminClusterNodeArchitecture = PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(nodeRepository.flagSource()); } public Capacity applyOn(Capacity capacity, ApplicationId application, boolean exclusive) { @@ -80,22 +80,25 @@ public class CapacityPolicies { return target; } - public NodeResources defaultNodeResources(ClusterSpec clusterSpec, ApplicationId applicationId) { + public NodeResources defaultNodeResources(ClusterSpec clusterSpec, ApplicationId applicationId, boolean exclusive) { if (clusterSpec.type() == ClusterSpec.Type.admin) { - Architecture architecture = Architecture.valueOf( - ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(flagSource) - .with(APPLICATION_ID, applicationId.serializedForm()) - .value()); + Architecture architecture = architecture(applicationId); + + // The lowest amount resources that can be exclusive allocated (i.e. a matching host flavor for this exists) + NodeResources smallestExclusiveResources = new NodeResources(0.5, 4, 50, 0.3); if (clusterSpec.id().value().equals("cluster-controllers")) { + if (requiresExclusiveHost(clusterSpec.type(), exclusive)) { + return versioned(clusterSpec, Map.of(new Version("0"), smallestExclusiveResources)).with(architecture); + } return versioned(clusterSpec, Map.of(new Version("0"), new NodeResources(0.25, 1.14, 10, 0.3), new Version("7.586.50"), new NodeResources(0.25, 1.333, 10, 0.3), new Version("7.586.54"), new NodeResources(0.25, 1.14, 10, 0.3))) .with(architecture); } - return (zone.getCloud().dynamicProvisioning() && ! sharedHosts.apply(clusterSpec.type()) - ? versioned(clusterSpec, Map.of(new Version("0"), new NodeResources(0.5, 4, 50, 0.3))) + return (requiresExclusiveHost(clusterSpec.type(), exclusive) + ? versioned(clusterSpec, Map.of(new Version("0"), smallestExclusiveResources)) : versioned(clusterSpec, Map.of(new Version("0"), new NodeResources(0.5, 2, 50, 0.3)))) .with(architecture); } @@ -105,6 +108,15 @@ public class CapacityPolicies { : versioned(clusterSpec, Map.of(new Version("0"), new NodeResources(1.5, 8, 50, 0.3))); } + private Architecture architecture(ApplicationId instance) { + return Architecture.valueOf(adminClusterNodeArchitecture.with(APPLICATION_ID, instance.serializedForm()).value()); + } + + /** Returns whether an exclusive host is required for given cluster type and exclusivity requirement */ + private boolean requiresExclusiveHost(ClusterSpec.Type type, boolean exclusive) { + return zone.getCloud().dynamicProvisioning() && (exclusive || !sharedHosts.value().isEnabled(type.name())); + } + /** Returns the resources for the newest version not newer than that requested in the cluster spec. */ static NodeResources versioned(ClusterSpec spec, Map<Version, NodeResources> resources) { return requireNonNull(new TreeMap<>(resources).floorEntry(spec.vespaVersion()), 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 c7bb42ded2e..64865c15529 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 @@ -104,20 +104,20 @@ public class NodeRepositoryProvisioner implements Provisioner { logIfDownscaled(requested.minResources().nodes(), actual.minResources().nodes(), cluster, logger); groups = target.groups(); - resources = getNodeResources(cluster, target.nodeResources(), application); + resources = getNodeResources(cluster, target.nodeResources(), application, exclusive); nodeSpec = NodeSpec.from(target.nodes(), resources, exclusive, actual.canFail(), requested.cloudAccount()); } else { groups = 1; // type request with multiple groups is not supported - resources = getNodeResources(cluster, requested.minResources().nodeResources(), application); + resources = getNodeResources(cluster, requested.minResources().nodeResources(), application, true); nodeSpec = NodeSpec.from(requested.type()); } return asSortedHosts(preparer.prepare(application, cluster, nodeSpec, groups), resources); } - private NodeResources getNodeResources(ClusterSpec cluster, NodeResources nodeResources, ApplicationId applicationId) { + private NodeResources getNodeResources(ClusterSpec cluster, NodeResources nodeResources, ApplicationId applicationId, boolean exclusive) { return nodeResources.isUnspecified() - ? capacityPolicies.defaultNodeResources(cluster, applicationId) + ? capacityPolicies.defaultNodeResources(cluster, applicationId, exclusive) : nodeResources; } @@ -178,7 +178,8 @@ public class NodeRepositoryProvisioner implements Provisioner { private ClusterResources initialResourcesFrom(Capacity requested, ClusterSpec clusterSpec, ApplicationId applicationId) { var initial = requested.minResources(); if (initial.nodeResources().isUnspecified()) - initial = initial.with(capacityPolicies.defaultNodeResources(clusterSpec, applicationId)); + initial = initial.with(capacityPolicies.defaultNodeResources(clusterSpec, applicationId, + capacityPolicies.decideExclusivity(requested, clusterSpec.isExclusive()))); return initial; } 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 f28b76ab31f..4140588d1c8 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 @@ -239,7 +239,7 @@ public class AutoscalingTest { ClusterSpec cluster1 = tester.clusterSpec(ClusterSpec.Type.container, "cluster1"); NodeResources defaultResources = - new CapacityPolicies(tester.nodeRepository()).defaultNodeResources(cluster1, application1); + new CapacityPolicies(tester.nodeRepository()).defaultNodeResources(cluster1, application1, false); // deploy tester.deploy(application1, cluster1, Capacity.from(min, max)); |