diff options
author | Jon Bratseth <bratseth@verizonmedia.com> | 2019-05-18 15:44:24 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@verizonmedia.com> | 2019-05-18 15:44:24 +0200 |
commit | fddfaf0d3a98b8ea389e032c8e6fc66a0404f542 (patch) | |
tree | 2455bfb8788d41e37d064ea52b501ab028bc03fb /config-provisioning | |
parent | fac14833ab735244d67abd8114a8ca46befe292f (diff) |
Take disk speed into account
Diffstat (limited to 'config-provisioning')
5 files changed, 152 insertions, 45 deletions
diff --git a/config-provisioning/abi-spec.json b/config-provisioning/abi-spec.json index fc9a3e011d9..f942ae56d64 100644 --- a/config-provisioning/abi-spec.json +++ b/config-provisioning/abi-spec.json @@ -384,6 +384,8 @@ "public java.lang.String name()", "public int cost()", "public boolean isStock()", + "public boolean isConfigured()", + "public com.yahoo.config.provision.NodeResources resources()", "public double getMinMainMemoryAvailableGb()", "public double getMinDiskAvailableGb()", "public boolean hasFastDisk()", @@ -401,8 +403,6 @@ "public boolean hasAtLeast(com.yahoo.config.provision.NodeResources)", "public void freeze()", "public boolean isLargerThan(com.yahoo.config.provision.Flavor)", - "public boolean isConfigured()", - "public com.yahoo.config.provision.NodeResources resources()", "public int hashCode()", "public boolean equals(java.lang.Object)", "public java.lang.String toString()" @@ -590,6 +590,24 @@ ], "fields": [] }, + "com.yahoo.config.provision.NodeResources$DiskSpeed": { + "superClass": "java.lang.Enum", + "interfaces": [], + "attributes": [ + "public", + "final", + "enum" + ], + "methods": [ + "public static com.yahoo.config.provision.NodeResources$DiskSpeed[] values()", + "public static com.yahoo.config.provision.NodeResources$DiskSpeed valueOf(java.lang.String)" + ], + "fields": [ + "public static final enum com.yahoo.config.provision.NodeResources$DiskSpeed fast", + "public static final enum com.yahoo.config.provision.NodeResources$DiskSpeed slow", + "public static final enum com.yahoo.config.provision.NodeResources$DiskSpeed any" + ] + }, "com.yahoo.config.provision.NodeResources": { "superClass": "java.lang.Object", "interfaces": [], @@ -598,14 +616,17 @@ ], "methods": [ "public void <init>(double, double, double)", + "public void <init>(double, double, double, com.yahoo.config.provision.NodeResources$DiskSpeed)", "public double vcpu()", "public double memoryGb()", "public double diskGb()", + "public com.yahoo.config.provision.NodeResources$DiskSpeed diskSpeed()", "public boolean allocateByLegacyName()", "public java.util.Optional legacyName()", "public boolean equals(java.lang.Object)", "public int hashCode()", "public java.lang.String toString()", + "public boolean satisfies(com.yahoo.config.provision.NodeResources)", "public static com.yahoo.config.provision.NodeResources fromLegacyName(java.lang.String)" ], "fields": [] diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/AllocatedHosts.java b/config-provisioning/src/main/java/com/yahoo/config/provision/AllocatedHosts.java index d90f52e971b..de4f3a555bd 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/AllocatedHosts.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/AllocatedHosts.java @@ -39,6 +39,7 @@ public class AllocatedHosts { private static final String vcpuKey = "vcpu"; private static final String memoryKey = "memory"; private static final String diskKey = "disk"; + private static final String diskSpeedKey = "diskSpeed"; /** Wanted version */ private static final String hostSpecVespaVersionKey = "vespaVersion"; @@ -92,6 +93,7 @@ public class AllocatedHosts { resourcesObject.setDouble(vcpuKey, resources.vcpu()); resourcesObject.setDouble(memoryKey, resources.memoryGb()); resourcesObject.setDouble(diskKey, resources.diskGb()); + resourcesObject.setString(diskSpeedKey, diskSpeedToString(resources.diskSpeed())); } } @@ -131,13 +133,34 @@ public class AllocatedHosts { Inspector resources = object.field(resourcesKey); return Optional.of(new Flavor(new NodeResources(resources.field(vcpuKey).asDouble(), resources.field(memoryKey).asDouble(), - resources.field(diskKey).asDouble()))); + resources.field(diskKey).asDouble(), + diskSpeedFromSlime(resources.field(diskSpeedKey))))); } else { return Optional.empty(); } } + private static NodeResources.DiskSpeed diskSpeedFromSlime(Inspector diskSpeed) { + if ( ! diskSpeed.valid()) return NodeResources.DiskSpeed.fast; // TODO: Remove this line after June 2019 + switch (diskSpeed.asString()) { + case "fast" : return NodeResources.DiskSpeed.fast; + case "slow" : return NodeResources.DiskSpeed.slow; + case "any" : return NodeResources.DiskSpeed.any; + default: throw new IllegalStateException("Illegal disk-speed value '" + diskSpeed.asString() + "'"); + } + } + + private static String diskSpeedToString(NodeResources.DiskSpeed diskSpeed) { + switch (diskSpeed) { + case fast : return "fast"; + case slow : return "slow"; + case any : return "any"; + default: throw new IllegalStateException("Illegal disk-speed value '" + diskSpeed + "'"); + } + + } + private static ClusterMembership membershipFromSlime(Inspector object) { return ClusterMembership.from(object.field(hostSpecMembershipKey).asString(), com.yahoo.component.Version.fromString(object.field(hostSpecVespaVersionKey).asString())); diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java index 74147a55a76..8667707883d 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java @@ -25,10 +25,6 @@ public class Flavor { private final int cost; private final boolean isStock; private final Type type; - private final double minCpuCores; - private final double minMainMemoryAvailableGb; - private final double minDiskAvailableGb; - private final boolean fastDisk; private final double bandwidth; private final String description; private final boolean retired; @@ -45,16 +41,15 @@ public class Flavor { this.cost = flavorConfig.cost(); this.isStock = flavorConfig.stock(); this.type = Type.valueOf(flavorConfig.environment()); - this.minCpuCores = flavorConfig.minCpuCores(); - this.minMainMemoryAvailableGb = flavorConfig.minMainMemoryAvailableGb(); - this.minDiskAvailableGb = flavorConfig.minDiskAvailableGb(); - this.fastDisk = flavorConfig.fastDisk(); + this.resources = new NodeResources(flavorConfig.minCpuCores(), + flavorConfig.minMainMemoryAvailableGb(), + flavorConfig.minDiskAvailableGb(), + flavorConfig.fastDisk() ? NodeResources.DiskSpeed.fast : NodeResources.DiskSpeed.slow); this.bandwidth = flavorConfig.bandwidth(); this.description = flavorConfig.description(); this.retired = flavorConfig.retired(); this.replacesFlavors = new ArrayList<>(); this.idealHeadroom = flavorConfig.idealHeadroom(); - this.resources = new NodeResources(minCpuCores, minMainMemoryAvailableGb, minDiskAvailableGb); } /** Creates a *node* flavor from a node resources spec */ @@ -68,10 +63,6 @@ public class Flavor { this.cost = 0; this.isStock = true; this.type = Type.DOCKER_CONTAINER; - this.minCpuCores = resources.vcpu(); - this.minMainMemoryAvailableGb = resources.memoryGb(); - this.minDiskAvailableGb = resources.diskGb(); - this.fastDisk = true; this.bandwidth = 1; this.description = ""; this.retired = false; @@ -93,15 +84,23 @@ public class Flavor { public boolean isStock() { return isStock; } - public double getMinMainMemoryAvailableGb() { return minMainMemoryAvailableGb; } + /** + * True if this is a configured flavor used for hosts, + * false if it is a virtual flavor created on the fly from node resources + */ + public boolean isConfigured() { return configured; } - public double getMinDiskAvailableGb() { return minDiskAvailableGb; } + public NodeResources resources() { return resources; } + + public double getMinMainMemoryAvailableGb() { return resources.memoryGb(); } - public boolean hasFastDisk() { return fastDisk; } + public double getMinDiskAvailableGb() { return resources.diskGb(); } + + public boolean hasFastDisk() { return resources.diskSpeed() == NodeResources.DiskSpeed.fast; } public double getBandwidth() { return bandwidth; } - public double getMinCpuCores() { return minCpuCores; } + public double getMinCpuCores() { return resources.vcpu(); } public String getDescription() { return description; } @@ -170,9 +169,7 @@ public class Flavor { * as large as the given resources. */ public boolean hasAtLeast(NodeResources resources) { - return this.minCpuCores >= resources.vcpu() && - this.minMainMemoryAvailableGb >= resources.memoryGb() && - this.minDiskAvailableGb >= resources.diskGb(); + return this.resources.satisfies(resources); } /** Irreversibly freezes the content of this */ @@ -182,28 +179,21 @@ public class Flavor { /** Returns whether this flavor has at least as much of each hardware resource as the given flavor */ public boolean isLargerThan(Flavor other) { - return this.minCpuCores >= other.minCpuCores && - this.minDiskAvailableGb >= other.minDiskAvailableGb && - this.minMainMemoryAvailableGb >= other.minMainMemoryAvailableGb && - this.fastDisk || ! other.fastDisk; + return hasAtLeast(other.resources); } - /** - * True if this is a configured flavor used for hosts, - * false if it is a virtual flavor created on the fly from node resources - */ - public boolean isConfigured() { return configured; } - - public NodeResources resources() { return resources; } - @Override public int hashCode() { return name.hashCode(); } @Override - public boolean equals(Object other) { - if (other == this) return true; - if ( ! (other instanceof Flavor)) return false; - return ((Flavor)other).name.equals(this.name); + public boolean equals(Object o) { + if (o == this) return true; + if ( ! (o instanceof Flavor)) return false; + Flavor other = (Flavor)o; + if (configured) + return other.name.equals(this.name); + else + return this.resources.equals(other.resources); } @Override diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java index 005bfac6b5c..b5d16c35dac 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java @@ -1,6 +1,7 @@ // Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.config.provision; +import java.util.Objects; import java.util.Optional; /** @@ -10,27 +11,42 @@ import java.util.Optional; */ public class NodeResources { + public enum DiskSpeed { + fast, // SSD disk or similar speed is needed + slow, // This is tuned to work with the speed of spinning disks + any // The performance of the cluster using this does not depend on disk speed + } + private final double vcpu; private final double memoryGb; private final double diskGb; + private final DiskSpeed diskSpeed; private final boolean allocateByLegacyName; /** The legacy (flavor) name of this, or null if none */ private final String legacyName; + /** Create node resources requiring fast disk */ public NodeResources(double vcpu, double memoryGb, double diskGb) { + this(vcpu, memoryGb, diskGb, DiskSpeed.fast); + } + + public NodeResources(double vcpu, double memoryGb, double diskGb, DiskSpeed diskSpeed) { this.vcpu = vcpu; this.memoryGb = memoryGb; this.diskGb = diskGb; + this.diskSpeed = diskSpeed; this.allocateByLegacyName = false; this.legacyName = null; } - private NodeResources(double vcpu, double memoryGb, double diskGb, boolean allocateByLegacyName, String legacyName) { + private NodeResources(double vcpu, double memoryGb, double diskGb, DiskSpeed diskSpeed, + boolean allocateByLegacyName, String legacyName) { this.vcpu = vcpu; this.memoryGb = memoryGb; this.diskGb = diskGb; + this.diskSpeed = diskSpeed; this.allocateByLegacyName = allocateByLegacyName; this.legacyName = legacyName; } @@ -38,6 +54,25 @@ public class NodeResources { public double vcpu() { return vcpu; } public double memoryGb() { return memoryGb; } public double diskGb() { return diskGb; } + public DiskSpeed diskSpeed() { return diskSpeed; } + + public NodeResources subtract(NodeResources other) { + if ( ! this.isInterchangeableWith(other)) + throw new IllegalArgumentException(this + " and " + other + " are not interchangeable"); + return new NodeResources(vcpu - other.vcpu, + memoryGb - other.memoryGb, + diskGb - other.diskGb, + combine(this.diskSpeed, other.diskSpeed)); + } + + public NodeResources add(NodeResources other) { + if ( ! this.isInterchangeableWith(other)) + throw new IllegalArgumentException(this + " and " + other + " are not interchangeable"); + return new NodeResources(vcpu + other.vcpu, + memoryGb + other.memoryGb, + diskGb + other.diskGb, + combine(this.diskSpeed, other.diskSpeed)); + } /** * If this is true, a non-docker legacy name was used to specify this and we'll respect that by mapping directly. @@ -50,6 +85,23 @@ public class NodeResources { return Optional.ofNullable(legacyName); } + private boolean isInterchangeableWith(NodeResources other) { + if (this.allocateByLegacyName != other.allocateByLegacyName) return false; + if (this.allocateByLegacyName) return legacyName.equals(other.legacyName); + + if (this.diskSpeed != DiskSpeed.any && other.diskSpeed != DiskSpeed.any && this.diskSpeed != other.diskSpeed) + return false; + + return true; + } + + private DiskSpeed combine(DiskSpeed a, DiskSpeed b) { + if (a == DiskSpeed.any) return b; + if (b == DiskSpeed.any) return a; + if (a == b) return a; + throw new IllegalArgumentException(a + " cannot be combined with " + b); + } + @Override public boolean equals(Object o) { if (o == this) return true; @@ -62,6 +114,7 @@ public class NodeResources { if (this.vcpu != other.vcpu) return false; if (this.memoryGb != other.memoryGb) return false; if (this.diskGb != other.diskGb) return false; + if (this.diskSpeed != other.diskSpeed) return false; return true; } } @@ -71,7 +124,7 @@ public class NodeResources { if (allocateByLegacyName) return legacyName.hashCode(); else - return (int)(2503 * vcpu + 22123 * memoryGb + 26987 * diskGb); + return (int)(2503 * vcpu + 22123 * memoryGb + 26987 * diskGb + diskSpeed.hashCode()); } @Override @@ -79,7 +132,25 @@ public class NodeResources { if (allocateByLegacyName) return "flavor '" + legacyName + "'"; else - return "[vcpu: " + vcpu + ", memory: " + memoryGb + " Gb, disk " + diskGb + " Gb]"; + return "[vcpu: " + vcpu + ", memory: " + memoryGb + " Gb, disk " + diskGb + " Gb" + + (diskSpeed != DiskSpeed.fast ? ", disk speed: " + diskSpeed : "") + "]"; + } + + /** Returns true if all the resources of this are the same or larger than the given resources */ + public boolean satisfies(NodeResources other) { + if (this.allocateByLegacyName || other.allocateByLegacyName) // resources are not available + return Objects.equals(this.legacyName, other.legacyName); + + if (this.vcpu < other.vcpu()) return false; + if (this.memoryGb < other.memoryGb) return false; + if (this.diskGb < other.diskGb) return false; + + // Why doesn't a fast disk satisfy a slow disk? Because if slow disk is explicitly specified + // (i.e not "any"), you should not randomly, sometimes get a faster disk as that means you may + // draw conclusions about performance on the basis of better resources than you think you have + if (other.diskSpeed != DiskSpeed.any && other.diskSpeed != this.diskSpeed) return false; + + return true; } /** @@ -96,10 +167,10 @@ public class NodeResources { if (cpu == 0) cpu = 0.5; if (cpu == 2 && mem == 8 ) cpu = 1.5; if (cpu == 2 && mem == 12 ) cpu = 2.3; - return new NodeResources(cpu, mem, dsk, false, flavorString); + return new NodeResources(cpu, mem, dsk, DiskSpeed.fast, false, flavorString); } else { // Another legacy flavor: Allocate by direct matching - return new NodeResources(0, 0, 0, true, flavorString); + return new NodeResources(0, 0, 0, DiskSpeed.fast, true, flavorString); } } diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/AllocatedHostsTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/AllocatedHostsTest.java index 3a37c96c612..737c1047197 100644 --- a/config-provisioning/src/test/java/com/yahoo/config/provision/AllocatedHostsTest.java +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/AllocatedHostsTest.java @@ -30,8 +30,10 @@ public class AllocatedHostsTest { List.of("alias1", "alias2"))); hosts.add(new HostSpec("allocated", Optional.of(ClusterMembership.from("container/test/0/0", com.yahoo.component.Version.fromString("6.73.1"))))); - hosts.add(new HostSpec("flavor-from-resources", + hosts.add(new HostSpec("flavor-from-resources-1", Collections.emptyList(), new Flavor(new NodeResources(0.5, 3.1, 4)))); + hosts.add(new HostSpec("flavor-from-resources-2", + Collections.emptyList(), new Flavor(new NodeResources(0.5, 3.1, 4, NodeResources.DiskSpeed.any)))); hosts.add(new HostSpec("configured-flavor", Collections.emptyList(), configuredFlavors.getFlavorOrThrow("C/12/45/100"))); hosts.add(new HostSpec("with-version", |