Skip to content

Commit 4c1272f

Browse files
authored
api: use <scheme,provider> map in nameResoverRegistry (#8323)
An improvement that makes name resolver provider scheme matching more explicit in name resolver registry.
1 parent a282019 commit 4c1272f

File tree

4 files changed

+118
-45
lines changed

4 files changed

+118
-45
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package io.grpc;
1818

19+
import io.grpc.NameResolver.Factory;
20+
1921
/**
2022
* Provider of name resolvers for name agnostic consumption.
2123
*
@@ -48,4 +50,16 @@ public abstract class NameResolverProvider extends NameResolver.Factory {
4850
* @since 1.0.0
4951
*/
5052
protected abstract int priority();
53+
54+
/**
55+
* Returns the scheme associated with the provider. The provider normally should only create a
56+
* {@link NameResolver} when target URI scheme matches the provider scheme. It temporarily
57+
* delegates to {@link Factory#getDefaultScheme()} before {@link NameResolver.Factory} is
58+
* deprecated in https://github.com/grpc/grpc-java/issues/7133.
59+
*
60+
* @since 1.40.0
61+
* */
62+
protected String getScheme() {
63+
return getDefaultScheme();
64+
}
5165
}

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

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
import static com.google.common.base.Preconditions.checkArgument;
2020

