xds: pre-parse custom metric names in WRR load balancer (#12773)
Introduce ParsedMetricName in MetricReportUtils to pre-parse configured
custom metric names into Enums and key Strings on config initialization
in WeightedRoundRobinLoadBalancerConfig, avoiding String parsing
operations in the data path.
This has been done by a combination of a few things
- Streams -> loop
- OptionalDouble -> double : We decided to take a hit here because it
provides semantic correctness over using sentinels.
- Pre parsing instead of hot path substring
OrcaReportListener now utilizes pre-parsed ParsedMetricName objects
during getCustomMetricUtilization to prevent OptionalDouble heap
allocations on the hot path.
Updated test coverage in MetricReportUtilsTest and
WeightedRoundRobinLoadBalancerTest.
# JMH Benchmark Report: MetricReportUtils Optimization
We performed a benchmark comparison of four different custom metric
resolution implementations in the Weighted Round Robin (WRR) load
balancer.
## Benchmark Results
| Benchmark Variant | Average Latency | Normalized Heap Allocations |
Speedup |
| :------------------------------------ | :-------------- |
:-------------------------- | :-------- |
| **Baseline (`String` + Streams)** | 174.46 ns/op | 704.00 B/op | 1x |
| **`ParsedMetricName` + Streams** | 148.95 ns/op | 608.00 B/op | ~1.1x
|
| **`String` + Loop** | 81.61 ns/op | 240.00 B/op | ~2.1x |
| **`ParsedMetricName` + Loop** | 52.92 ns/op | 144.00 B/op | ~3.2x |
| **`ParsedMetricName` + Unboxed Loop** | **43.76 ns/op** | **≈ 0.00
B/op** | **~4.0x** |
---
diff --git a/xds/src/main/java/io/grpc/xds/WeightedRoundRobinLoadBalancer.java b/xds/src/main/java/io/grpc/xds/WeightedRoundRobinLoadBalancer.java
index a8b7e12..6744903 100644
--- a/xds/src/main/java/io/grpc/xds/WeightedRoundRobinLoadBalancer.java
+++ b/xds/src/main/java/io/grpc/xds/WeightedRoundRobinLoadBalancer.java
@@ -41,6 +41,7 @@
import io.grpc.util.ForwardingSubchannel;
import io.grpc.util.MultiChildLoadBalancer;
import io.grpc.xds.internal.MetricReportUtils;
+import io.grpc.xds.internal.MetricReportUtils.ParsedMetricName;
import io.grpc.xds.orca.OrcaOobUtil;
import io.grpc.xds.orca.OrcaOobUtil.OrcaOobReportListener;
import io.grpc.xds.orca.OrcaPerRequestUtil;
@@ -239,7 +240,7 @@
private SubchannelPicker createReadyPicker(Collection<ChildLbState> activeList) {
WeightedRoundRobinPicker picker = new WeightedRoundRobinPicker(ImmutableList.copyOf(activeList),
config.enableOobLoadReport, config.errorUtilizationPenalty, sequence,
- config.metricNamesForComputingUtilization);
+ config.parsedMetricNamesForComputingUtilization);
updateWeight(picker);
return picker;
}
@@ -329,15 +330,15 @@
}
public OrcaReportListener getOrCreateOrcaListener(float errorUtilizationPenalty,
- ImmutableList<String> metricNamesForComputingUtilization) {
+ ImmutableList<ParsedMetricName> parsedMetricNamesForComputingUtilization) {
if (orcaReportListener != null
&& orcaReportListener.errorUtilizationPenalty == errorUtilizationPenalty
- && orcaReportListener.metricNamesForComputingUtilization
- .equals(metricNamesForComputingUtilization)) {
+ && orcaReportListener.parsedMetricNamesForComputingUtilization
+ .equals(parsedMetricNamesForComputingUtilization)) {
return orcaReportListener;
}
orcaReportListener =
- new OrcaReportListener(errorUtilizationPenalty, metricNamesForComputingUtilization);
+ new OrcaReportListener(errorUtilizationPenalty, parsedMetricNamesForComputingUtilization);
return orcaReportListener;
}
@@ -362,17 +363,17 @@
final class OrcaReportListener implements OrcaPerRequestReportListener, OrcaOobReportListener {
private final float errorUtilizationPenalty;
- private final ImmutableList<String> metricNamesForComputingUtilization;
+ private final ImmutableList<ParsedMetricName> parsedMetricNamesForComputingUtilization;
OrcaReportListener(float errorUtilizationPenalty,
- ImmutableList<String> metricNamesForComputingUtilization) {
+ ImmutableList<ParsedMetricName> parsedMetricNamesForComputingUtilization) {
this.errorUtilizationPenalty = errorUtilizationPenalty;
- this.metricNamesForComputingUtilization = metricNamesForComputingUtilization;
+ this.parsedMetricNamesForComputingUtilization = parsedMetricNamesForComputingUtilization;
}
@Override
public void onLoadReport(MetricReport report) {
- double utilization = getUtilization(report, metricNamesForComputingUtilization);
+ double utilization = getUtilization(report);
double newWeight = 0;
if (utilization > 0 && report.getQps() > 0) {
@@ -398,8 +399,8 @@
* if application utilization is > 0, it is returned. If neither are present, the CPU
* utilization is returned.
*/
- private double getUtilization(MetricReport report, ImmutableList<String> metricNames) {
- OptionalDouble customUtil = getCustomMetricUtilization(report, metricNames);
+ private double getUtilization(MetricReport report) {
+ OptionalDouble customUtil = getCustomMetricUtilization(report);
if (customUtil.isPresent()) {
return customUtil.getAsDouble();
}
@@ -411,19 +412,23 @@
}
/**
- * Returns the maximum utilization value among the specified metric names.
+ * Returns the maximum utilization value among the parsed metric names.
* Returns OptionalDouble.empty() if NONE of the specified metrics are present in the report,
- * or if all present metrics are NaN.
- * Returns OptionalDouble.of(maxUtil) if at least one non-NaN metric is present.
+ * or if all present metrics are NaN or non positive.
*/
- private OptionalDouble getCustomMetricUtilization(MetricReport report,
- ImmutableList<String> metricNames) {
- return metricNames.stream()
- .map(name -> MetricReportUtils.getMetric(report, name))
- .filter(OptionalDouble::isPresent)
- .mapToDouble(OptionalDouble::getAsDouble)
- .filter(d -> !Double.isNaN(d) && d > 0)
- .max();
+ private OptionalDouble getCustomMetricUtilization(MetricReport report) {
+ OptionalDouble max = OptionalDouble.empty();
+ for (int i = 0; i < parsedMetricNamesForComputingUtilization.size(); i++) {
+ OptionalDouble opt = MetricReportUtils.getMetricValue(report,
+ parsedMetricNamesForComputingUtilization.get(i));
+ if (opt.isPresent()) {
+ double d = opt.getAsDouble();
+ if (!Double.isNaN(d) && d > 0 && (!max.isPresent() || d > max.getAsDouble())) {
+ max = opt;
+ }
+ }
+ }
+ return max;
}
}
}
@@ -446,7 +451,7 @@
if (config.enableOobLoadReport) {
OrcaOobUtil.setListener(weightedSubchannel,
wChild.getOrCreateOrcaListener(config.errorUtilizationPenalty,
- config.metricNamesForComputingUtilization),
+ config.parsedMetricNamesForComputingUtilization),
OrcaOobUtil.OrcaReportingConfig.newBuilder()
.setReportInterval(config.oobReportingPeriodNanos, TimeUnit.NANOSECONDS).build());
} else {
@@ -516,7 +521,7 @@
WeightedRoundRobinPicker(List<ChildLbState> children, boolean enableOobLoadReport,
float errorUtilizationPenalty, AtomicInteger sequence,
- ImmutableList<String> metricNamesForComputingUtilization) {
+ ImmutableList<ParsedMetricName> parsedMetricNamesForComputingUtilization) {
checkNotNull(children, "children");
Preconditions.checkArgument(!children.isEmpty(), "empty child list");
this.children = children;
@@ -526,7 +531,7 @@
WeightedChildLbState wChild = (WeightedChildLbState) child;
pickers.add(wChild.getCurrentPicker());
reportListeners.add(wChild.getOrCreateOrcaListener(errorUtilizationPenalty,
- metricNamesForComputingUtilization));
+ parsedMetricNamesForComputingUtilization));
}
this.pickers = pickers;
this.reportListeners = reportListeners;
@@ -767,7 +772,7 @@
final long oobReportingPeriodNanos;
final long weightUpdatePeriodNanos;
final float errorUtilizationPenalty;
- final ImmutableList<String> metricNamesForComputingUtilization;
+ final ImmutableList<ParsedMetricName> parsedMetricNamesForComputingUtilization;
public static Builder newBuilder() {
return new Builder();
@@ -783,7 +788,20 @@
this.oobReportingPeriodNanos = oobReportingPeriodNanos;
this.weightUpdatePeriodNanos = weightUpdatePeriodNanos;
this.errorUtilizationPenalty = errorUtilizationPenalty;
- this.metricNamesForComputingUtilization = metricNamesForComputingUtilization;
+
+ ImmutableList.Builder<ParsedMetricName> builder = ImmutableList.builder();
+ if (metricNamesForComputingUtilization != null) {
+ for (int i = 0; i < metricNamesForComputingUtilization.size(); i++) {
+ String metricName = metricNamesForComputingUtilization.get(i);
+ ParsedMetricName parsed = MetricReportUtils.ParsedMetricName.parse(metricName);
+ if (parsed.getMetricType() != MetricReportUtils.MetricType.INVALID) {
+ builder.add(parsed);
+ } else {
+ log.log(Level.FINE, "Invalid custom metric name configured and ignored: " + metricName);
+ }
+ }
+ }
+ this.parsedMetricNamesForComputingUtilization = builder.build();
}
@Override
@@ -799,15 +817,15 @@
&& this.weightUpdatePeriodNanos == that.weightUpdatePeriodNanos
// Float.compare considers NaNs equal
&& Float.compare(this.errorUtilizationPenalty, that.errorUtilizationPenalty) == 0
- && Objects.equals(this.metricNamesForComputingUtilization,
- that.metricNamesForComputingUtilization);
+ && Objects.equals(this.parsedMetricNamesForComputingUtilization,
+ that.parsedMetricNamesForComputingUtilization);
}
@Override
public int hashCode() {
return Objects.hash(blackoutPeriodNanos, weightExpirationPeriodNanos, enableOobLoadReport,
oobReportingPeriodNanos, weightUpdatePeriodNanos, errorUtilizationPenalty,
- metricNamesForComputingUtilization);
+ parsedMetricNamesForComputingUtilization);
}
static final class Builder {
diff --git a/xds/src/main/java/io/grpc/xds/internal/MetricReportUtils.java b/xds/src/main/java/io/grpc/xds/internal/MetricReportUtils.java
index 7da9a3a..4194cab 100644
--- a/xds/src/main/java/io/grpc/xds/internal/MetricReportUtils.java
+++ b/xds/src/main/java/io/grpc/xds/internal/MetricReportUtils.java
@@ -16,10 +16,12 @@
package io.grpc.xds.internal;
+import com.google.auto.value.AutoValue;
import io.grpc.services.MetricReport;
-import java.util.Map;
+import java.util.Optional;
import java.util.OptionalDouble;
+
/**
* Utilities for parsing and resolving metrics from {@link MetricReport}.
*/
@@ -27,41 +29,91 @@
private MetricReportUtils() {}
+ public enum MetricType {
+ CPU_UTILIZATION,
+ APPLICATION_UTILIZATION,
+ MEMORY_UTILIZATION,
+ UTILIZATION,
+ NAMED_METRICS,
+ INVALID
+ }
+
+ @AutoValue
+ public abstract static class ParsedMetricName {
+ public abstract MetricType getMetricType();
+
+ public abstract Optional<String> getKey();
+
+ public static ParsedMetricName create(MetricType metricType, Optional<String> key) {
+ return new AutoValue_MetricReportUtils_ParsedMetricName(metricType, key);
+ }
+
+ /**
+ * Pre-parses a custom metric name into a {@link ParsedMetricName}.
+ *
+ * @param name The custom metric name to parse.
+ * @return The parsed metric name.
+ */
+ public static ParsedMetricName parse(String name) {
+ if (name.equals("cpu_utilization")) {
+ return create(MetricType.CPU_UTILIZATION, Optional.empty());
+ }
+ if (name.equals("application_utilization")) {
+ return create(MetricType.APPLICATION_UTILIZATION, Optional.empty());
+ }
+ if (name.equals("mem_utilization")) {
+ return create(MetricType.MEMORY_UTILIZATION, Optional.empty());
+ }
+ if (name.startsWith("utilization.")) {
+ return create(MetricType.UTILIZATION, Optional.of(name.substring("utilization.".length())));
+ }
+ if (name.startsWith("named_metrics.")) {
+ return create(MetricType.NAMED_METRICS,
+ Optional.of(name.substring("named_metrics.".length())));
+ }
+ return create(MetricType.INVALID, Optional.empty());
+ }
+
+ }
+
/**
- * Resolves a metric value from the report based on the given metric name.
- * The logic checks for specific prefixes to determine where to look up the metric:
- * <ul>
- * <li>"cpu_utilization" -> getCpuUtilization()</li>
- * <li>"application_utilization" -> getApplicationUtilization()</li>
- * <li>"mem_utilization" -> getMemoryUtilization()</li>
- * <li>"utilization." -> lookup in utilizationMetrics</li>
- * <li>"named_metrics." -> lookup in namedMetrics</li>
- * </ul>
+ * Resolves a custom metric value for `parsedMetric`
+ * Returns OptionalDouble.empty() if the metric is absent or invalid.
*
* @param report The metric report to query.
- * @param metricName The name of the custom metric to look up.
- * @return The value of the metric if found, or empty if not found.
+ * @param parsedMetric The parsed metric to lookup.
+ * @return The metric value wrapped in an OptionalDouble, or empty if absent.
*/
- public static OptionalDouble getMetric(MetricReport report, String metricName) {
- if (metricName.equals("cpu_utilization")) {
- return OptionalDouble.of(report.getCpuUtilization());
- } else if (metricName.equals("application_utilization")) {
- return OptionalDouble.of(report.getApplicationUtilization());
- } else if (metricName.equals("mem_utilization")) {
- return OptionalDouble.of(report.getMemoryUtilization());
- } else if (metricName.startsWith("utilization.")) {
- Map<String, Double> map = report.getUtilizationMetrics();
- Double val = map.get(metricName.substring("utilization.".length()));
- if (val != null) {
- return OptionalDouble.of(val);
- }
- } else if (metricName.startsWith("named_metrics.")) {
- Map<String, Double> map = report.getNamedMetrics();
- Double val = map.get(metricName.substring("named_metrics.".length()));
- if (val != null) {
- return OptionalDouble.of(val);
- }
+
+ public static OptionalDouble getMetricValue(MetricReport report, ParsedMetricName parsedMetric) {
+ switch (parsedMetric.getMetricType()) {
+ case CPU_UTILIZATION:
+ return OptionalDouble.of(report.getCpuUtilization());
+ case APPLICATION_UTILIZATION:
+ return OptionalDouble.of(report.getApplicationUtilization());
+ case MEMORY_UTILIZATION:
+ return OptionalDouble.of(report.getMemoryUtilization());
+ case UTILIZATION:
+ if (parsedMetric.getKey().isPresent()) {
+ String key = parsedMetric.getKey().get();
+ Double val = report.getUtilizationMetrics().get(key);
+ if (val != null) {
+ return OptionalDouble.of(val);
+ }
+ }
+ return OptionalDouble.empty();
+ case NAMED_METRICS:
+ if (parsedMetric.getKey().isPresent()) {
+ String key = parsedMetric.getKey().get();
+ Double val = report.getNamedMetrics().get(key);
+ if (val != null) {
+ return OptionalDouble.of(val);
+ }
+ }
+ return OptionalDouble.empty();
+ case INVALID:
+ default:
+ return OptionalDouble.empty();
}
- return OptionalDouble.empty();
}
}
diff --git a/xds/src/test/java/io/grpc/xds/WeightedRoundRobinLoadBalancerProviderTest.java b/xds/src/test/java/io/grpc/xds/WeightedRoundRobinLoadBalancerProviderTest.java
index 7bd1590..0bd3283 100644
--- a/xds/src/test/java/io/grpc/xds/WeightedRoundRobinLoadBalancerProviderTest.java
+++ b/xds/src/test/java/io/grpc/xds/WeightedRoundRobinLoadBalancerProviderTest.java
@@ -29,6 +29,7 @@
import io.grpc.internal.FakeClock;
import io.grpc.internal.JsonParser;
import io.grpc.xds.WeightedRoundRobinLoadBalancer.WeightedRoundRobinLoadBalancerConfig;
+import io.grpc.xds.internal.MetricReportUtils.ParsedMetricName;
import java.io.IOException;
import java.util.Map;
import org.junit.Test;
@@ -112,16 +113,19 @@
}
@Test
- public void parseLoadBalancingConfigCustomMetrics() throws IOException {
+ public void parseLoadBalancingConfigCustomMetricsIgnoresInvalid() throws IOException {
System.setProperty("GRPC_EXPERIMENTAL_WRR_CUSTOM_METRICS", "true");
try {
- String lbConfig = "{\"metricNamesForComputingUtilization\" : [\"foo\", \"bar\"]}";
+ String lbConfig =
+ "{\"metricNamesForComputingUtilization\" : "
+ + "[\"utilization.foo\", \"invalid_name\", \"named_metrics.bar\"]}";
ConfigOrError configOrError = provider.parseLoadBalancingPolicyConfig(
parseJsonObject(lbConfig));
assertThat(configOrError.getConfig()).isNotNull();
WeightedRoundRobinLoadBalancerConfig config =
(WeightedRoundRobinLoadBalancerConfig) configOrError.getConfig();
- assertThat(config.metricNamesForComputingUtilization).containsExactly("foo", "bar");
+ assertThat(config.parsedMetricNamesForComputingUtilization).containsExactly(
+ ParsedMetricName.parse("utilization.foo"), ParsedMetricName.parse("named_metrics.bar"));
} finally {
System.clearProperty("GRPC_EXPERIMENTAL_WRR_CUSTOM_METRICS");
}
diff --git a/xds/src/test/java/io/grpc/xds/WeightedRoundRobinLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/WeightedRoundRobinLoadBalancerTest.java
index d495521..bac62d1 100644
--- a/xds/src/test/java/io/grpc/xds/WeightedRoundRobinLoadBalancerTest.java
+++ b/xds/src/test/java/io/grpc/xds/WeightedRoundRobinLoadBalancerTest.java
@@ -291,11 +291,11 @@
WeightedChildLbState weightedChild1 = (WeightedChildLbState) getChild(weightedPicker, 0);
WeightedChildLbState weightedChild2 = (WeightedChildLbState) getChild(weightedPicker, 1);
weightedChild1.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.1, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
weightedChild2.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.2, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
int expectedTasks = isEnabledHappyEyeballs() ? 2 : 1;
@@ -348,11 +348,11 @@
WeightedChildLbState weightedChild1 = (WeightedChildLbState) getChild(weightedPicker, 0);
WeightedChildLbState weightedChild2 = (WeightedChildLbState) getChild(weightedPicker, 1);
weightedChild1.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.1, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
weightedChild2.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.9, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
int expectedTasks = isEnabledHappyEyeballs() ? 2 : 1;
@@ -409,11 +409,11 @@
WeightedChildLbState weightedChild2 = (WeightedChildLbState) getChild(weightedPicker, 1);
WeightedChildLbState weightedChild3 = (WeightedChildLbState) getChild(weightedPicker, 2);
weightedChild1.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(r1);
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(r1);
weightedChild2.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(r2);
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(r2);
weightedChild3.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(r3);
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(r3);
assertThat(fakeClock.forwardTime(11, TimeUnit.SECONDS)).isEqualTo(1);
Map<EquivalentAddressGroup, Integer> pickCount = new HashMap<>();
@@ -611,11 +611,11 @@
WeightedChildLbState weightedChild1 = (WeightedChildLbState) getChild(weightedPicker, 0);
WeightedChildLbState weightedChild2 = (WeightedChildLbState) getChild(weightedPicker, 1);
weightedChild1.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.1, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
weightedChild2.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.2, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
int expectedCount = isEnabledHappyEyeballs() ? 2 : 1;
@@ -676,11 +676,11 @@
WeightedChildLbState weightedChild1 = (WeightedChildLbState) getChild(weightedPicker, 0);
WeightedChildLbState weightedChild2 = (WeightedChildLbState) getChild(weightedPicker, 1);
weightedChild1.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.1, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
weightedChild2.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.2, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
int expectedTasks = isEnabledHappyEyeballs() ? 2 : 1;
@@ -695,11 +695,11 @@
.setAttributes(affinity).build()));
assertThat(getNumFilteredPendingTasks()).isEqualTo(1);
weightedChild1.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.2, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
weightedChild2.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.1, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
//timer fires, new weight updated
@@ -732,11 +732,11 @@
WeightedChildLbState weightedChild1 = (WeightedChildLbState) getChild(weightedPicker, 0);
WeightedChildLbState weightedChild2 = (WeightedChildLbState) getChild(weightedPicker, 1);
weightedChild1.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.1, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
weightedChild2.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.2, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
int expectedTasks = isEnabledHappyEyeballs() ? 2 : 1;
@@ -840,11 +840,11 @@
WeightedChildLbState weightedChild1 = (WeightedChildLbState) getChild(weightedPicker, 0);
WeightedChildLbState weightedChild2 = (WeightedChildLbState) getChild(weightedPicker, 1);
weightedChild1.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.1, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
weightedChild2.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.2, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
assertThat(fakeClock.forwardTime(10, TimeUnit.SECONDS)).isEqualTo(1);
@@ -883,11 +883,11 @@
WeightedChildLbState weightedChild1 = (WeightedChildLbState) getChild(weightedPicker, 0);
WeightedChildLbState weightedChild2 = (WeightedChildLbState) getChild(weightedPicker, 1);
weightedChild1.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.1, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
weightedChild2.new OrcaReportListener(weightedConfig.errorUtilizationPenalty,
- weightedConfig.metricNamesForComputingUtilization).onLoadReport(
+ weightedConfig.parsedMetricNamesForComputingUtilization).onLoadReport(
InternalCallMetricRecorder.createMetricReport(
0.2, 0, 0.1, 1, 0, new HashMap<>(), new HashMap<>(), new HashMap<>()));
CyclicBarrier barrier = new CyclicBarrier(2);
@@ -1224,7 +1224,8 @@
// can be calculated, but it's still essentially round_robin
Iterator<ChildLbState> childLbStates = wrr.getChildLbStates().iterator();
((WeightedChildLbState) childLbStates.next()).new OrcaReportListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization)
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization)
.onLoadReport(InternalCallMetricRecorder.createMetricReport(0.1, 0, 0.1, 1, 0,
new HashMap<>(), new HashMap<>(), new HashMap<>()));
@@ -1232,11 +1233,13 @@
// Now send a second child LB state an ORCA update, so there's real weights
((WeightedChildLbState) childLbStates.next()).new OrcaReportListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization)
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization)
.onLoadReport(InternalCallMetricRecorder.createMetricReport(0.1, 0, 0.1, 1, 0,
new HashMap<>(), new HashMap<>(), new HashMap<>()));
((WeightedChildLbState) childLbStates.next()).new OrcaReportListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization)
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization)
.onLoadReport(InternalCallMetricRecorder.createMetricReport(0.1, 0, 0.1, 1, 0,
new HashMap<>(), new HashMap<>(), new HashMap<>()));
@@ -1355,7 +1358,8 @@
WeightedChildLbState weightedChild =
(WeightedChildLbState) wrr.getChildLbStates().iterator().next();
WeightedChildLbState.OrcaReportListener listener = weightedChild.getOrCreateOrcaListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization);
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization);
Map<String, Double> namedMetrics = new HashMap<>();
namedMetrics.put("cost", 0.5);
@@ -1389,7 +1393,8 @@
WeightedChildLbState weightedChild =
(WeightedChildLbState) wrr.getChildLbStates().iterator().next();
WeightedChildLbState.OrcaReportListener listener = weightedChild.getOrCreateOrcaListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization);
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization);
// custom metric is NaN, App util = 0.8
Map<String, Double> namedMetrics = new HashMap<>();
@@ -1424,7 +1429,8 @@
WeightedChildLbState weightedChild =
(WeightedChildLbState) wrr.getChildLbStates().iterator().next();
WeightedChildLbState.OrcaReportListener listener = weightedChild.getOrCreateOrcaListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization);
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization);
Map<String, Double> namedMetrics = new HashMap<>();
namedMetrics.put("cost", 0.5);
@@ -1456,7 +1462,8 @@
WeightedChildLbState weightedChild =
(WeightedChildLbState) wrr.getChildLbStates().iterator().next();
WeightedChildLbState.OrcaReportListener listener = weightedChild.getOrCreateOrcaListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization);
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization);
// custom metric is NaN, but CPU is 0.1
Map<String, Double> namedMetrics = new HashMap<>();
@@ -1493,7 +1500,8 @@
WeightedChildLbState weightedChild =
(WeightedChildLbState) wrr.getChildLbStates().iterator().next();
WeightedChildLbState.OrcaReportListener listener = weightedChild.getOrCreateOrcaListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization);
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization);
Map<String, Double> namedMetrics = new HashMap<>();
namedMetrics.put("cost", 0.5);
@@ -1528,7 +1536,8 @@
WeightedChildLbState weightedChild =
(WeightedChildLbState) wrr.getChildLbStates().iterator().next();
WeightedChildLbState.OrcaReportListener listener = weightedChild.getOrCreateOrcaListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization);
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization);
Map<String, Double> namedMetrics = new HashMap<>();
namedMetrics.put("cost", Double.NaN);
@@ -1564,7 +1573,8 @@
WeightedChildLbState weightedChild =
(WeightedChildLbState) wrr.getChildLbStates().iterator().next();
WeightedChildLbState.OrcaReportListener listener = weightedChild.getOrCreateOrcaListener(
- weightedConfig.errorUtilizationPenalty, weightedConfig.metricNamesForComputingUtilization);
+ weightedConfig.errorUtilizationPenalty,
+ weightedConfig.parsedMetricNamesForComputingUtilization);
Map<String, Double> namedMetrics = new HashMap<>();
namedMetrics.put("cost", Double.NaN);
diff --git a/xds/src/test/java/io/grpc/xds/internal/MetricReportUtilsTest.java b/xds/src/test/java/io/grpc/xds/internal/MetricReportUtilsTest.java
index bf5e0ae..9d7a391 100644
--- a/xds/src/test/java/io/grpc/xds/internal/MetricReportUtilsTest.java
+++ b/xds/src/test/java/io/grpc/xds/internal/MetricReportUtilsTest.java
@@ -35,59 +35,76 @@
public class MetricReportUtilsTest {
@Test
- public void getMetric_cpuUtilization() {
+ public void getMetricValue_cpuUtilization() {
MetricReport report = createMetricReport(0.5, 0.1, 0.2, 10.0, 5.0, Collections.emptyMap());
- OptionalDouble result = MetricReportUtils.getMetric(report, "cpu_utilization");
+ MetricReportUtils.ParsedMetricName parsed =
+ MetricReportUtils.ParsedMetricName.parse("cpu_utilization");
+ OptionalDouble result = MetricReportUtils.getMetricValue(report, parsed);
assertTrue(result.isPresent());
assertEquals(0.5, result.getAsDouble(), 0.0001);
}
@Test
- public void getMetric_applicationUtilization() {
+ public void getMetricValue_applicationUtilization() {
MetricReport report = createMetricReport(0.5, 0.1, 0.2, 10.0, 5.0, Collections.emptyMap());
- OptionalDouble result = MetricReportUtils.getMetric(report, "application_utilization");
+ MetricReportUtils.ParsedMetricName parsed =
+ MetricReportUtils.ParsedMetricName.parse("application_utilization");
+ OptionalDouble result = MetricReportUtils.getMetricValue(report, parsed);
assertTrue(result.isPresent());
assertEquals(0.1, result.getAsDouble(), 0.0001);
}
@Test
- public void getMetric_memUtilization() {
+ public void getMetricValue_memUtilization() {
MetricReport report = createMetricReport(0.5, 0.1, 0.2, 10.0, 5.0, Collections.emptyMap());
- OptionalDouble result = MetricReportUtils.getMetric(report, "mem_utilization");
+ MetricReportUtils.ParsedMetricName parsed =
+ MetricReportUtils.ParsedMetricName.parse("mem_utilization");
+ OptionalDouble result = MetricReportUtils.getMetricValue(report, parsed);
assertTrue(result.isPresent());
assertEquals(0.2, result.getAsDouble(), 0.0001);
}
@Test
- public void getMetric_utilizationMetric() {
+ public void getMetricValue_utilizationMetric() {
Map<String, Double> utilizationMetrics = new HashMap<>();
utilizationMetrics.put("foo", 1.23);
MetricReport report = InternalCallMetricRecorder.createMetricReport(
- 0, 0, 0, 0, 0, Collections.emptyMap(), utilizationMetrics, Collections.emptyMap());
+ 0, 0, 0, 0, 0, Collections.emptyMap(), utilizationMetrics, Collections.emptyMap());
- OptionalDouble result = MetricReportUtils.getMetric(report, "utilization.foo");
+ MetricReportUtils.ParsedMetricName parsed =
+ MetricReportUtils.ParsedMetricName.parse("utilization.foo");
+ OptionalDouble result = MetricReportUtils.getMetricValue(report, parsed);
assertTrue(result.isPresent());
assertEquals(1.23, result.getAsDouble(), 0.0001);
- assertFalse(MetricReportUtils.getMetric(report, "utilization.bar").isPresent());
+
+ MetricReportUtils.ParsedMetricName bad =
+ MetricReportUtils.ParsedMetricName.parse("utilization.bar");
+ assertFalse(MetricReportUtils.getMetricValue(report, bad).isPresent());
}
@Test
- public void getMetric_namedMetric() {
+ public void getMetricValue_namedMetric() {
Map<String, Double> namedMetrics = new HashMap<>();
namedMetrics.put("foo", 7.89);
MetricReport report = createMetricReport(0, 0, 0, 0, 0, namedMetrics);
- OptionalDouble result = MetricReportUtils.getMetric(report, "named_metrics.foo");
+
+ MetricReportUtils.ParsedMetricName parsed =
+ MetricReportUtils.ParsedMetricName.parse("named_metrics.foo");
+ OptionalDouble result = MetricReportUtils.getMetricValue(report, parsed);
assertTrue(result.isPresent());
assertEquals(7.89, result.getAsDouble(), 0.0001);
- assertFalse(MetricReportUtils.getMetric(report, "named_metrics.bar").isPresent());
+ MetricReportUtils.ParsedMetricName bad =
+ MetricReportUtils.ParsedMetricName.parse("named_metrics.bar");
+ assertFalse(MetricReportUtils.getMetricValue(report, bad).isPresent());
}
@Test
- public void getMetric_unknownPrefix() {
- MetricReport report = createMetricReport(0, 0, 0, 0, 0, Collections.emptyMap());
- assertFalse(MetricReportUtils.getMetric(report, "unknown.foo").isPresent());
- assertFalse(MetricReportUtils.getMetric(report, "foo").isPresent());
+ public void getMetricValue_invalidMetric() {
+ MetricReport report = createMetricReport(0.5, 0.1, 0.2, 10.0, 5.0, Collections.emptyMap());
+ MetricReportUtils.ParsedMetricName invalid =
+ MetricReportUtils.ParsedMetricName.parse("invalid_metric");
+ assertFalse(MetricReportUtils.getMetricValue(report, invalid).isPresent());
}
private MetricReport createMetricReport(double cpu, double app, double mem, double qps,