Skip to content

Commit 54ac06a

Browse files
committed
rls: Add metric test with real channel
1 parent 6bede04 commit 54ac06a

File tree

3 files changed

+133
-28
lines changed

3 files changed

+133
-28
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2024 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
import java.util.Collections;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.Set;
23+
24+
/**
25+
* A MetricSink that discards all records.
26+
*/
27+
public class NoopMetricSink implements MetricSink {
28+
private int size;
29+
30+
@Override
31+
public Map<String, Boolean> getEnabledMetrics() {
32+
return Collections.emptyMap();
33+
}
34+
35+
@Override
36+
public Set<String> getOptionalLabels() {
37+
return Collections.emptySet();
38+
}
39+
40+
@Override
41+
public synchronized int getMeasuresSize() {
42+
return size;
43+
}
44+
45+
@Override
46+
public synchronized void updateMeasures(List<MetricInstrument> instruments) {
47+
size = instruments.size();
48+
}
49+
}

rls/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ dependencies {
2828
project(':grpc-inprocess'),
2929
project(':grpc-testing'),
3030
project(':grpc-testing-proto'),
31+
testFixtures(project(':grpc-api')),
3132
testFixtures(project(':grpc-core'))
3233
signature libraries.signature.java
3334
}

rls/src/test/java/io/grpc/rls/RlsLoadBalancerTest.java

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import static com.google.common.base.Preconditions.checkNotNull;
2020
import static com.google.common.truth.Truth.assertThat;
21+
import static org.mockito.AdditionalAnswers.delegatesTo;
2122
import static org.mockito.ArgumentMatchers.any;
2223
import static org.mockito.ArgumentMatchers.anyString;
2324
import static org.mockito.ArgumentMatchers.argThat;
@@ -50,15 +51,18 @@
5051
import io.grpc.LoadBalancer.Subchannel;
5152
import io.grpc.LoadBalancer.SubchannelPicker;
5253
import io.grpc.LoadBalancer.SubchannelStateListener;
53-
import io.grpc.LongCounterMetricInstrument;
5454
import io.grpc.ManagedChannel;
5555
import io.grpc.ManagedChannelBuilder;
5656
import io.grpc.Metadata;
5757
import io.grpc.MethodDescriptor;
58-
import io.grpc.MethodDescriptor.Marshaller;
5958
import io.grpc.MethodDescriptor.MethodType;
59+
import io.grpc.MetricInstrument;
6060
import io.grpc.MetricRecorder;
61+
import io.grpc.MetricSink;
6162
import io.grpc.NameResolver.ConfigOrError;
63+
import io.grpc.NoopMetricSink;
64+
import io.grpc.ServerCall;
65+
import io.grpc.ServerServiceDefinition;
6266
import io.grpc.Status;
6367
import io.grpc.Status.Code;
6468
import io.grpc.SynchronizationContext;
@@ -67,16 +71,20 @@
6771
import io.grpc.internal.FakeClock;
6872
import io.grpc.internal.JsonParser;
6973
import io.grpc.internal.PickSubchannelArgsImpl;
74+
import io.grpc.internal.testing.StreamRecorder;
7075
import io.grpc.lookup.v1.RouteLookupServiceGrpc;
7176
import io.grpc.rls.RlsLoadBalancer.CachingRlsLbClientBuilderProvider;
7277
import io.grpc.rls.RlsProtoConverters.RouteLookupResponseConverter;
7378
import io.grpc.rls.RlsProtoData.RouteLookupRequest;
7479
import io.grpc.rls.RlsProtoData.RouteLookupResponse;
80+
import io.grpc.stub.ClientCalls;
7581
import io.grpc.stub.StreamObserver;
7682
import io.grpc.testing.GrpcCleanupRule;
83+
import io.grpc.testing.TestMethodDescriptors;
7784
import java.io.IOException;
7885
import java.lang.Thread.UncaughtExceptionHandler;
7986
import java.net.SocketAddress;
87+
import java.util.Arrays;
8088
import java.util.Collections;
8189
import java.util.Deque;
8290
import java.util.LinkedList;
@@ -125,12 +133,10 @@ public void uncaughtException(Thread t, Throwable e) {
125133
private final Deque<FakeSubchannel> subchannels = new LinkedList<>();
126134
private final FakeThrottler fakeThrottler = new FakeThrottler();
127135
private final String channelTarget = "channelTarget";
128-
@Mock
129-
private Marshaller<Object> mockMarshaller;
130136
@Captor
131137
private ArgumentCaptor<SubchannelPicker> pickerCaptor;
132-
private MethodDescriptor<Object, Object> fakeSearchMethod;
133-
private MethodDescriptor<Object, Object> fakeRescueMethod;
138+
private MethodDescriptor<Void, Void> fakeSearchMethod;
139+
private MethodDescriptor<Void, Void> fakeRescueMethod;
134140
private RlsLoadBalancer rlsLb;
135141
private String defaultTarget = "defaultTarget";
136142
private PickSubchannelArgs searchSubchannelArgs;
@@ -139,17 +145,17 @@ public void uncaughtException(Thread t, Throwable e) {
139145
@Before
140146
public void setUp() {
141147
fakeSearchMethod =
142-
MethodDescriptor.newBuilder()
148+
MethodDescriptor.<Void, Void>newBuilder()
143149
.setFullMethodName("com.google/Search")
144-
.setRequestMarshaller(mockMarshaller)
145-
.setResponseMarshaller(mockMarshaller)
150+
.setRequestMarshaller(TestMethodDescriptors.voidMarshaller())
151+
.setResponseMarshaller(TestMethodDescriptors.voidMarshaller())
146152
.setType(MethodType.CLIENT_STREAMING)
147153
.build();
148154
fakeRescueMethod =
149-
MethodDescriptor.newBuilder()
155+
MethodDescriptor.<Void, Void>newBuilder()
150156
.setFullMethodName("com.google/Rescue")
151-
.setRequestMarshaller(mockMarshaller)
152-
.setResponseMarshaller(mockMarshaller)
157+
.setRequestMarshaller(TestMethodDescriptors.voidMarshaller())
158+
.setResponseMarshaller(TestMethodDescriptors.voidMarshaller())
153159
.setType(MethodType.UNARY)
154160
.build();
155161
fakeRlsServerImpl.setLookupTable(
@@ -282,6 +288,44 @@ public void lb_working_withDefaultTarget_rlsResponding() throws Exception {
282288
verifyNoMoreInteractions(mockMetricRecorder);
283289
}
284290

291+
@Test
292+
public void metricsWithRealChannel() throws Exception {
293+
grpcCleanupRule.register(
294+
InProcessServerBuilder.forName("fake-bigtable.googleapis.com")
295+
.addService(ServerServiceDefinition.builder("com.google")
296+
.addMethod(fakeSearchMethod, (call, headers) -> {
297+
call.sendHeaders(new Metadata());
298+
call.sendMessage(null);
299+
call.close(Status.OK, new Metadata());
300+
return new ServerCall.Listener<Void>() {};
301+
})
302+
.build())
303+
.directExecutor()
304+
.build()
305+
.start());
306+
MetricSink metrics = mock(MetricSink.class, delegatesTo(new NoopMetricSink()));
307+
ManagedChannel channel = grpcCleanupRule.register(
308+
InProcessChannelBuilder.forName("fake-bigtable.googleapis.com")
309+
.defaultServiceConfig(parseJson(getServiceConfigJsonStr()))
310+
.addMetricSink(metrics)
311+
.directExecutor()
312+
.build());
313+
314+
StreamRecorder<Void> recorder = StreamRecorder.create();
315+
StreamObserver<Void> requestObserver = ClientCalls.asyncClientStreamingCall(
316+
channel.newCall(fakeSearchMethod, CallOptions.DEFAULT), recorder);
317+
requestObserver.onCompleted();
318+
assertThat(recorder.awaitCompletion(10, TimeUnit.SECONDS)).isTrue();
319+
assertThat(recorder.getError()).isNull();
320+
321+
verify(metrics).addLongCounter(
322+
eqMetricInstrumentName("grpc.lb.rls.default_target_picks"),
323+
eq(1L),
324+
eq(Arrays.asList("directaddress:///fake-bigtable.googleapis.com", "localhost:8972",
325+
"defaultTarget", "complete")),
326+
eq(Arrays.asList()));
327+
}
328+
285329
@Test
286330
public void lb_working_withoutDefaultTarget_noRlsResponse() throws Exception {
287331
defaultTarget = "";
@@ -498,7 +542,7 @@ private PickResult markReadyAndGetPickResult(InOrder inOrder,
498542

499543
private void deliverResolvedAddresses() throws Exception {
500544
ConfigOrError parsedConfigOrError =
501-
provider.parseLoadBalancingPolicyConfig(getServiceConfig());
545+
provider.parseLoadBalancingPolicyConfig(parseJson(getLbConfigJsonStr()));
502546
assertThat(parsedConfigOrError.getConfig()).isNotNull();
503547
rlsLb.acceptResolvedAddresses(ResolvedAddresses.newBuilder()
504548
.setAddresses(ImmutableList.of(new EquivalentAddressGroup(mock(SocketAddress.class))))
@@ -508,13 +552,24 @@ private void deliverResolvedAddresses() throws Exception {
508552
}
509553

510554
@SuppressWarnings("unchecked")
511-
private Map<String, Object> getServiceConfig() throws IOException {
512-
String serviceConfig = "{"
555+
private Map<String, Object> parseJson(String json) throws IOException {
556+
return (Map<String, Object>) JsonParser.parse(json);
557+
}
558+
559+
private String getServiceConfigJsonStr() {
560+
return "{"
561+
+ " \"loadBalancingConfig\": [{"
562+
+ " \"rls_experimental\": " + getLbConfigJsonStr()
563+
+ " }]"
564+
+ "}";
565+
}
566+
567+
private String getLbConfigJsonStr() {
568+
return "{"
513569
+ " \"routeLookupConfig\": " + getRlsConfigJsonStr() + ", "
514570
+ " \"childPolicy\": [{\"pick_first\": {}}],"
515571
+ " \"childPolicyConfigTargetFieldName\": \"serviceName\""
516572
+ "}";
517-
return (Map<String, Object>) JsonParser.parse(serviceConfig);
518573
}
519574

520575
private String getRlsConfigJsonStr() {
@@ -558,12 +613,7 @@ private void verifyLongCounterAdd(String name, int times, long value,
558613
String dataPlaneTargetLabel, String pickResult) {
559614
// TODO: support the "grpc.target" label once available.
560615
verify(mockMetricRecorder, times(times)).addLongCounter(
561-
argThat(new ArgumentMatcher<LongCounterMetricInstrument>() {
562-
@Override
563-
public boolean matches(LongCounterMetricInstrument longCounterInstrument) {
564-
return longCounterInstrument.getName().equals(name);
565-
}
566-
}), eq(value),
616+
eqMetricInstrumentName(name), eq(value),
567617
eq(Lists.newArrayList(channelTarget, "localhost:8972", dataPlaneTargetLabel, pickResult)),
568618
eq(Lists.newArrayList()));
569619
}
@@ -572,16 +622,21 @@ public boolean matches(LongCounterMetricInstrument longCounterInstrument) {
572622
private void verifyFailedPicksCounterAdd(int times, long value) {
573623
// TODO: support the "grpc.target" label once available.
574624
verify(mockMetricRecorder, times(times)).addLongCounter(
575-
argThat(new ArgumentMatcher<LongCounterMetricInstrument>() {
576-
@Override
577-
public boolean matches(LongCounterMetricInstrument longCounterInstrument) {
578-
return longCounterInstrument.getName().equals("grpc.lb.rls.failed_picks");
579-
}
580-
}), eq(value),
625+
eqMetricInstrumentName("grpc.lb.rls.failed_picks"), eq(value),
581626
eq(Lists.newArrayList(channelTarget, "localhost:8972")),
582627
eq(Lists.newArrayList()));
583628
}
584629

630+
@SuppressWarnings("TypeParameterUnusedInFormals")
631+
private <T extends MetricInstrument> T eqMetricInstrumentName(String name) {
632+
return argThat(new ArgumentMatcher<T>() {
633+
@Override
634+
public boolean matches(T instrument) {
635+
return instrument.getName().equals(name);
636+
}
637+
});
638+
}
639+
585640
private PickSubchannelArgs newPickSubchannelArgs(MethodDescriptor<?, ?> method) {
586641
return new PickSubchannelArgsImpl(
587642
method, new Metadata(), CallOptions.DEFAULT, new PickDetailsConsumer() {});

0 commit comments

Comments
 (0)