services,orca: update backend metrics support to allow for server-wide metrics recording (per-call and OOB) (#9902)

Also added input range validation.
This commit is contained in:
Daniel Liu 2023-04-10 11:45:04 -07:00 committed by GitHub
parent 11a1f9e3e8
commit 5201e49ce1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 250 additions and 45 deletions

View File

@ -63,7 +63,7 @@ public class CustomBackendMetricsServer {
// Enable OOB custom backend metrics reporting.
.addService(orcaOobService)
// Enable per-query custom backend metrics reporting.
.intercept(OrcaMetricReportingServerInterceptor.getInstance())
.intercept(OrcaMetricReportingServerInterceptor.create(metricRecorder))
.build()
.start();
logger.info("Server started, listening on " + port);

View File

@ -1777,7 +1777,7 @@ public abstract class AbstractInteropTest {
final TestOrcaReport answer2 = TestOrcaReport.newBuilder()
.setCpuUtilization(0.29309)
.setMemoryUtilization(0.2)
.putUtilization("util", 100.2039)
.putUtilization("util", 0.2039)
.build();
final int retryLimit = 5;

View File

@ -164,7 +164,7 @@ public class TestServiceServer {
ServerInterceptors.intercept(
new TestServiceImpl(executor, metricRecorder), TestServiceImpl.interceptors()))
.addService(orcaOobService)
.intercept(OrcaMetricReportingServerInterceptor.getInstance())
.intercept(OrcaMetricReportingServerInterceptor.create(metricRecorder))
.build()
.start();
}

View File

@ -37,6 +37,7 @@ java_library(
"src/main/java/io/grpc/services/CallMetricRecorder.java",
"src/main/java/io/grpc/services/MetricRecorder.java",
"src/main/java/io/grpc/services/MetricReport.java",
"src/main/java/io/grpc/services/MetricRecorderHelper.java",
],
deps = [
"//api",

View File

@ -65,8 +65,8 @@ public final class CallMetricRecorder {
}
/**
* Records a call metric measurement for utilization.
* If RPC has already finished, this method is no-op.
* Records a call metric measurement for utilization in the range [0, 1]. Values outside the valid
* range are ignored. If RPC has already finished, this method is no-op.
*
* <p>A latter record will overwrite its former name-sakes.
*
@ -74,7 +74,7 @@ public final class CallMetricRecorder {
* @since 1.23.0
*/
public CallMetricRecorder recordUtilizationMetric(String name, double value) {
if (disabled) {
if (disabled || !MetricRecorderHelper.isUtilizationValid(value)) {
return this;
}
if (utilizationMetrics.get() == null) {
@ -126,8 +126,8 @@ public final class CallMetricRecorder {
}
/**
* Records a call metric measurement for CPU utilization.
* If RPC has already finished, this method is no-op.
* Records a call metric measurement for CPU utilization in the range [0, 1]. Values outside the
* valid range are ignored. If RPC has already finished, this method is no-op.
*
* <p>A latter record will overwrite its former name-sakes.
*
@ -135,7 +135,7 @@ public final class CallMetricRecorder {
* @since 1.47.0
*/
public CallMetricRecorder recordCpuUtilizationMetric(double value) {
if (disabled) {
if (disabled || !MetricRecorderHelper.isUtilizationValid(value)) {
return this;
}
cpuUtilizationMetric = value;
@ -143,8 +143,8 @@ public final class CallMetricRecorder {
}
/**
* Records a call metric measurement for memory utilization.
* If RPC has already finished, this method is no-op.
* Records a call metric measurement for memory utilization in the range [0, 1]. Values outside
* the valid range are ignored. If RPC has already finished, this method is no-op.
*
* <p>A latter record will overwrite its former name-sakes.
*
@ -152,7 +152,7 @@ public final class CallMetricRecorder {
* @since 1.47.0
*/
public CallMetricRecorder recordMemoryUtilizationMetric(double value) {
if (disabled) {
if (disabled || !MetricRecorderHelper.isUtilizationValid(value)) {
return this;
}
memoryUtilizationMetric = value;
@ -160,8 +160,8 @@ public final class CallMetricRecorder {
}
/**
* Records a call metric measurement for qps.
* If RPC has already finished, this method is no-op.
* Records a call metric measurement for qps in the range [0, inf). Values outside the valid range
* are ignored. If RPC has already finished, this method is no-op.
*
* <p>A latter record will overwrite its former name-sakes.
*
@ -169,7 +169,7 @@ public final class CallMetricRecorder {
* @since 1.54.0
*/
public CallMetricRecorder recordQpsMetric(double value) {
if (disabled) {
if (disabled || !MetricRecorderHelper.isQpsValid(value)) {
return this;
}
qps = value;

View File

@ -39,14 +39,18 @@ public final class MetricRecorder {
private MetricRecorder() {}
/**
* Update the metrics value corresponding to the specified key.
* Update the metrics value in the range [0, 1] corresponding to the specified key. Values outside
* the valid range are ignored.
*/
public void putUtilizationMetric(String key, double value) {
if (!MetricRecorderHelper.isUtilizationValid(value)) {
return;
}
metricsData.put(key, value);
}
/**
* Replace the whole metrics data using the specified map.
* Replace the whole metrics data using the specified map. No range validation.
*/
public void setAllUtilizationMetrics(Map<String, Double> metrics) {
metricsData = new ConcurrentHashMap<>(metrics);
@ -60,9 +64,13 @@ public final class MetricRecorder {
}
/**
* Update the CPU utilization metrics data.
* Update the CPU utilization metrics data in the range [0, 1]. Values outside the valid range are
* ignored.
*/
public void setCpuUtilizationMetric(double value) {
if (!MetricRecorderHelper.isUtilizationValid(value)) {
return;
}
cpuUtilization = value;
}
@ -74,9 +82,13 @@ public final class MetricRecorder {
}
/**
* Update the memory utilization metrics data.
* Update the memory utilization metrics data in the range [0, 1]. Values outside the valid range
* are ignored.
*/
public void setMemoryUtilizationMetric(double value) {
if (!MetricRecorderHelper.isUtilizationValid(value)) {
return;
}
memoryUtilization = value;
}
@ -88,9 +100,12 @@ public final class MetricRecorder {
}
/**
* Update the QPS metrics data.
* Update the QPS metrics data in the range [0, inf). Values outside the valid range are ignored.
*/
public void setQps(double value) {
if (!MetricRecorderHelper.isQpsValid(value)) {
return;
}
qps = value;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2023 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.services;
/**
* Utility helper class to check whether values for {@link CallMetricRecorder} and
* {@link MetricRecorder} are inside the valid range.
*/
final class MetricRecorderHelper {
/**
* Return true if the utilization value is in the range [0, 1] and false otherwise.
*/
static boolean isUtilizationValid(double utilization) {
return utilization >= 0.0 && utilization <= 1.0;
}
/**
* Return true if the qps value is in the range [0, inf) and false otherwise.
*/
static boolean isQpsValid(double qps) {
return qps >= 0.0;
}
// Prevent instantiation.
private MetricRecorderHelper() {
}
}

View File

@ -38,33 +38,62 @@ public class CallMetricRecorderTest {
@Test
public void dumpDumpsAllSavedMetricValues() {
recorder.recordUtilizationMetric("util1", 154353.423);
recorder.recordUtilizationMetric("util1", 0.154353423);
recorder.recordUtilizationMetric("util2", 0.1367);
recorder.recordUtilizationMetric("util3", 1437.34);
recorder.recordUtilizationMetric("util3", 0.143734);
recorder.recordRequestCostMetric("cost1", 37465.12);
recorder.recordRequestCostMetric("cost2", 10293.0);
recorder.recordRequestCostMetric("cost3", 1.0);
recorder.recordCpuUtilizationMetric(0.1928);
recorder.recordMemoryUtilizationMetric(47.4);
recorder.recordMemoryUtilizationMetric(0.474);
recorder.recordQpsMetric(2522.54);
MetricReport dump = recorder.finalizeAndDump2();
Truth.assertThat(dump.getUtilizationMetrics())
.containsExactly("util1", 154353.423, "util2", 0.1367, "util3", 1437.34);
.containsExactly("util1", 0.154353423, "util2", 0.1367, "util3", 0.143734);
Truth.assertThat(dump.getRequestCostMetrics())
.containsExactly("cost1", 37465.12, "cost2", 10293.0, "cost3", 1.0);
Truth.assertThat(dump.getCpuUtilization()).isEqualTo(0.1928);
Truth.assertThat(dump.getMemoryUtilization()).isEqualTo(47.4);
Truth.assertThat(dump.getMemoryUtilization()).isEqualTo(0.474);
Truth.assertThat(dump.getQps()).isEqualTo(2522.54);
}
@Test
public void noMetricsRecordedAfterSnapshot() {
Map<String, Double> initDump = recorder.finalizeAndDump();
recorder.recordUtilizationMetric("cost", 154353.423);
recorder.recordUtilizationMetric("cost", 0.154353423);
assertThat(recorder.finalizeAndDump()).isEqualTo(initDump);
}
@Test
public void noMetricsRecordedIfUtilizationIsGreaterThanUpperBound() {
recorder.recordCpuUtilizationMetric(1.001);
recorder.recordMemoryUtilizationMetric(1.001);
recorder.recordUtilizationMetric("util1", 1.001);
MetricReport dump = recorder.finalizeAndDump2();
Truth.assertThat(dump.getCpuUtilization()).isEqualTo(0);
Truth.assertThat(dump.getMemoryUtilization()).isEqualTo(0);
Truth.assertThat(dump.getQps()).isEqualTo(0);
Truth.assertThat(dump.getUtilizationMetrics()).isEmpty();
Truth.assertThat(dump.getRequestCostMetrics()).isEmpty();
}
@Test
public void noMetricsRecordedIfUtilizationAndQpsAreLessThanLowerBound() {
recorder.recordCpuUtilizationMetric(-0.001);
recorder.recordMemoryUtilizationMetric(-0.001);
recorder.recordQpsMetric(-0.001);
recorder.recordUtilizationMetric("util1", -0.001);
MetricReport dump = recorder.finalizeAndDump2();
Truth.assertThat(dump.getCpuUtilization()).isEqualTo(0);
Truth.assertThat(dump.getMemoryUtilization()).isEqualTo(0);
Truth.assertThat(dump.getQps()).isEqualTo(0);
Truth.assertThat(dump.getUtilizationMetrics()).isEmpty();
Truth.assertThat(dump.getRequestCostMetrics()).isEmpty();
}
@Test
public void lastValueWinForMetricsWithSameName() {
recorder.recordRequestCostMetric("cost1", 3412.5435);
@ -72,20 +101,20 @@ public class CallMetricRecorderTest {
recorder.recordRequestCostMetric("cost1", 6441.341);
recorder.recordRequestCostMetric("cost1", 4654.67);
recorder.recordRequestCostMetric("cost2", 75.83);
recorder.recordMemoryUtilizationMetric(1.3);
recorder.recordMemoryUtilizationMetric(3.1);
recorder.recordUtilizationMetric("util1", 28374.21);
recorder.recordMemoryUtilizationMetric(9384.0);
recorder.recordUtilizationMetric("util1", 84323.3);
recorder.recordMemoryUtilizationMetric(0.13);
recorder.recordMemoryUtilizationMetric(0.31);
recorder.recordUtilizationMetric("util1", 0.2837421);
recorder.recordMemoryUtilizationMetric(0.93840);
recorder.recordUtilizationMetric("util1", 0.843233);
recorder.recordQpsMetric(1928.3);
recorder.recordQpsMetric(100.8);
MetricReport dump = recorder.finalizeAndDump2();
Truth.assertThat(dump.getRequestCostMetrics())
.containsExactly("cost1", 4654.67, "cost2", 75.83);
Truth.assertThat(dump.getMemoryUtilization()).isEqualTo(9384.0);
Truth.assertThat(dump.getMemoryUtilization()).isEqualTo(0.93840);
Truth.assertThat(dump.getUtilizationMetrics())
.containsExactly("util1", 84323.3);
.containsExactly("util1", 0.843233);
Truth.assertThat(dump.getCpuUtilization()).isEqualTo(0);
Truth.assertThat(dump.getQps()).isEqualTo(100.8);
}

View File

@ -31,7 +31,10 @@ import io.grpc.Status;
import io.grpc.protobuf.ProtoUtils;
import io.grpc.services.CallMetricRecorder;
import io.grpc.services.InternalCallMetricRecorder;
import io.grpc.services.InternalMetricRecorder;
import io.grpc.services.MetricRecorder;
import io.grpc.services.MetricReport;
import javax.annotation.Nullable;
/**
* A {@link ServerInterceptor} that intercepts a {@link ServerCall} by running server-side RPC
@ -45,7 +48,7 @@ import io.grpc.services.MetricReport;
public final class OrcaMetricReportingServerInterceptor implements ServerInterceptor {
private static final OrcaMetricReportingServerInterceptor INSTANCE =
new OrcaMetricReportingServerInterceptor();
new OrcaMetricReportingServerInterceptor(null);
@VisibleForTesting
static final Metadata.Key<OrcaLoadReport> ORCA_ENDPOINT_LOAD_METRICS_KEY =
@ -53,14 +56,29 @@ public final class OrcaMetricReportingServerInterceptor implements ServerInterce
"endpoint-load-metrics-bin",
ProtoUtils.metadataMarshaller(OrcaLoadReport.getDefaultInstance()));
@Nullable
private final MetricRecorder metricRecorder;
@VisibleForTesting
OrcaMetricReportingServerInterceptor() {
OrcaMetricReportingServerInterceptor(@Nullable MetricRecorder metricRecorder) {
this.metricRecorder = metricRecorder;
}
public static OrcaMetricReportingServerInterceptor getInstance() {
return INSTANCE;
}
/**
* Creates a new {@link OrcaMetricReportingServerInterceptor} instance with the given
* {@link MetricRecorder}. When both {@link CallMetricRecorder} and {@link MetricRecorder} exist,
* the metrics are merged such that per-request metrics from {@link CallMetricRecorder} takes a
* higher precedence compared to metrics from {@link MetricRecorder}.
*/
public static OrcaMetricReportingServerInterceptor create(
@Nullable MetricRecorder metricRecorder) {
return new OrcaMetricReportingServerInterceptor(metricRecorder);
}
@Override
public <ReqT, RespT> Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
@ -75,8 +93,12 @@ public final class OrcaMetricReportingServerInterceptor implements ServerInterce
new SimpleForwardingServerCall<ReqT, RespT>(call) {
@Override
public void close(Status status, Metadata trailers) {
OrcaLoadReport report = fromInternalReport(
OrcaLoadReport.Builder reportBuilder = metricRecorder != null ? fromInternalReport(
InternalMetricRecorder.getMetricReport(metricRecorder))
: OrcaLoadReport.newBuilder();
mergeMetrics(reportBuilder,
InternalCallMetricRecorder.finalizeAndDump2(finalCallMetricRecorder));
OrcaLoadReport report = reportBuilder.build();
if (!report.equals(OrcaLoadReport.getDefaultInstance())) {
trailers.put(ORCA_ENDPOINT_LOAD_METRICS_KEY, report);
}
@ -90,12 +112,42 @@ public final class OrcaMetricReportingServerInterceptor implements ServerInterce
next);
}
private static OrcaLoadReport fromInternalReport(MetricReport internalReport) {
private static OrcaLoadReport.Builder fromInternalReport(MetricReport internalReport) {
return OrcaLoadReport.newBuilder()
.setCpuUtilization(internalReport.getCpuUtilization())
.setMemUtilization(internalReport.getMemoryUtilization())
.setRpsFractional(internalReport.getQps())
.putAllUtilization(internalReport.getUtilizationMetrics())
.putAllRequestCost(internalReport.getRequestCostMetrics())
.build();
.putAllRequestCost(internalReport.getRequestCostMetrics());
}
/**
* Modify the given {@link OrcaLoadReport.Builder} containing metrics for {@link MetricRecorder}
* such that metrics from the given {@link MetricReport} for {@link CallMetricRecorder} takes a
* higher precedence.
*/
private static void mergeMetrics(
OrcaLoadReport.Builder metricRecorderReportBuilder,
MetricReport callMetricRecorderReport
) {
metricRecorderReportBuilder.putAllUtilization(callMetricRecorderReport.getUtilizationMetrics())
.putAllRequestCost(callMetricRecorderReport.getRequestCostMetrics());
// Overwrite only if the values from the given MetricReport for CallMetricRecorder are set
double cpu = callMetricRecorderReport.getCpuUtilization();
if (isReportValueSet(cpu)) {
metricRecorderReportBuilder.setCpuUtilization(cpu);
}
double mem = callMetricRecorderReport.getMemoryUtilization();
if (isReportValueSet(mem)) {
metricRecorderReportBuilder.setMemUtilization(mem);
}
double rps = callMetricRecorderReport.getQps();
if (isReportValueSet(rps)) {
metricRecorderReportBuilder.setRpsFractional(rps);
}
}
private static boolean isReportValueSet(double value) {
return value != 0;
}
}

View File

@ -38,6 +38,7 @@ import io.grpc.inprocess.InProcessChannelBuilder;
import io.grpc.inprocess.InProcessServerBuilder;
import io.grpc.services.CallMetricRecorder;
import io.grpc.services.InternalCallMetricRecorder;
import io.grpc.services.MetricRecorder;
import io.grpc.stub.ClientCalls;
import io.grpc.stub.StreamObserver;
import io.grpc.testing.GrpcCleanupRule;
@ -73,6 +74,8 @@ public class OrcaMetricReportingServerInterceptorTest {
private final Map<String, Double> applicationCostMetrics = new HashMap<>();
private double cpuUtilizationMetrics = 0;
private double memoryUtilizationMetrics = 0;
private double qpsMetrics = 0;
private MetricRecorder metricRecorder;
private final AtomicReference<Metadata> trailersCapture = new AtomicReference<>();
@ -95,6 +98,7 @@ public class OrcaMetricReportingServerInterceptorTest {
}
CallMetricRecorder.getCurrent().recordCpuUtilizationMetric(cpuUtilizationMetrics);
CallMetricRecorder.getCurrent().recordMemoryUtilizationMetric(memoryUtilizationMetrics);
CallMetricRecorder.getCurrent().recordQpsMetric(qpsMetrics);
SimpleResponse response =
SimpleResponse.newBuilder().setResponseMessage("Simple response").build();
responseObserver.onNext(response);
@ -102,7 +106,9 @@ public class OrcaMetricReportingServerInterceptorTest {
}
};
ServerInterceptor metricReportingServerInterceptor = new OrcaMetricReportingServerInterceptor();
metricRecorder = MetricRecorder.newInstance();
ServerInterceptor metricReportingServerInterceptor = new OrcaMetricReportingServerInterceptor(
metricRecorder);
String serverName = InProcessServerBuilder.generateName();
grpcCleanupRule.register(
InProcessServerBuilder
@ -149,7 +155,8 @@ public class OrcaMetricReportingServerInterceptorTest {
}
};
ServerInterceptor metricReportingServerInterceptor = new OrcaMetricReportingServerInterceptor();
ServerInterceptor metricReportingServerInterceptor =
OrcaMetricReportingServerInterceptor.getInstance();
String serverName = InProcessServerBuilder.generateName();
grpcCleanupRule.register(
InProcessServerBuilder
@ -177,7 +184,7 @@ public class OrcaMetricReportingServerInterceptorTest {
}
@Test
public void responseTrailersContainAllReportedMetrics() {
public void responseTrailersContainAllReportedMetricsFromCallMetricRecorder() {
applicationCostMetrics.put("cost1", 1231.4543);
applicationCostMetrics.put("cost2", 0.1367);
applicationCostMetrics.put("cost3", 7614.145);
@ -186,6 +193,7 @@ public class OrcaMetricReportingServerInterceptorTest {
applicationUtilizationMetrics.put("util3", 0.5342);
cpuUtilizationMetrics = 0.3465;
memoryUtilizationMetrics = 0.764;
qpsMetrics = 3.1415926535;
ClientCalls.blockingUnaryCall(channelToUse, SIMPLE_METHOD, CallOptions.DEFAULT, REQUEST);
Metadata receivedTrailers = trailersCapture.get();
OrcaLoadReport report =
@ -196,6 +204,54 @@ public class OrcaMetricReportingServerInterceptorTest {
.containsExactly("cost1", 1231.4543, "cost2", 0.1367, "cost3", 7614.145);
assertThat(report.getCpuUtilization()).isEqualTo(0.3465);
assertThat(report.getMemUtilization()).isEqualTo(0.764);
assertThat(report.getRpsFractional()).isEqualTo(3.1415926535);
}
@Test
public void responseTrailersContainMergedMetricsFromCallMetricRecorderAndMetricRecorder() {
applicationUtilizationMetrics.put("util1", 0.1482);
applicationUtilizationMetrics.put("util2", 0.4036);
applicationUtilizationMetrics.put("util3", 0.5742);
cpuUtilizationMetrics = 0.3465;
memoryUtilizationMetrics = 0.967;
metricRecorder.setMemoryUtilizationMetric(0.764);
metricRecorder.setQps(1.618);
metricRecorder.putUtilizationMetric("serverUtil1", 0.7467);
metricRecorder.putUtilizationMetric("serverUtil2", 0.2233);
metricRecorder.putUtilizationMetric("util1", 0.01);
metricRecorder.putUtilizationMetric("util3", 0.99);
ClientCalls.blockingUnaryCall(channelToUse, SIMPLE_METHOD, CallOptions.DEFAULT, REQUEST);
Metadata receivedTrailers = trailersCapture.get();
OrcaLoadReport report =
receivedTrailers.get(OrcaMetricReportingServerInterceptor.ORCA_ENDPOINT_LOAD_METRICS_KEY);
assertThat(report.getUtilizationMap())
.containsExactly("util1", 0.1482, "util2", 0.4036, "util3", 0.5742, "serverUtil1", 0.7467,
"serverUtil2", 0.2233);
assertThat(report.getRequestCostMap()).isEmpty();
assertThat(report.getCpuUtilization()).isEqualTo(0.3465);
assertThat(report.getMemUtilization()).isEqualTo(0.967);
assertThat(report.getRpsFractional()).isEqualTo(1.618);
}
@Test
public void responseTrailersContainMergedMetricsFromCallMetricRecorderAndMetricRecorderNoMap() {
qpsMetrics = 5142.77;
metricRecorder.setCpuUtilizationMetric(0.314159);
metricRecorder.setMemoryUtilizationMetric(0.764);
metricRecorder.setQps(1.618);
ClientCalls.blockingUnaryCall(channelToUse, SIMPLE_METHOD, CallOptions.DEFAULT, REQUEST);
Metadata receivedTrailers = trailersCapture.get();
OrcaLoadReport report =
receivedTrailers.get(OrcaMetricReportingServerInterceptor.ORCA_ENDPOINT_LOAD_METRICS_KEY);
assertThat(report.getUtilizationMap()).isEmpty();
assertThat(report.getRequestCostMap()).isEmpty();
assertThat(report.getCpuUtilization()).isEqualTo(0.314159);
assertThat(report.getMemUtilization()).isEqualTo(0.764);
assertThat(report.getRpsFractional()).isEqualTo(5142.77);
}
private static final class TrailersCapturingClientInterceptor implements ClientInterceptor {

View File

@ -150,7 +150,7 @@ public class OrcaServiceImplTest {
call.halfClose();
call.request(1);
OrcaLoadReport expect = OrcaLoadReport.newBuilder().putUtilization("buffer", 0.2)
.setRpsFractional(1.9).build();
.setRpsFractional(1.9).build();
verify(listener).onMessage(eq(expect));
reset(listener);
defaultTestService.removeUtilizationMetric("buffer0");
@ -215,12 +215,12 @@ public class OrcaServiceImplTest {
public void testMultipleClients() {
ClientCall<OrcaLoadReportRequest, OrcaLoadReport> call = channel.newCall(
OpenRcaServiceGrpc.getStreamCoreMetricsMethod(), CallOptions.DEFAULT);
defaultTestService.putUtilizationMetric("omg", 100);
defaultTestService.putUtilizationMetric("omg", 1.00);
call.start(listener, new Metadata());
call.sendMessage(OrcaLoadReportRequest.newBuilder().build());
call.halfClose();
call.request(1);
OrcaLoadReport expect = OrcaLoadReport.newBuilder().putUtilization("omg", 100).build();
OrcaLoadReport expect = OrcaLoadReport.newBuilder().putUtilization("omg", 1.00).build();
verify(listener).onMessage(eq(expect));
defaultTestService.setMemoryUtilizationMetric(0.5);
ClientCall<OrcaLoadReportRequest, OrcaLoadReport> call2 = channel.newCall(
@ -275,6 +275,16 @@ public class OrcaServiceImplTest {
fakeClock.forwardTime(1, TimeUnit.SECONDS);
assertThat(reports.next()).isEqualTo(goldenReport);
defaultTestService.setCpuUtilizationMetric(-0.001);
defaultTestService.setCpuUtilizationMetric(1.001);
defaultTestService.setMemoryUtilizationMetric(-0.001);
defaultTestService.setMemoryUtilizationMetric(1.001);
defaultTestService.setQps(-0.001);
defaultTestService.putUtilizationMetric("util-out-of-range", -0.001);
defaultTestService.putUtilizationMetric("util-out-of-range", 1.001);
fakeClock.forwardTime(1, TimeUnit.SECONDS);
assertThat(reports.next()).isEqualTo(goldenReport);
CyclicBarrier barrier = new CyclicBarrier(2);
new Thread(new Runnable() {
@Override