2121
import com.google.common.annotations.VisibleForTesting;
22+
import com.google.common.collect.ImmutableMap;
2223
import java.net.URI;
2324
import java.util.ArrayList;
2425
import java.util.Collections;
25-
import java.util.Comparator;
26+
import java.util.HashMap;
2627
import java.util.LinkedHashSet;
2728
import java.util.List;
29+
import java.util.Map;
2830
import java.util.logging.Level;
2931
import java.util.logging.Logger;
3032
import javax.annotation.Nullable;
@@ -44,12 +46,17 @@ public final class NameResolverRegistry {
4446
private static NameResolverRegistry instance;
4547

4648
private final NameResolver.Factory factory = new NameResolverFactory();
49+
private static final String UNKNOWN_SCHEME = "unknown";
50+
@GuardedBy("this")
51+
private String defaultScheme = UNKNOWN_SCHEME;
4752

4853
@GuardedBy("this")
4954
private final LinkedHashSet<NameResolverProvider> allProviders = new LinkedHashSet<>();
50-
/** Immutable, sorted version of {@code allProviders}. Is replaced instead of mutating. */
55+
/** Generated from {@code allProviders}. Is mapping from scheme key to the highest priority
56+
* {@link NameResolverProvider}. Is replaced instead of mutating. */
5157
@GuardedBy("this")
52-
private List<NameResolverProvider> effectiveProviders = Collections.emptyList();
58+
private ImmutableMap<String, NameResolverProvider> effectiveProviders = ImmutableMap.of();
59+
5360

5461
/**
5562
* Register a provider.
@@ -81,16 +88,23 @@ public synchronized void deregister(NameResolverProvider provider) {
8188
}
8289

8390
private synchronized void refreshProviders() {
84-
List<NameResolverProvider> providers = new ArrayList<>(allProviders);
85-
// Sort descending based on priority.
86-
// sort() must be stable, as we prefer first-registered providers
87-
Collections.sort(providers, Collections.reverseOrder(new Comparator<NameResolverProvider>() {
88-
@Override
89-
public int compare(NameResolverProvider o1, NameResolverProvider o2) {
90-
return o1.priority() - o2.priority();
91+
Map<String, NameResolverProvider> refreshedProviders = new HashMap<>();
92+
int maxPriority = Integer.MIN_VALUE;
93+
String refreshedDefaultScheme = UNKNOWN_SCHEME;
94+
// We prefer first-registered providers
95+
for (NameResolverProvider provider : allProviders) {
96+
String scheme = provider.getScheme();
97+
NameResolverProvider existing = refreshedProviders.get(scheme);
98+
if (existing == null || existing.priority() < provider.priority()) {
99+
refreshedProviders.put(scheme, provider);
100+
}
101+
if (maxPriority < provider.priority()) {
102+
maxPriority = provider.priority();
103+
refreshedDefaultScheme = provider.getScheme();
91104
}
92-
}));
93-
effectiveProviders = Collections.unmodifiableList(providers);
105+
}
106+
effectiveProviders = ImmutableMap.copyOf(refreshedProviders);
107+
defaultScheme = refreshedDefaultScheme;
94108
}
95109

96110
/**
@@ -120,10 +134,11 @@ public static synchronized NameResolverRegistry getDefaultRegistry() {
120134
}
121135

122136
/**
123-
* Returns effective providers, in priority order.
137+
* Returns effective providers map from scheme to the highest priority NameResolverProvider of
138+
* that scheme.
124139
*/
125140
@VisibleForTesting
126-
synchronized List<NameResolverProvider> providers() {
141+
synchronized Map<String, NameResolverProvider> providers() {
127142
return effectiveProviders;
128143
}
129144

@@ -149,23 +164,15 @@ private final class NameResolverFactory extends NameResolver.Factory {
149164
@Override
150165
@Nullable
151166
public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
152-
List<NameResolverProvider> providers = providers();
153-
for (NameResolverProvider provider : providers) {
154-
NameResolver resolver = provider.newNameResolver(targetUri, args);
155-
if (resolver != null) {
156-
return resolver;
157-
}
158-
}
159-
return null;
167+
NameResolverProvider provider = providers().get(targetUri.getScheme());
168+
return provider == null ? null : provider.newNameResolver(targetUri, args);
160169
}
161170

162171
@Override
163172
public String getDefaultScheme() {
164-
List<NameResolverProvider> providers = providers();
165-
if (providers.isEmpty()) {
166-
return "unknown";
173+
synchronized (NameResolverRegistry.this) {
174+
return defaultScheme;
167175
}
168-
return providers.get(0).getDefaultScheme();
169176
}
170177
}
171178

api/src/test/java/io/grpc/NameResolverRegistryTest.java

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.lang.Thread.UncaughtExceptionHandler;
2626
import java.net.URI;
2727
import java.util.List;
28+
import java.util.Map;
2829
import org.junit.Test;
2930
import org.junit.runner.RunWith;
3031
import org.junit.runners.JUnit4;
@@ -59,12 +60,16 @@ public void deregister() {
5960
NameResolverProvider p1 = new BaseProvider(true, 5);
6061
NameResolverProvider p2 = new BaseProvider(true, 5);
6162
NameResolverProvider p3 = new BaseProvider(true, 5);
63+
String sameScheme = p1.getDefaultScheme();
6264
reg.register(p1);
6365
reg.register(p2);
6466
reg.register(p3);
65-
assertThat(reg.providers()).containsExactly(p1, p2, p3).inOrder();
67+
assertThat(reg.providers().get(sameScheme)).isSameInstanceAs(p1);
6668
reg.deregister(p2);
67-
assertThat(reg.providers()).containsExactly(p1, p3).inOrder();
69+
assertThat(reg.providers().get(sameScheme)).isSameInstanceAs(p1);
70+
reg.deregister(p1);
71+
assertThat(reg.providers().get(sameScheme)).isSameInstanceAs(p3);
72+
6873
}
6974

7075
@Test
@@ -75,12 +80,13 @@ public void provider_sorted() {
7580
NameResolverProvider p3 = new BaseProvider(true, 8);
7681
NameResolverProvider p4 = new BaseProvider(true, 3);
7782
NameResolverProvider p5 = new BaseProvider(true, 8);
83+
String sameScheme = p1.getDefaultScheme();
7884
reg.register(p1);
7985
reg.register(p2);
8086
reg.register(p3);
8187
reg.register(p4);
8288
reg.register(p5);
83-
assertThat(reg.providers()).containsExactly(p3, p5, p1, p2, p4).inOrder();
89+
assertThat(reg.providers().get(sameScheme)).isSameInstanceAs(p3);
8490
}
8591

8692
@Test
@@ -93,7 +99,7 @@ public void getDefaultScheme_noProvider() {
9399
public void newNameResolver_providerReturnsNull() {
94100
NameResolverRegistry registry = new NameResolverRegistry();
95101
registry.register(
96-
new BaseProvider(true, 5) {
102+
new BaseProvider(true, 5, "noScheme") {
97103
@Override
98104
public NameResolver newNameResolver(URI passedUri, NameResolver.Args passedArgs) {
99105
assertThat(passedUri).isSameInstanceAs(uri);
@@ -102,12 +108,13 @@ public NameResolver newNameResolver(URI passedUri, NameResolver.Args passedArgs)
102108
}
103109
});
104110
assertThat(registry.asFactory().newNameResolver(uri, args)).isNull();
111+
assertThat(registry.asFactory().getDefaultScheme()).isEqualTo("noScheme");
105112
}
106113

107114
@Test
108115
public void newNameResolver_providerReturnsNonNull() {
109116
NameResolverRegistry registry = new NameResolverRegistry();
110-
registry.register(new BaseProvider(true, 5) {
117+
registry.register(new BaseProvider(true, 5, uri.getScheme()) {
111118
@Override
112119
public NameResolver newNameResolver(URI passedUri, NameResolver.Args passedArgs) {
113120
return null;
@@ -127,38 +134,75 @@ public NameResolver newNameResolver(URI passedUri, NameResolver.Args passedArgs)
127134
}
128135
};
129136
registry.register(
130-
new BaseProvider(true, 4) {
137+
new BaseProvider(true, 4, uri.getScheme()) {
131138
@Override
132139
public NameResolver newNameResolver(URI passedUri, NameResolver.Args passedArgs) {
133140
return nr;
134141
}
135142
});
136143
registry.register(
137-
new BaseProvider(true, 3) {
144+
new BaseProvider(true, 3, uri.getScheme()) {
138145
@Override
139146
public NameResolver newNameResolver(URI passedUri, NameResolver.Args passedArgs) {
140147
fail("Should not be called");
141148
throw new AssertionError();
142149
}
143150
});
144-
assertThat(registry.asFactory().newNameResolver(uri, args)).isSameInstanceAs(nr);
151+
assertThat(registry.asFactory().newNameResolver(uri, args)).isNull();
152+
assertThat(registry.asFactory().getDefaultScheme()).isEqualTo(uri.getScheme());
153+
}
154+
155+
@Test
156+
public void newNameResolver_multipleScheme() {
157+
NameResolverRegistry registry = new NameResolverRegistry();
158+
registry.register(new BaseProvider(true, 5, uri.getScheme()) {
159+
@Override
160+
public NameResolver newNameResolver(URI passedUri, NameResolver.Args passedArgs) {
161+
return null;
162+
}
163+
});
164+
final NameResolver nr = new NameResolver() {
165+
@Override public String getServiceAuthority() {
166+
throw new UnsupportedOperationException();
167+
}
168+
169+
@Override public void start(Listener2 listener) {
170+
throw new UnsupportedOperationException();
171+
}
172+
173+
@Override public void shutdown() {
174+
throw new UnsupportedOperationException();
175+
}
176+
};
177+
registry.register(
178+
new BaseProvider(true, 4, "other") {
179+
@Override
180+
public NameResolver newNameResolver(URI passedUri, NameResolver.Args passedArgs) {
181+
return nr;
182+
}
183+
});
184+
185+
assertThat(registry.asFactory().newNameResolver(uri, args)).isNull();
186+
assertThat(registry.asFactory().newNameResolver(URI.create("other:///0.0.0.0:80"), args))
187+
.isSameInstanceAs(nr);
188+
assertThat(registry.asFactory().getDefaultScheme()).isEqualTo("dns");
145189
}
146190

147191
@Test
148192
public void newNameResolver_noProvider() {
149193
NameResolver.Factory factory = new NameResolverRegistry().asFactory();
150194
assertThat(factory.newNameResolver(uri, args)).isNull();
195+
assertThat(factory.getDefaultScheme()).isEqualTo("unknown");
151196
}
152197

153198
@Test
154199
public void baseProviders() {
155-
List<NameResolverProvider> providers = NameResolverRegistry.getDefaultRegistry().providers();
156-
assertThat(providers).hasSize(2);
157-
// 2 name resolvers from grpclb and core, ordered with decreasing priorities.
158-
assertThat(providers.get(0).getClass().getName())
200+
Map<String, NameResolverProvider> providers =
201+
NameResolverRegistry.getDefaultRegistry().providers();
202+
assertThat(providers).hasSize(1);
203+
// 2 name resolvers from grpclb and core, higher priority one is returned.
204+
assertThat(providers.get("dns").getClass().getName())
159205
.isEqualTo("io.grpc.grpclb.SecretGrpclbNameResolverProvider$Provider");
160-
assertThat(providers.get(1).getClass().getName())
161-
.isEqualTo("io.grpc.internal.DnsNameResolverProvider");
162206
assertThat(NameResolverRegistry.getDefaultRegistry().asFactory().getDefaultScheme())
163207
.isEqualTo("dns");
164208
}
@@ -184,10 +228,18 @@ NameResolverProvider.class, getClass().getClassLoader())) {
184228
private static class BaseProvider extends NameResolverProvider {
185229
private final boolean isAvailable;
186230
private final int priority;
231+
private final String scheme;
187232

188233
public BaseProvider(boolean isAvailable, int priority) {
189234
this.isAvailable = isAvailable;
190235
this.priority = priority;
236+
this.scheme = null;
237+
}
238+
239+
public BaseProvider(boolean isAvailable, int priority, String scheme) {
240+
this.isAvailable = isAvailable;
241+
this.priority = priority;
242+
this.scheme = scheme;
191243
}
192244

193245
@Override
@@ -207,7 +259,7 @@ public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
207259

208260
@Override
209261
public String getDefaultScheme() {
210-
return "scheme" + getClass().getSimpleName();
262+
return scheme == null ? "scheme" + getClass().getSimpleName() : scheme;
211263
}
212264
}
213265
}

xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,11 @@ public class XdsSdsClientServerTest {
8888
private TlsContextManagerImpl tlsContextManagerForServer;
8989

9090
@Before
91-
public void setUp() throws IOException {
91+
public void setUp() throws Exception {
9292
port = XdsServerTestHelper.findFreePort();
93+
URI expectedUri = new URI("sdstest://localhost:" + port);
94+
fakeNameResolverFactory = new FakeNameResolverFactory.Builder(expectedUri).build();
95+
NameResolverRegistry.getDefaultRegistry().register(fakeNameResolverFactory);
9396
}
9497

9598
@After
@@ -410,9 +413,6 @@ static EnvoyServerProtoData.Listener buildListener(
410413
private SimpleServiceGrpc.SimpleServiceBlockingStub getBlockingStub(
411414
final UpstreamTlsContext upstreamTlsContext, String overrideAuthority)
412415
throws URISyntaxException {
413-
URI expectedUri = new URI("sdstest://localhost:" + port);
414-
fakeNameResolverFactory = new FakeNameResolverFactory.Builder(expectedUri).build();
415-
NameResolverRegistry.getDefaultRegistry().register(fakeNameResolverFactory);
416416
ManagedChannelBuilder<?> channelBuilder =
417417
Grpc.newChannelBuilder(
418418
"sdstest://localhost:" + port,

0 commit comments

Comments
 (0)