Skip to content

Commit 7c73baa

Browse files
committed
Revert "Move name resolution retry from managed channel to name resolver. (#9758)"
This reverts commit 43bc578. It breaks API without stair-stepping and needs to be tweaked architecturally.
1 parent f08300e commit 7c73baa

12 files changed

+337
-455
lines changed

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

+2-65
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,6 @@
5959
*/
6060
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
6161
public abstract class NameResolver {
62-
63-
// Outside listeners that get notified of the result of each name resolution.
64-
private ArrayList<ResolutionResultListener> resolutionResultListeners = new ArrayList<>();
65-
6662
/**
6763
* Returns the authority used to authenticate connections to servers. It <strong>must</strong> be
6864
* from a trusted source, because if the authority is tampered with, RPCs may be sent to the
@@ -95,9 +91,8 @@ public void onError(Status error) {
9591
}
9692

9793
@Override
98-
public boolean onResult(ResolutionResult resolutionResult) {
94+
public void onResult(ResolutionResult resolutionResult) {
9995
listener.onAddresses(resolutionResult.getAddresses(), resolutionResult.getAttributes());
100-
return true;
10196
}
10297
});
10398
}
@@ -136,43 +131,6 @@ public void start(Listener2 listener) {
136131
*/
137132
public void refresh() {}
138133

139-
/**
140-
* Adds a new {@link ResolutionResultListener} that will get notified of the outcome of each
141-
* resolution.
142-
*
143-
* @since 1.53.0
144-
*/
145-
public final void addResolutionResultListener(ResolutionResultListener listener) {
146-
checkArgument(listener != null, "listener");
147-
resolutionResultListeners.add(listener);
148-
}
149-
150-
/**
151-
* Removes an existing {@link ResolutionResultListener}.
152-
*
153-
* @return {@code true} if the listener was removed, otherwise {@code false}
154-
* @since 1.53.0
155-
*/
156-
public final boolean removeResolutionResultListener(ResolutionResultListener listener) {
157-
checkArgument(listener != null);
158-
return resolutionResultListeners.remove(listener);
159-
}
160-
161-
/**
162-
* Intended for extending classes to call when they know the result of a name resolution.
163-
*
164-
* <p>Note that while these listeners can be added to any {@link NameResolver}, only concrete
165-
* implementations that call this method will actually support this facility.
166-
*
167-
* @param successful {@code true} if resolution was successful and the addresses were accepted.
168-
* @since 1.53.0
169-
*/
170-
protected final void fireResolutionResultEvent(boolean successful) {
171-
for (ResolutionResultListener listener : resolutionResultListeners) {
172-
listener.resolutionAttempted(successful);
173-
}
174-
}
175-
176134
/**
177135
* Factory that creates {@link NameResolver} instances.
178136
*
@@ -267,12 +225,9 @@ public final void onAddresses(
267225
* {@link ResolutionResult#getAddresses()} is empty, {@link #onError(Status)} will be called.
268226
*
269227
* @param resolutionResult the resolved server addresses, attributes, and Service Config.
270-
* @return {@code true} if the listener accepts the resolved addresses, otherwise {@code false}.
271-
* If the addresses are not accepted the {@link NameResolver} will refresh and retry
272-
* later if it uses polling.
273228
* @since 1.21.0
274229
*/
275-
public abstract boolean onResult(ResolutionResult resolutionResult);
230+
public abstract void onResult(ResolutionResult resolutionResult);
276231

277232
/**
278233
* Handles a name resolving error from the resolver. The listener is responsible for eventually
@@ -294,24 +249,6 @@ public final void onAddresses(
294249
@Documented
295250
public @interface ResolutionResultAttr {}
296251

297-
298-
/**
299-
* A callback interface called at the end of every resolve operation to indicate if the operation
300-
* was successful. Success means that there were no problems with either the name resolution part
301-
* nor with {@link Listener} accepting the resolution results.
302-
*/
303-
public interface ResolutionResultListener {
304-
305-
/**
306-
* Called after an attempt at name resolution.
307-
*
308-
* <p>Note! Implementations of this should return quickly and not throw exceptions.
309-
*
310-
* @param successful {@code true} if resolution was successful and the addresses were accepted.
311-
*/
312-
void resolutionAttempted(boolean successful);
313-
}
314-
315252
/**
316253
* Information that a {@link Factory} uses to create a {@link NameResolver}.
317254
*

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,6 @@ public void run() {
318318
result = doResolve(false);
319319
if (result.error != null) {
320320
savedListener.onError(result.error);
321-
fireResolutionResultEvent(false);
322321
return;
323322
}
324323
if (result.addresses != null) {
@@ -331,7 +330,7 @@ public void run() {
331330
resolutionResultBuilder.setAttributes(result.attributes);
332331
}
333332
}
334-
fireResolutionResultEvent(savedListener.onResult(resolutionResultBuilder.build()));
333+
savedListener.onResult(resolutionResultBuilder.build());
335334
} catch (IOException e) {
336335
savedListener.onError(
337336
Status.UNAVAILABLE.withDescription("Unable to resolve host " + host).withCause(e));

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

+8-12
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,19 @@ public final class DnsNameResolverProvider extends NameResolverProvider {
4747
private static final String SCHEME = "dns";
4848

4949
@Override
50-
public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
50+
public DnsNameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
5151
if (SCHEME.equals(targetUri.getScheme())) {
5252
String targetPath = Preconditions.checkNotNull(targetUri.getPath(), "targetPath");
5353
Preconditions.checkArgument(targetPath.startsWith("/"),
5454
"the path component (%s) of the target (%s) must start with '/'", targetPath, targetUri);
5555
String name = targetPath.substring(1);
56-
return new RetryingNameResolver(
57-
new DnsNameResolver(
58-
targetUri.getAuthority(),
59-
name,
60-
args,
61-
GrpcUtil.SHARED_CHANNEL_EXECUTOR,
62-
Stopwatch.createUnstarted(),
63-
InternalServiceProviders.isAndroid(getClass().getClassLoader())),
64-
new ExponentialBackoffPolicy.Provider(),
65-
args.getScheduledExecutorService(),
66-
args.getSynchronizationContext());
56+
return new DnsNameResolver(
57+
targetUri.getAuthority(),
58+
name,
59+
args,
60+
GrpcUtil.SHARED_CHANNEL_EXECUTOR,
61+
Stopwatch.createUnstarted(),
62+
InternalServiceProviders.isAndroid(getClass().getClassLoader()));
6763
} else {
6864
return null;
6965
}

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

+68-20
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,6 @@ public void uncaughtException(Thread t, Throwable e) {
280280
// Must be mutated and read from constructor or syncContext
281281
// used for channel tracing when value changed
282282
private ManagedChannelServiceConfig lastServiceConfig = EMPTY_SERVICE_CONFIG;
283-
// Must be mutated and read from constructor or syncContext
284-
// Denotes if the last resolved addresses were accepted by the load balancer. A {@code null}
285-
// value indicates no attempt has been made yet.
286-
private Boolean lastAddressesAccepted;
287283

288284
@Nullable
289285
private final ManagedChannelServiceConfig defaultServiceConfig;
@@ -371,6 +367,7 @@ private void shutdownNameResolverAndLoadBalancer(boolean channelIsActive) {
371367
checkState(lbHelper != null, "lbHelper is null");
372368
}
373369
if (nameResolver != null) {
370+
cancelNameResolverBackoff();
374371
nameResolver.shutdown();
375372
nameResolverStarted = false;
376373
if (channelIsActive) {
@@ -453,10 +450,42 @@ private void rescheduleIdleTimer() {
453450
idleTimer.reschedule(idleTimeoutMillis, TimeUnit.MILLISECONDS);
454451
}
455452

453+
// Run from syncContext
454+
@VisibleForTesting
455+
class DelayedNameResolverRefresh implements Runnable {
456+
@Override
457+
public void run() {
458+
scheduledNameResolverRefresh = null;
459+
refreshNameResolution();
460+
}
461+
}
462+
463+
// Must be used from syncContext
464+
@Nullable private ScheduledHandle scheduledNameResolverRefresh;
465+
// The policy to control backoff between name resolution attempts. Non-null when an attempt is
466+
// scheduled. Must be used from syncContext
467+
@Nullable private BackoffPolicy nameResolverBackoffPolicy;
468+
469+
// Must be run from syncContext
470+
private void cancelNameResolverBackoff() {
471+
syncContext.throwIfNotInThisSynchronizationContext();
472+
if (scheduledNameResolverRefresh != null) {
473+
scheduledNameResolverRefresh.cancel();
474+
scheduledNameResolverRefresh = null;
475+
nameResolverBackoffPolicy = null;
476+
}
477+
}
478+
456479
/**
457-
* Force name resolution refresh to happen immediately. Must be run
480+
* Force name resolution refresh to happen immediately and reset refresh back-off. Must be run
458481
* from syncContext.
459482
*/
483+
private void refreshAndResetNameResolution() {
484+
syncContext.throwIfNotInThisSynchronizationContext();
485+
cancelNameResolverBackoff();
486+
refreshNameResolution();
487+
}
488+
460489
private void refreshNameResolution() {
461490
syncContext.throwIfNotInThisSynchronizationContext();
462491
if (nameResolverStarted) {
@@ -1261,7 +1290,7 @@ private void maybeTerminateChannel() {
12611290
// Must be called from syncContext
12621291
private void handleInternalSubchannelState(ConnectivityStateInfo newState) {
12631292
if (newState.getState() == TRANSIENT_FAILURE || newState.getState() == IDLE) {
1264-
refreshNameResolution();
1293+
refreshAndResetNameResolution();
12651294
}
12661295
}
12671296

@@ -1308,9 +1337,9 @@ public void run() {
13081337
if (shutdown.get()) {
13091338
return;
13101339
}
1311-
if (lastAddressesAccepted != null && !lastAddressesAccepted) {
1340+
if (scheduledNameResolverRefresh != null && scheduledNameResolverRefresh.isPending()) {
13121341
checkState(nameResolverStarted, "name resolver must be started");
1313-
refreshNameResolution();
1342+
refreshAndResetNameResolution();
13141343
}
13151344
for (InternalSubchannel subchannel : subchannels) {
13161345
subchannel.resetConnectBackoff();
@@ -1466,7 +1495,7 @@ public void refreshNameResolution() {
14661495
final class LoadBalancerRefreshNameResolution implements Runnable {
14671496
@Override
14681497
public void run() {
1469-
ManagedChannelImpl.this.refreshNameResolution();
1498+
refreshAndResetNameResolution();
14701499
}
14711500
}
14721501

@@ -1707,7 +1736,7 @@ private final class NameResolverListener extends NameResolver.Listener2 {
17071736
}
17081737

17091738
@Override
1710-
public boolean onResult(final ResolutionResult resolutionResult) {
1739+
public void onResult(final ResolutionResult resolutionResult) {
17111740
final class NamesResolved implements Runnable {
17121741

17131742
@SuppressWarnings("ReferenceEquality")
@@ -1716,7 +1745,6 @@ public void run() {
17161745
if (ManagedChannelImpl.this.nameResolver != resolver) {
17171746
return;
17181747
}
1719-
lastAddressesAccepted = false;
17201748

17211749
List<EquivalentAddressGroup> servers = resolutionResult.getAddresses();
17221750
channelLogger.log(
@@ -1730,6 +1758,7 @@ public void run() {
17301758
lastResolutionState = ResolutionState.SUCCESS;
17311759
}
17321760

1761+
nameResolverBackoffPolicy = null;
17331762
ConfigOrError configOrError = resolutionResult.getServiceConfig();
17341763
InternalConfigSelector resolvedConfigSelector =
17351764
resolutionResult.getAttributes().get(InternalConfigSelector.KEY);
@@ -1787,7 +1816,6 @@ public void run() {
17871816
// we later check for these error codes when investigating pick results in
17881817
// GrpcUtil.getTransportFromPickResult().
17891818
onError(configOrError.getError());
1790-
lastAddressesAccepted = false;
17911819
return;
17921820
} else {
17931821
effectiveServiceConfig = lastServiceConfig;
@@ -1831,24 +1859,21 @@ public void run() {
18311859
}
18321860
Attributes attributes = attrBuilder.build();
18331861

1834-
lastAddressesAccepted = helper.lb.tryAcceptResolvedAddresses(
1862+
boolean addressesAccepted = helper.lb.tryAcceptResolvedAddresses(
18351863
ResolvedAddresses.newBuilder()
18361864
.setAddresses(servers)
18371865
.setAttributes(attributes)
18381866
.setLoadBalancingPolicyConfig(effectiveServiceConfig.getLoadBalancingConfig())
18391867
.build());
1868+
1869+
if (!addressesAccepted) {
1870+
scheduleExponentialBackOffInSyncContext();
1871+
}
18401872
}
18411873
}
18421874
}
18431875

18441876
syncContext.execute(new NamesResolved());
1845-
1846-
// If NameResolved did not assign a value to lastAddressesAccepted, we assume there was an
1847-
// exception and set it to false.
1848-
if (lastAddressesAccepted == null) {
1849-
lastAddressesAccepted = false;
1850-
}
1851-
return lastAddressesAccepted;
18521877
}
18531878

18541879
@Override
@@ -1878,6 +1903,29 @@ private void handleErrorInSyncContext(Status error) {
18781903
}
18791904

18801905
helper.lb.handleNameResolutionError(error);
1906+
1907+
scheduleExponentialBackOffInSyncContext();
1908+
}
1909+
1910+
private void scheduleExponentialBackOffInSyncContext() {
1911+
if (scheduledNameResolverRefresh != null && scheduledNameResolverRefresh.isPending()) {
1912+
// The name resolver may invoke onError multiple times, but we only want to
1913+
// schedule one backoff attempt
1914+
// TODO(ericgribkoff) Update contract of NameResolver.Listener or decide if we
1915+
// want to reset the backoff interval upon repeated onError() calls
1916+
return;
1917+
}
1918+
if (nameResolverBackoffPolicy == null) {
1919+
nameResolverBackoffPolicy = backoffPolicyProvider.get();
1920+
}
1921+
long delayNanos = nameResolverBackoffPolicy.nextBackoffNanos();
1922+
channelLogger.log(
1923+
ChannelLogLevel.DEBUG,
1924+
"Scheduling DNS resolution backoff for {0} ns", delayNanos);
1925+
scheduledNameResolverRefresh =
1926+
syncContext.schedule(
1927+
new DelayedNameResolverRefresh(), delayNanos, TimeUnit.NANOSECONDS,
1928+
transportFactory .getScheduledExecutorService());
18811929
}
18821930
}
18831931

0 commit comments

Comments
 (0)