Skip to content

Commit 03b453b

Browse files
Merge branch 'grpc:master' into Issue_fixed_12142
2 parents c414609 + d2d8ed8 commit 03b453b

File tree

15 files changed

+551
-275
lines changed

15 files changed

+551
-275
lines changed

api/src/main/java/io/grpc/NameResolver.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ public static final class Args {
303303
@Nullable private final Executor executor;
304304
@Nullable private final String overrideAuthority;
305305
@Nullable private final MetricRecorder metricRecorder;
306+
@Nullable private final NameResolverRegistry nameResolverRegistry;
306307
@Nullable private final IdentityHashMap<Key<?>, Object> customArgs;
307308

308309
private Args(Builder builder) {
@@ -316,6 +317,7 @@ private Args(Builder builder) {
316317
this.executor = builder.executor;
317318
this.overrideAuthority = builder.overrideAuthority;
318319
this.metricRecorder = builder.metricRecorder;
320+
this.nameResolverRegistry = builder.nameResolverRegistry;
319321
this.customArgs = cloneCustomArgs(builder.customArgs);
320322
}
321323

@@ -447,6 +449,18 @@ public MetricRecorder getMetricRecorder() {
447449
return metricRecorder;
448450
}
449451

452+
/**
453+
* Returns the {@link NameResolverRegistry} that the Channel uses to look for {@link
454+
* NameResolver}s.
455+
*
456+
* @since 1.74.0
457+
*/
458+
public NameResolverRegistry getNameResolverRegistry() {
459+
if (nameResolverRegistry == null) {
460+
throw new IllegalStateException("NameResolverRegistry is not set in Builder");
461+
}
462+
return nameResolverRegistry;
463+
}
450464

451465
@Override
452466
public String toString() {
@@ -461,6 +475,7 @@ public String toString() {
461475
.add("executor", executor)
462476
.add("overrideAuthority", overrideAuthority)
463477
.add("metricRecorder", metricRecorder)
478+
.add("nameResolverRegistry", nameResolverRegistry)
464479
.toString();
465480
}
466481

@@ -480,6 +495,7 @@ public Builder toBuilder() {
480495
builder.setOffloadExecutor(executor);
481496
builder.setOverrideAuthority(overrideAuthority);
482497
builder.setMetricRecorder(metricRecorder);
498+
builder.setNameResolverRegistry(nameResolverRegistry);
483499
builder.customArgs = cloneCustomArgs(customArgs);
484500
return builder;
485501
}
@@ -508,6 +524,7 @@ public static final class Builder {
508524
private Executor executor;
509525
private String overrideAuthority;
510526
private MetricRecorder metricRecorder;
527+
private NameResolverRegistry nameResolverRegistry;
511528
private IdentityHashMap<Key<?>, Object> customArgs;
512529

513530
Builder() {
@@ -614,6 +631,16 @@ public Builder setMetricRecorder(MetricRecorder metricRecorder) {
614631
return this;
615632
}
616633

634+
/**
635+
* See {@link Args#getNameResolverRegistry}. This is an optional field.
636+
*
637+
* @since 1.74.0
638+
*/
639+
public Builder setNameResolverRegistry(NameResolverRegistry registry) {
640+
this.nameResolverRegistry = registry;
641+
return this;
642+
}
643+
617644
/**
618645
* Builds an {@link Args}.
619646
*

core/src/main/java/io/grpc/internal/ManagedChannelImpl.java

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@
9494
import io.grpc.internal.ManagedChannelServiceConfig.ServiceConfigConvertedSelector;
9595
import io.grpc.internal.RetriableStream.ChannelBufferMeter;
9696
import io.grpc.internal.RetriableStream.Throttle;
97-
import io.grpc.internal.RetryingNameResolver.ResolutionResultListener;
9897
import java.net.URI;
9998
import java.util.ArrayList;
10099
import java.util.Collection;
@@ -598,7 +597,8 @@ ClientStream newSubstream(
598597
.setChannelLogger(channelLogger)
599598
.setOffloadExecutor(this.offloadExecutorHolder)
600599
.setOverrideAuthority(this.authorityOverride)
601-
.setMetricRecorder(this.metricRecorder);
600+
.setMetricRecorder(this.metricRecorder)
601+
.setNameResolverRegistry(builder.nameResolverRegistry);
602602
builder.copyAllNameResolverCustomArgsTo(nameResolverArgsBuilder);
603603
this.nameResolverArgs = nameResolverArgsBuilder.build();
604604
this.nameResolver = getNameResolver(
@@ -686,11 +686,7 @@ static NameResolver getNameResolver(
686686
// We wrap the name resolver in a RetryingNameResolver to give it the ability to retry failures.
687687
// TODO: After a transition period, all NameResolver implementations that need retry should use
688688
// RetryingNameResolver directly and this step can be removed.
689-
NameResolver usedNameResolver = new RetryingNameResolver(resolver,
690-
new BackoffPolicyRetryScheduler(new ExponentialBackoffPolicy.Provider(),
691-
nameResolverArgs.getScheduledExecutorService(),
692-
nameResolverArgs.getSynchronizationContext()),
693-
nameResolverArgs.getSynchronizationContext());
689+
NameResolver usedNameResolver = RetryingNameResolver.wrap(resolver, nameResolverArgs);
694690

695691
if (overrideAuthority == null) {
696692
return usedNameResolver;
@@ -1653,18 +1649,7 @@ final class NameResolverListener extends NameResolver.Listener2 {
16531649

16541650
@Override
16551651
public void onResult(final ResolutionResult resolutionResult) {
1656-
final class NamesResolved implements Runnable {
1657-
1658-
@Override
1659-
public void run() {
1660-
Status status = onResult2(resolutionResult);
1661-
ResolutionResultListener resolutionResultListener = resolutionResult.getAttributes()
1662-
.get(RetryingNameResolver.RESOLUTION_RESULT_LISTENER_KEY);
1663-
resolutionResultListener.resolutionAttempted(status);
1664-
}
1665-
}
1666-
1667-
syncContext.execute(new NamesResolved());
1652+
syncContext.execute(() -> onResult2(resolutionResult));
16681653
}
16691654

16701655
@SuppressWarnings("ReferenceEquality")

core/src/main/java/io/grpc/internal/RetryingNameResolver.java

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package io.grpc.internal;
1818

1919
import com.google.common.annotations.VisibleForTesting;
20-
import io.grpc.Attributes;
2120
import io.grpc.NameResolver;
2221
import io.grpc.Status;
2322
import io.grpc.SynchronizationContext;
@@ -28,16 +27,22 @@
2827
*
2928
* <p>The {@link NameResolver} used with this
3029
*/
31-
final class RetryingNameResolver extends ForwardingNameResolver {
30+
public final class RetryingNameResolver extends ForwardingNameResolver {
31+
public static NameResolver wrap(NameResolver retriedNameResolver, Args args) {
32+
// For migration, this might become conditional
33+
return new RetryingNameResolver(
34+
retriedNameResolver,
35+
new BackoffPolicyRetryScheduler(
36+
new ExponentialBackoffPolicy.Provider(),
37+
args.getScheduledExecutorService(),
38+
args.getSynchronizationContext()),
39+
args.getSynchronizationContext());
40+
}
3241

3342
private final NameResolver retriedNameResolver;
3443
private final RetryScheduler retryScheduler;
3544
private final SynchronizationContext syncContext;
3645

37-
static final Attributes.Key<ResolutionResultListener> RESOLUTION_RESULT_LISTENER_KEY
38-
= Attributes.Key.create(
39-
"io.grpc.internal.RetryingNameResolver.RESOLUTION_RESULT_LISTENER_KEY");
40-
4146
/**
4247
* Creates a new {@link RetryingNameResolver}.
4348
*
@@ -88,18 +93,7 @@ private class RetryingListener extends Listener2 {
8893

8994
@Override
9095
public void onResult(ResolutionResult resolutionResult) {
91-
// If the resolution result listener is already an attribute it indicates that a name resolver
92-
// has already been wrapped with this class. This indicates a misconfiguration.
93-
if (resolutionResult.getAttributes().get(RESOLUTION_RESULT_LISTENER_KEY) != null) {
94-
throw new IllegalStateException(
95-
"RetryingNameResolver can only be used once to wrap a NameResolver");
96-
}
97-
98-
// To have retry behavior for name resolvers that haven't migrated to onResult2.
99-
delegateListener.onResult(resolutionResult.toBuilder().setAttributes(
100-
resolutionResult.getAttributes().toBuilder()
101-
.set(RESOLUTION_RESULT_LISTENER_KEY, new ResolutionResultListener()).build())
102-
.build());
96+
syncContext.execute(() -> onResult2(resolutionResult));
10397
}
10498

10599
@Override
@@ -119,19 +113,4 @@ public void onError(Status error) {
119113
syncContext.execute(() -> retryScheduler.schedule(new DelayedNameResolverRefresh()));
120114
}
121115
}
122-
123-
/**
124-
* Simple callback class to store in {@link ResolutionResult} attributes so that
125-
* ManagedChannel can indicate if the resolved addresses were accepted. Temporary until
126-
* the Listener2.onResult() API can be changed to return a boolean for this purpose.
127-
*/
128-
class ResolutionResultListener {
129-
public void resolutionAttempted(Status successStatus) {
130-
if (successStatus.isOk()) {
131-
retryScheduler.reset();
132-
} else {
133-
retryScheduler.schedule(new DelayedNameResolverRefresh());
134-
}
135-
}
136-
}
137116
}

core/src/test/java/io/grpc/internal/DnsNameResolverTest.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -207,14 +207,7 @@ private RetryingNameResolver newResolver(
207207

208208
// In practice the DNS name resolver provider always wraps the resolver in a
209209
// RetryingNameResolver which adds retry capabilities to it. We use the same setup here.
210-
return new RetryingNameResolver(
211-
dnsResolver,
212-
new BackoffPolicyRetryScheduler(
213-
new ExponentialBackoffPolicy.Provider(),
214-
fakeExecutor.getScheduledExecutorService(),
215-
syncContext
216-
),
217-
syncContext);
210+
return (RetryingNameResolver) RetryingNameResolver.wrap(dnsResolver, args);
218211
}
219212

220213
@Before

core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3844,8 +3844,6 @@ public double nextDouble() {
38443844
verify(mockLoadBalancer).acceptResolvedAddresses(resolvedAddressCaptor.capture());
38453845
ResolvedAddresses resolvedAddresses = resolvedAddressCaptor.getValue();
38463846
assertThat(resolvedAddresses.getAddresses()).isEqualTo(nameResolverFactory.servers);
3847-
assertThat(resolvedAddresses.getAttributes()
3848-
.get(RetryingNameResolver.RESOLUTION_RESULT_LISTENER_KEY)).isNotNull();
38493847

38503848
// simulating request connection and then transport ready after resolved address
38513849
Subchannel subchannel =
@@ -3951,8 +3949,6 @@ public void hedgingScheduledThenChannelShutdown_hedgeShouldStillHappen_newCallSh
39513949
verify(mockLoadBalancer).acceptResolvedAddresses(resolvedAddressCaptor.capture());
39523950
ResolvedAddresses resolvedAddresses = resolvedAddressCaptor.getValue();
39533951
assertThat(resolvedAddresses.getAddresses()).isEqualTo(nameResolverFactory.servers);
3954-
assertThat(resolvedAddresses.getAttributes()
3955-
.get(RetryingNameResolver.RESOLUTION_RESULT_LISTENER_KEY)).isNotNull();
39563952

39573953
// simulating request connection and then transport ready after resolved address
39583954
Subchannel subchannel =

core/src/test/java/io/grpc/internal/RetryingNameResolverTest.java

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package io.grpc.internal;
1818

1919
import static com.google.common.truth.Truth.assertThat;
20-
import static org.junit.Assert.fail;
2120
import static org.mockito.ArgumentMatchers.isA;
2221
import static org.mockito.Mockito.mock;
2322
import static org.mockito.Mockito.verify;
@@ -28,7 +27,6 @@
2827
import io.grpc.NameResolver.ResolutionResult;
2928
import io.grpc.Status;
3029
import io.grpc.SynchronizationContext;
31-
import io.grpc.internal.RetryingNameResolver.ResolutionResultListener;
3230
import java.lang.Thread.UncaughtExceptionHandler;
3331
import org.junit.Before;
3432
import org.junit.Rule;
@@ -58,8 +56,6 @@ public class RetryingNameResolverTest {
5856
private RetryScheduler mockRetryScheduler;
5957
@Captor
6058
private ArgumentCaptor<Listener2> listenerCaptor;
61-
@Captor
62-
private ArgumentCaptor<ResolutionResult> onResultCaptor;
6359
private final SynchronizationContext syncContext = new SynchronizationContext(
6460
mock(UncaughtExceptionHandler.class));
6561

@@ -77,21 +73,14 @@ public void startAndShutdown() {
7773
retryingNameResolver.shutdown();
7874
}
7975

80-
// Make sure the ResolutionResultListener callback is added to the ResolutionResult attributes,
81-
// and the retry scheduler is reset since the name resolution was successful.
8276
@Test
8377
public void onResult_success() {
78+
when(mockListener.onResult2(isA(ResolutionResult.class))).thenReturn(Status.OK);
8479
retryingNameResolver.start(mockListener);
8580
verify(mockNameResolver).start(listenerCaptor.capture());
8681

8782
listenerCaptor.getValue().onResult(ResolutionResult.newBuilder().build());
88-
verify(mockListener).onResult(onResultCaptor.capture());
89-
ResolutionResultListener resolutionResultListener = onResultCaptor.getValue()
90-
.getAttributes()
91-
.get(RetryingNameResolver.RESOLUTION_RESULT_LISTENER_KEY);
92-
assertThat(resolutionResultListener).isNotNull();
9383

94-
resolutionResultListener.resolutionAttempted(Status.OK);
9584
verify(mockRetryScheduler).reset();
9685
}
9786

@@ -107,21 +96,15 @@ public void onResult2_sucesss() {
10796
verify(mockRetryScheduler).reset();
10897
}
10998

110-
// Make sure the ResolutionResultListener callback is added to the ResolutionResult attributes,
111-
// and that a retry gets scheduled when the resolution results are rejected.
99+
// Make sure that a retry gets scheduled when the resolution results are rejected.
112100
@Test
113101
public void onResult_failure() {
102+
when(mockListener.onResult2(isA(ResolutionResult.class))).thenReturn(Status.UNAVAILABLE);
114103
retryingNameResolver.start(mockListener);
115104
verify(mockNameResolver).start(listenerCaptor.capture());
116105

117106
listenerCaptor.getValue().onResult(ResolutionResult.newBuilder().build());
118-
verify(mockListener).onResult(onResultCaptor.capture());
119-
ResolutionResultListener resolutionResultListener = onResultCaptor.getValue()
120-
.getAttributes()
121-
.get(RetryingNameResolver.RESOLUTION_RESULT_LISTENER_KEY);
122-
assertThat(resolutionResultListener).isNotNull();
123107

124-
resolutionResultListener.resolutionAttempted(Status.UNAVAILABLE);
125108
verify(mockRetryScheduler).schedule(isA(Runnable.class));
126109
}
127110

@@ -138,24 +121,6 @@ public void onResult2_failure() {
138121
verify(mockRetryScheduler).schedule(isA(Runnable.class));
139122
}
140123

141-
// Wrapping a NameResolver more than once is a misconfiguration.
142-
@Test
143-
public void onResult_failure_doubleWrapped() {
144-
NameResolver doubleWrappedResolver = new RetryingNameResolver(retryingNameResolver,
145-
mockRetryScheduler, syncContext);
146-
147-
doubleWrappedResolver.start(mockListener);
148-
verify(mockNameResolver).start(listenerCaptor.capture());
149-
150-
try {
151-
listenerCaptor.getValue().onResult(ResolutionResult.newBuilder().build());
152-
} catch (IllegalStateException e) {
153-
assertThat(e).hasMessageThat().contains("can only be used once");
154-
return;
155-
}
156-
fail("An exception should have been thrown for a double wrapped NAmeResolver");
157-
}
158-
159124
// A retry should get scheduled when name resolution fails.
160125
@Test
161126
public void onError() {
@@ -165,4 +130,4 @@ public void onError() {
165130
verify(mockListener).onError(Status.DEADLINE_EXCEEDED);
166131
verify(mockRetryScheduler).schedule(isA(Runnable.class));
167132
}
168-
}
133+
}

0 commit comments

Comments
 (0)