aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@gmail.com>2021-05-26 17:36:43 +0200
committerJon Bratseth <bratseth@gmail.com>2021-05-26 17:36:43 +0200
commit65a6e1ec48a7c36c5b5d40d3f37ad241fdfff878 (patch)
tree57909160c0e799d9b38d0ed2ed91048ab5ae4cd7 /node-repository
parent4f3e0fcd908e61f7e5ef9ff37b2e3bb5f0cdc70b (diff)
Show info on nearest flavor also with autoscaling
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocationOptimizer.java3
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java54
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java37
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java32
5 files changed, 97 insertions, 32 deletions
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 79b09348d21..0a1c6c5df6b 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
@@ -49,7 +49,6 @@ public class AllocationOptimizer {
new ClusterResources(maximumNodes, maximumNodes, NodeResources.unspecified()));
else
limits = atLeast(minimumNodes, limits);
-
Optional<AllocatableClusterResources> bestAllocation = Optional.empty();
NodeList hosts = nodeRepository.nodes().list().hosts();
for (int groups = limits.min().groups(); groups <= limits.max().groups(); groups++) {
@@ -67,8 +66,8 @@ public class AllocationOptimizer {
nodeResourcesWith(nodesAdjustedForRedundancy,
groupsAdjustedForRedundancy,
limits, target, current, clusterModel));
-
var allocatableResources = AllocatableClusterResources.from(next, current.clusterSpec(), limits, hosts, nodeRepository);
+
if (allocatableResources.isEmpty()) continue;
if (bestAllocation.isEmpty() || allocatableResources.get().preferableTo(bestAllocation.get()))
bestAllocation = allocatableResources;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
index 81a56e4d47e..def992a264b 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
@@ -62,8 +62,8 @@ public class GroupPreparer {
List<Node> surplusActiveNodes, NodeIndices indices, int wantedGroups) {
String allocateOsRequirement = allocateOsRequirementFlag
- .with(FetchVector.Dimension.APPLICATION_ID, application.serializedForm())
- .value();
+ .with(FetchVector.Dimension.APPLICATION_ID, application.serializedForm())
+ .value();
// Try preparing in memory without global unallocated lock. Most of the time there should be no changes and we
// can return nodes previously allocated.
@@ -89,24 +89,16 @@ public class GroupPreparer {
allocateOsRequirement);
NodeType hostType = allocation.nodeType().hostType();
if (canProvisionDynamically(hostType)) {
- final Version osVersion;
- if (allocateOsRequirement.equals("rhel8")) {
- osVersion = new Version(8, Integer.MAX_VALUE /* always use latest 8 version */, 0);
- } else if (allocateOsRequirement.equals("rhel7")) {
- osVersion = new Version(7, Integer.MAX_VALUE /* always use latest 7 version */, 0);
- } else {
- osVersion = nodeRepository.osVersions().targetFor(hostType).orElse(Version.emptyVersion);
- }
HostSharing sharing = hostSharing(requestedNodes, hostType);
List<ProvisionedHost> provisionedHosts = allocation.hostDeficit()
- .map(deficit -> {
- return hostProvisioner.get().provisionHosts(allocation.provisionIndices(deficit.count()),
- hostType,
- deficit.resources(),
- application,
- osVersion,
- sharing);
- })
+ .map(deficit ->
+ hostProvisioner.get().provisionHosts(allocation.provisionIndices(deficit.count()),
+ hostType,
+ deficit.resources(),
+ application,
+ decideOsVersion(allocateOsRequirement, hostType),
+ sharing)
+ )
.orElseGet(List::of);
// At this point we have started provisioning of the hosts, the first priority is to make sure that
@@ -141,12 +133,17 @@ public class GroupPreparer {
List<Node> surplusActiveNodes, Supplier<Integer> nextIndex, int wantedGroups,
Mutex allocationLock, String allocateOsRequirement) {
LockedNodeList allNodes = nodeRepository.nodes().list(allocationLock);
- NodeAllocation allocation = new NodeAllocation(allNodes, application, cluster, requestedNodes,
- nextIndex, nodeRepository);
- NodePrioritizer prioritizer = new NodePrioritizer(
- allNodes, application, cluster, requestedNodes, wantedGroups,
- nodeRepository.zone().getCloud().dynamicProvisioning(), nodeRepository.nameResolver(),
- nodeRepository.resourcesCalculator(), nodeRepository.spareCount(), allocateOsRequirement);
+ NodeAllocation allocation = new NodeAllocation(allNodes, application, cluster, requestedNodes, nextIndex, nodeRepository);
+ NodePrioritizer prioritizer = new NodePrioritizer(allNodes,
+ application,
+ cluster,
+ requestedNodes,
+ wantedGroups,
+ nodeRepository.zone().getCloud().dynamicProvisioning(),
+ nodeRepository.nameResolver(),
+ nodeRepository.resourcesCalculator(),
+ nodeRepository.spareCount(),
+ allocateOsRequirement);
allocation.offer(prioritizer.collect(surplusActiveNodes));
return allocation;
}
@@ -164,4 +161,13 @@ public class GroupPreparer {
return sharing;
}
+ private Version decideOsVersion(String allocateOsRequirement, NodeType hostType) {
+ if (allocateOsRequirement.equals("rhel8"))
+ return new Version(8, Integer.MAX_VALUE /* always use latest 8 version */, 0);
+ else if (allocateOsRequirement.equals("rhel7"))
+ return new Version(7, Integer.MAX_VALUE /* always use latest 7 version */, 0);
+ else
+ return nodeRepository.osVersions().targetFor(hostType).orElse(Version.emptyVersion);
+ }
+
}
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 ab881a68ebe..ee8ce23a5c0 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
@@ -8,6 +8,7 @@ import com.yahoo.config.provision.ApplicationTransaction;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.HostFilter;
import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.NodeResources;
@@ -27,7 +28,6 @@ import com.yahoo.vespa.hosted.provision.autoscale.AllocatableClusterResources;
import com.yahoo.vespa.hosted.provision.autoscale.AllocationOptimizer;
import com.yahoo.vespa.hosted.provision.autoscale.ClusterModel;
import com.yahoo.vespa.hosted.provision.autoscale.Limits;
-import com.yahoo.vespa.hosted.provision.autoscale.MetricsDb;
import com.yahoo.vespa.hosted.provision.autoscale.ResourceTarget;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.filter.ApplicationFilter;
@@ -184,7 +184,7 @@ public class NodeRepositoryProvisioner implements Provisioner {
current,
clusterModel,
limits)
- .orElseThrow(() -> new IllegalArgumentException("No allocation possible within " + limits))
+ .orElseThrow(() -> newNoAllocationPossible(current.clusterSpec(), limits))
.advertisedResources();
}
@@ -224,4 +224,37 @@ public class NodeRepositoryProvisioner implements Provisioner {
}
}
+ private IllegalArgumentException newNoAllocationPossible(ClusterSpec spec, Limits limits) {
+ StringBuilder message = new StringBuilder("No allocation possible within ").append(limits);
+
+ boolean exclusiveHosts = spec.isExclusive() || nodeRepository.zone().getCloud().dynamicProvisioning();
+ if (exclusiveHosts)
+ message.append(". Nearest allowed node resources: ").append(findNearestNodeResources(limits));
+
+ return new IllegalArgumentException(message.toString());
+ }
+
+ private NodeResources findNearestNodeResources(Limits limits) {
+ NodeResources nearestMin = nearestFlavorResources(limits.min().nodeResources());
+ NodeResources nearestMax = nearestFlavorResources(limits.max().nodeResources());
+ if (limits.min().nodeResources().distanceTo(nearestMin) < limits.max().nodeResources().distanceTo(nearestMax))
+ return nearestMin;
+ else
+ return nearestMax;
+ }
+
+ /** Returns the advertised flavor resources which are nearest to the given resources */
+ private NodeResources nearestFlavorResources(NodeResources requestedResources) {
+ NodeResources nearestHostResources = nodeRepository.flavors().getFlavors().stream()
+ .map(flavor -> nodeRepository.resourcesCalculator().advertisedResourcesOf(flavor))
+ .filter(resources -> resources.diskSpeed().compatibleWith(requestedResources.diskSpeed()))
+ .filter(resources -> resources.storageType().compatibleWith(requestedResources.storageType()))
+ .min(Comparator.comparingDouble(resources -> resources.distanceTo(requestedResources)))
+ .orElseThrow()
+ .withBandwidthGbps(requestedResources.bandwidthGbps());
+ if ( nearestHostResources.storageType() == NodeResources.StorageType.remote)
+ nearestHostResources = nearestHostResources.withDiskGb(requestedResources.diskGb());
+ return nearestHostResources;
+ }
+
}
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 d2b701e5312..97f935d273b 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
@@ -45,8 +45,7 @@ class Preparer {
catch (OutOfCapacityException e) {
throw new OutOfCapacityException("Could not satisfy " + requestedNodes +
( wantedGroups > 1 ? " (in " + wantedGroups + " groups)" : "") +
- " in " + application + " " + cluster +
- ": " + e.getMessage());
+ " in " + application + " " + cluster + ": " + e.getMessage());
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
index 3ad9041cdbb..f2ca993f4d7 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
@@ -338,7 +338,8 @@ public class DockerProvisioningTest {
tester.makeReadyHosts(2, hostFlavor.resources()).activateTenantHosts();
ApplicationId app1 = ProvisioningTester.applicationId("app1");
- ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build();
+ ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content,
+ new ClusterSpec.Id("cluster1")).vespaVersion("7").build();
// 5 Gb requested memory becomes 5-3=2 Gb real memory, which is an illegally small amount
var resources = new NodeResources(1, 5, 10, 1);
@@ -346,7 +347,34 @@ public class DockerProvisioningTest {
new ClusterResources(4, 1, resources)));
}
catch (IllegalArgumentException e) {
- assertEquals("No allocation possible within limits: from 2 nodes with [vcpu: 1.0, memory: 5.0 Gb, disk 10.0 Gb, bandwidth: 1.0 Gbps] to 4 nodes with [vcpu: 1.0, memory: 5.0 Gb, disk 10.0 Gb, bandwidth: 1.0 Gbps]",
+ assertEquals("No allocation possible within limits: " +
+ "from 2 nodes with [vcpu: 1.0, memory: 5.0 Gb, disk 10.0 Gb, bandwidth: 1.0 Gbps] " +
+ "to 4 nodes with [vcpu: 1.0, memory: 5.0 Gb, disk 10.0 Gb, bandwidth: 1.0 Gbps]",
+ e.getMessage());
+ }
+ }
+
+ @Test
+ public void exclusive_resources_not_matching_host_causes_failure() {
+ try {
+ Flavor hostFlavor1 = new Flavor(new NodeResources(20, 40, 100, 4));
+ Flavor hostFlavor2 = new Flavor(new NodeResources(30, 40, 100, 4));
+ ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east")))
+ .flavors(List.of(hostFlavor1, hostFlavor2))
+ .build();
+ ApplicationId app1 = ProvisioningTester.applicationId("app1");
+ ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content,
+ new ClusterSpec.Id("cluster1")).exclusive(true).vespaVersion("7").build();
+
+ var resources = new NodeResources(20, 37, 100, 1);
+ tester.activate(app1, cluster1, Capacity.from(new ClusterResources(2, 1, resources),
+ new ClusterResources(4, 1, resources)));
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("No allocation possible within limits: " +
+ "from 2 nodes with [vcpu: 20.0, memory: 37.0 Gb, disk 100.0 Gb, bandwidth: 1.0 Gbps] " +
+ "to 4 nodes with [vcpu: 20.0, memory: 37.0 Gb, disk 100.0 Gb, bandwidth: 1.0 Gbps]. " +
+ "Nearest allowed node resources: [vcpu: 20.0, memory: 40.0 Gb, disk 100.0 Gb, bandwidth: 1.0 Gbps, storage type: remote]",
e.getMessage());
}
}