aboutsummaryrefslogtreecommitdiffstats
path: root/flags
diff options
context:
space:
mode:
authorHåkon Hallingstad <hakon@verizonmedia.com>2020-11-26 15:22:19 +0100
committerHåkon Hallingstad <hakon@verizonmedia.com>2020-11-26 15:22:19 +0100
commitd6e727e30087e321911aa240fee03053cc22b1f4 (patch)
treef20b90d16103704adcad2085a6084252ef826fc8 /flags
parentc1303724a101117e5c7cbf0d3ff9997705c01033 (diff)
Allow preprovision capacity on partially filled hosts
Adds new functionality that can be disabled by setting the compact-preprovision-capacity flag to false. preprovision-capacity can be satisfied by hosts with spare resources. The DynamicProvisioningMaintainer does this as follows: 1. For each cluster in preprovision-capacity, try to a. allocate the cluster using NodePrioritizer b. If there is a deficit, provision the deficit with HostProvisioner, which may provision larger shared hosts depending on shared-hosts, and retry (1) from the first cluster again. c. Otherwise, pretend the nodes are allocated and go to next cluster. 2. All of preprovision-capacity was successfully allocated, and empty hosts are therefore excess that can be deprovisioned.
Diffstat (limited to 'flags')
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java18
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/custom/ClusterCapacity.java89
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/custom/HostCapacity.java73
-rw-r--r--flags/src/test/java/com/yahoo/vespa/flags/custom/ClusterCapacityTest.java41
4 files changed, 142 insertions, 79 deletions
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index cd60a082472..bdbe439dcdf 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -3,7 +3,7 @@ package com.yahoo.vespa.flags;
import com.yahoo.component.Vtag;
import com.yahoo.vespa.defaults.Defaults;
-import com.yahoo.vespa.flags.custom.HostCapacity;
+import com.yahoo.vespa.flags.custom.ClusterCapacity;
import com.yahoo.vespa.flags.custom.SharedHost;
import java.util.List;
@@ -83,11 +83,17 @@ public class Flags {
"Takes effect on the next run of RetiredExpirer.",
HOSTNAME);
- public static final UnboundListFlag<HostCapacity> TARGET_CAPACITY = defineListFlag(
- "preprovision-capacity", List.of(), HostCapacity.class,
- "List of node resources and their count that should be provisioned." +
- "In a dynamically provisioned zone this specifies the unallocated (i.e. pre-provisioned) capacity. " +
- "Otherwise it specifies the total (unallocated or not) capacity.",
+ public static final UnboundListFlag<ClusterCapacity> PREPROVISION_CAPACITY = defineListFlag(
+ "preprovision-capacity", List.of(), ClusterCapacity.class,
+ "Specifies the resources that ought to be immediately available for additional cluster " +
+ "allocations. If the resources are not available, additional hosts will be provisioned. " +
+ "Only applies to dynamically provisioned zones.",
+ "Takes effect on next iteration of DynamicProvisioningMaintainer.");
+
+ public static final UnboundBooleanFlag COMPACT_PREPROVISION_CAPACITY = defineFeatureFlag(
+ "compact-preprovision-capacity", true,
+ "Whether preprovision capacity can be satisfied with available capacity on hosts with " +
+ "existing allocations. Historically preprovision-capacity referred to empty hosts.",
"Takes effect on next iteration of DynamicProvisioningMaintainer.");
public static final UnboundJacksonFlag<SharedHost> SHARED_HOST = defineJacksonFlag(
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/custom/ClusterCapacity.java b/flags/src/main/java/com/yahoo/vespa/flags/custom/ClusterCapacity.java
new file mode 100644
index 00000000000..8970d421681
--- /dev/null
+++ b/flags/src/main/java/com/yahoo/vespa/flags/custom/ClusterCapacity.java
@@ -0,0 +1,89 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.flags.custom;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonGetter;
+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 java.util.Objects;
+import java.util.Optional;
+
+/**
+ * @author freva
+ */
+// @Immutable
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(value = JsonInclude.Include.NON_NULL)
+public class ClusterCapacity {
+ private final int count;
+ private final double vcpu;
+ private final double memoryGb;
+ private final double diskGb;
+ private final Optional<Double> bandwidthGbps;
+
+ @JsonCreator
+ public ClusterCapacity(@JsonProperty("count") int count,
+ @JsonProperty("vcpu") double vcpu,
+ @JsonProperty("memoryGb") double memoryGb,
+ @JsonProperty("diskGb") double diskGb,
+ @JsonProperty("bandwidthGbps") Double bandwidthGbps) {
+ this.count = (int) requireNonNegative("count", count);
+ this.vcpu = requireNonNegative("vcpu", vcpu);
+ this.memoryGb = requireNonNegative("memoryGb", memoryGb);
+ this.diskGb = requireNonNegative("diskGb", diskGb);
+ this.bandwidthGbps = Optional.ofNullable(bandwidthGbps);
+ }
+
+ /** Returns a new ClusterCapacity equal to {@code this}, but with the given count. */
+ public ClusterCapacity withCount(int count) {
+ return new ClusterCapacity(count, vcpu, memoryGb, diskGb, bandwidthGbps.orElse(null));
+ }
+
+ @JsonGetter("count") public int count() { return count; }
+ @JsonGetter("vcpu") public double vcpu() { return vcpu; }
+ @JsonGetter("memoryGb") public double memoryGb() { return memoryGb; }
+ @JsonGetter("diskGb") public double diskGb() { return diskGb; }
+ @JsonGetter("bandwidthGbps") public Double bandwidthGbpsOrNull() { return bandwidthGbps.orElse(null); }
+
+ @JsonIgnore
+ public double bandwidthGbps() { return bandwidthGbps.orElse(1.0); }
+
+ @Override
+ public String toString() {
+ return "ClusterCapacity{" +
+ "count=" + count +
+ ", vcpu=" + vcpu +
+ ", memoryGb=" + memoryGb +
+ ", diskGb=" + diskGb +
+ ", bandwidthGbps=" + bandwidthGbps +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ClusterCapacity that = (ClusterCapacity) o;
+ return count == that.count &&
+ Double.compare(that.vcpu, vcpu) == 0 &&
+ Double.compare(that.memoryGb, memoryGb) == 0 &&
+ Double.compare(that.diskGb, diskGb) == 0 &&
+ ((bandwidthGbps.isEmpty() && that.bandwidthGbps.isEmpty()) ||
+ ((bandwidthGbps.isPresent() && that.bandwidthGbps.isPresent() &&
+ Double.compare(that.bandwidthGbps.get(), bandwidthGbps.get()) == 0)));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(count, vcpu, memoryGb, diskGb, bandwidthGbps);
+ }
+
+ private static double requireNonNegative(String name, double value) {
+ if (value < 0)
+ throw new IllegalArgumentException("'" + name + "' must be positive, was " + value);
+ return value;
+ }
+}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/custom/HostCapacity.java b/flags/src/main/java/com/yahoo/vespa/flags/custom/HostCapacity.java
deleted file mode 100644
index 947520ca2d7..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/custom/HostCapacity.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.custom;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import java.util.Objects;
-
-/**
- * @author freva
- */
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class HostCapacity {
- @JsonProperty("vcpu")
- private final double vcpu;
-
- @JsonProperty("memoryGb")
- private final double memoryGb;
-
- @JsonProperty("diskGb")
- private final double diskGb;
-
- @JsonProperty("count")
- private final int count;
-
- public HostCapacity(@JsonProperty("vcpu") double vcpu,
- @JsonProperty("memoryGb") double memoryGb,
- @JsonProperty("diskGb") double diskGb,
- @JsonProperty("count") int count) {
- this.vcpu = requirePositive("vcpu", vcpu);
- this.memoryGb = requirePositive("memoryGb", memoryGb);
- this.diskGb = requirePositive("diskGb", diskGb);
- this.count = (int) requirePositive("count", count);
- }
-
- public double getVcpu() {
- return vcpu;
- }
-
- public double getMemoryGb() {
- return memoryGb;
- }
-
- public double getDiskGb() {
- return diskGb;
- }
-
- public int getCount() {
- return count;
- }
-
- private static double requirePositive(String name, double value) {
- if (value <= 0)
- throw new IllegalArgumentException("'" + name + "' must be positive, was " + value);
- return value;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- HostCapacity that = (HostCapacity) o;
- return Double.compare(that.vcpu, vcpu) == 0 &&
- Double.compare(that.memoryGb, memoryGb) == 0 &&
- Double.compare(that.diskGb, diskGb) == 0 &&
- count == that.count;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(vcpu, memoryGb, diskGb, count);
- }
-}
diff --git a/flags/src/test/java/com/yahoo/vespa/flags/custom/ClusterCapacityTest.java b/flags/src/test/java/com/yahoo/vespa/flags/custom/ClusterCapacityTest.java
new file mode 100644
index 00000000000..0258b562897
--- /dev/null
+++ b/flags/src/test/java/com/yahoo/vespa/flags/custom/ClusterCapacityTest.java
@@ -0,0 +1,41 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.flags.custom;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+public class ClusterCapacityTest {
+ @Test
+ public void serialization() throws IOException {
+ ClusterCapacity clusterCapacity = new ClusterCapacity(7, 1.2, 3.4, 5.6, null);
+ ObjectMapper mapper = new ObjectMapper();
+ String json = mapper.writeValueAsString(clusterCapacity);
+ assertEquals("{\"count\":7,\"vcpu\":1.2,\"memoryGb\":3.4,\"diskGb\":5.6}", json);
+
+ ClusterCapacity deserialized = mapper.readValue(json, ClusterCapacity.class);
+ assertEquals(1.2, deserialized.vcpu(), 0.0001);
+ assertEquals(3.4, deserialized.memoryGb(), 0.0001);
+ assertEquals(5.6, deserialized.diskGb(), 0.0001);
+ assertEquals(1.0, deserialized.bandwidthGbps(), 0.0001);
+ assertEquals(7, deserialized.count());
+ }
+
+ @Test
+ public void serialization2() throws IOException {
+ ClusterCapacity clusterCapacity = new ClusterCapacity(7, 1.2, 3.4, 5.6, 2.3);
+ ObjectMapper mapper = new ObjectMapper();
+ String json = mapper.writeValueAsString(clusterCapacity);
+ assertEquals("{\"count\":7,\"vcpu\":1.2,\"memoryGb\":3.4,\"diskGb\":5.6,\"bandwidthGbps\":2.3}", json);
+
+ ClusterCapacity deserialized = mapper.readValue(json, ClusterCapacity.class);
+ assertEquals(1.2, deserialized.vcpu(), 0.0001);
+ assertEquals(3.4, deserialized.memoryGb(), 0.0001);
+ assertEquals(5.6, deserialized.diskGb(), 0.0001);
+ assertEquals(2.3, deserialized.bandwidthGbps(), 0.0001);
+ assertEquals(7, deserialized.count());
+ }
+} \ No newline at end of file