summaryrefslogtreecommitdiffstats
path: root/config-model-api
diff options
context:
space:
mode:
authorØyvind Grønnesby <oyving@verizonmedia.com>2020-09-30 11:03:43 +0200
committerØyvind Grønnesby <oyving@verizonmedia.com>2020-09-30 11:03:43 +0200
commit68176288649b5ebc3bbf00ae934875c17b9a75b7 (patch)
treeb262777aba8cde3e3c05dd6a2cc3583db0778a00 /config-model-api
parent628a9a9dd57d9eadd3ad786c5cde40ccfec1ba63 (diff)
Use BigDecimal for budget inside the Quota
- To allow budgets below $1/hour we change the internal representation of budget to a decimal number. Some interfaces that assume integers are kept to keep the API stable. - Created a nicer method .unlimited() instead of .empt() to better show semantics. - Added some serialisation tests to make sure we support integers and decimals.
Diffstat (limited to 'config-model-api')
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java2
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/Quota.java60
-rw-r--r--config-model-api/src/test/java/com/yahoo/config/model/api/QuotaTest.java41
3 files changed, 89 insertions, 14 deletions
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 0c78aafcf20..b1145d49f59 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
@@ -112,7 +112,7 @@ public interface ModelContext {
default double feedCoreThreadPoolSizeFactor() { return 4.0; }
default Quota quota() {
- return Quota.empty();
+ return Quota.unlimited();
}
// TODO(bjorncs): Temporary feature flag
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/Quota.java b/config-model-api/src/main/java/com/yahoo/config/model/api/Quota.java
index cb600bc0a5e..3a57e5bb66c 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/Quota.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/Quota.java
@@ -4,7 +4,9 @@ package com.yahoo.config.model.api;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.Slime;
import com.yahoo.slime.SlimeUtils;
+import com.yahoo.slime.Type;
+import java.math.BigDecimal;
import java.util.Objects;
import java.util.Optional;
@@ -14,39 +16,54 @@ import java.util.Optional;
* @author ogronnesby
*/
public class Quota {
+ private static final Quota UNLIMITED = new Quota(Optional.empty(), Optional.empty());
+
private final Optional<Integer> maxClusterSize;
- private final Optional<Integer> budget;
+ private final Optional<BigDecimal> budget;
+
+ // TODO: Remove constructor once Vespa < 7.300 is gone from production
+ public Quota(Optional<Integer> maxClusterSize, Optional<Integer> budget) {
+ this(maxClusterSize, budget.map(BigDecimal::new), true);
+ }
- public Quota(Optional<Integer> maybeClusterSize, Optional<Integer> budget) {
- this.maxClusterSize = maybeClusterSize;
- this.budget = budget;
+ private Quota(Optional<Integer> maxClusterSize, Optional<BigDecimal> budget, boolean isDecimal) {
+ this.maxClusterSize = Objects.requireNonNull(maxClusterSize);
+ this.budget = Objects.requireNonNull(budget);
}
public static Quota fromSlime(Inspector inspector) {
var clusterSize = SlimeUtils.optionalLong(inspector.field("clusterSize"));
- var budget = SlimeUtils.optionalLong(inspector.field("budget"));
- return new Quota(clusterSize.map(Long::intValue), budget.map(Long::intValue));
+ var budget = budgetFromSlime(inspector.field("budget"));
+ return new Quota(clusterSize.map(Long::intValue), budget, true);
+ }
+
+ public Quota withBudget(Optional<BigDecimal> budget) {
+ return new Quota(this.maxClusterSize, budget, true);
+ }
+
+ public Quota withClusterSize(Optional<Integer> clusterSize) {
+ return new Quota(clusterSize, this.budget, true);
}
public Slime toSlime() {
var slime = new Slime();
var root = slime.setObject();
maxClusterSize.ifPresent(clusterSize -> root.setLong("clusterSize", clusterSize));
- budget.ifPresent(b -> root.setLong("budget", b));
+ budget.ifPresent(b -> root.setString("budget", b.toPlainString()));
return slime;
}
- public static Quota empty() {
- return new Quota(Optional.empty(), Optional.empty());
- }
+ public static Quota unlimited() { return UNLIMITED; }
public Optional<Integer> maxClusterSize() {
return maxClusterSize;
}
- public Optional<Integer> budget() {
- return budget;
- }
+ public Optional<BigDecimal> budgetAsDecimal() { return budget; }
+
+ // TODO: Remove once Vespa < 7.300 is gone from production
+ public static Quota empty() { return unlimited(); }
+ public Optional<Integer> budget() { return budget.map(BigDecimal::intValue); }
@Override
public boolean equals(Object o) {
@@ -69,4 +86,21 @@ public class Quota {
", budget=" + budget +
'}';
}
+
+ /**
+ * Since Slime does not support any decimal numeric value that isn't a floating point of some sort, we need
+ * to be liberal in what we accept. Since we are dealing with currency, ideally we would have a decimal
+ * data type all the way through.
+ *
+ * There are three ways of communicating the budget to the Quota class:
+ * 1. A LONG means we are looking at the budget in whole dollars. This is the legacy way.
+ * 2. A STRING formatted as a number is a full precision decimal number. This is the proper way.
+ * 3. A DOUBLE gets translated into a decimal type, but loses precision. This is the CYA way.
+ */
+ private static Optional<BigDecimal> budgetFromSlime(Inspector inspector) {
+ if (inspector.type() == Type.STRING) return Optional.of(inspector.asString()).map(BigDecimal::new);
+ if (inspector.type() == Type.LONG) return Optional.of(inspector.asLong()).map(BigDecimal::new);
+ if (inspector.type() == Type.DOUBLE) return Optional.of(inspector.asDouble()).map(BigDecimal::new);
+ return Optional.empty();
+ }
}
diff --git a/config-model-api/src/test/java/com/yahoo/config/model/api/QuotaTest.java b/config-model-api/src/test/java/com/yahoo/config/model/api/QuotaTest.java
new file mode 100644
index 00000000000..83d4ffbbe72
--- /dev/null
+++ b/config-model-api/src/test/java/com/yahoo/config/model/api/QuotaTest.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.config.model.api;
+
+import com.yahoo.slime.SlimeUtils;
+import org.junit.Test;
+
+
+import java.math.BigDecimal;
+
+import static org.junit.Assert.assertEquals;
+
+public class QuotaTest {
+
+ @Test
+ public void test_serialization_with_integers() {
+ var json = "{\"budget\": 123}";
+ var slime = SlimeUtils.jsonToSlime(json);
+ var quota = Quota.fromSlime(slime.get());
+ assertEquals((Integer) 123, quota.budget().get());
+ assertEquals(123, quota.budgetAsDecimal().get().intValueExact());
+ }
+
+ @Test
+ public void test_serialization_with_floats() {
+ var json = "{\"budget\": 123.4}";
+ var slime = SlimeUtils.jsonToSlime(json);
+ var quota = Quota.fromSlime(slime.get());
+ assertEquals((Integer) 123, quota.budget().get());
+ assertEquals(123.4, quota.budgetAsDecimal().get().doubleValue(), 0.01);
+ }
+
+ @Test
+ public void test_serialization_with_string() {
+ var json = "{\"budget\": \"123.4\"}";
+ var slime = SlimeUtils.jsonToSlime(json);
+ var quota = Quota.fromSlime(slime.get());
+ assertEquals((Integer) 123, quota.budget().get());
+ assertEquals(new BigDecimal("123.4"), quota.budgetAsDecimal().get());
+ }
+
+}