@@ -344,6 +344,7 @@ private void shutdownNameResolverAndLoadBalancer(boolean verifyActive) {
344
344
checkState (lbHelper != null , "lbHelper is null" );
345
345
}
346
346
if (nameResolver != null ) {
347
+ cancelNameResolverBackoff ();
347
348
nameResolver .shutdown ();
348
349
nameResolver = null ;
349
350
nameResolverStarted = false ;
@@ -429,6 +430,46 @@ public void run() {
429
430
idleTimeoutMillis , TimeUnit .MILLISECONDS );
430
431
}
431
432
433
+ // Run from channelExecutor
434
+ @ VisibleForTesting
435
+ class NameResolverRefresh implements Runnable {
436
+ // Only mutated from channelExecutor
437
+ boolean cancelled ;
438
+
439
+ @ Override
440
+ public void run () {
441
+ if (cancelled ) {
442
+ // Race detected: this task was scheduled on channelExecutor before
443
+ // cancelNameResolverBackoff() could cancel the timer.
444
+ return ;
445
+ }
446
+ nameResolverRefreshFuture = null ;
447
+ nameResolverRefresh = null ;
448
+ if (nameResolver != null ) {
449
+ nameResolver .refresh ();
450
+ }
451
+ }
452
+ }
453
+
454
+ // Must be used from channelExecutor
455
+ @ Nullable private ScheduledFuture <?> nameResolverRefreshFuture ;
456
+ // Must be used from channelExecutor
457
+ @ Nullable private NameResolverRefresh nameResolverRefresh ;
458
+ // The policy to control backoff between name resolution attempts. Non-null when an attempt is
459
+ // scheduled. Must be used from channelExecutor
460
+ @ Nullable private BackoffPolicy nameResolverBackoffPolicy ;
461
+
462
+ // Must be run from channelExecutor
463
+ private void cancelNameResolverBackoff () {
464
+ if (nameResolverRefreshFuture != null ) {
465
+ nameResolverRefreshFuture .cancel (false );
466
+ nameResolverRefresh .cancelled = true ;
467
+ nameResolverRefreshFuture = null ;
468
+ nameResolverRefresh = null ;
469
+ nameResolverBackoffPolicy = null ;
470
+ }
471
+ }
472
+
432
473
private final ClientTransportProvider transportProvider = new ClientTransportProvider () {
433
474
@ Override
434
475
public ClientTransport get (PickSubchannelArgs args ) {
@@ -799,24 +840,28 @@ public void run() {
799
840
800
841
@ Override
801
842
public void resetConnectBackoff () {
802
- channelExecutor .executeLater (
803
- new Runnable () {
804
- @ Override
805
- public void run () {
806
- if (shutdown .get ()) {
807
- return ;
808
- }
809
- if (nameResolverStarted ) {
810
- nameResolver .refresh ();
811
- }
812
- for (InternalSubchannel subchannel : subchannels ) {
813
- subchannel .resetConnectBackoff ();
814
- }
815
- for (OobChannel oobChannel : oobChannels ) {
816
- oobChannel .resetConnectBackoff ();
817
- }
818
- }
819
- }).drain ();
843
+ channelExecutor
844
+ .executeLater (
845
+ new Runnable () {
846
+ @ Override
847
+ public void run () {
848
+ if (shutdown .get ()) {
849
+ return ;
850
+ }
851
+ if (nameResolverRefreshFuture != null ) {
852
+ checkState (nameResolverStarted , "name resolver must be started" );
853
+ cancelNameResolverBackoff ();
854
+ nameResolver .refresh ();
855
+ }
856
+ for (InternalSubchannel subchannel : subchannels ) {
857
+ subchannel .resetConnectBackoff ();
858
+ }
859
+ for (OobChannel oobChannel : oobChannels ) {
860
+ oobChannel .resetConnectBackoff ();
861
+ }
862
+ }
863
+ })
864
+ .drain ();
820
865
}
821
866
822
867
@ Override
@@ -1132,6 +1177,8 @@ public void run() {
1132
1177
return ;
1133
1178
}
1134
1179
1180
+ nameResolverBackoffPolicy = null ;
1181
+
1135
1182
try {
1136
1183
if (retryEnabled ) {
1137
1184
retryPolicies = getRetryPolicies (config );
@@ -1156,16 +1203,41 @@ public void onError(final Status error) {
1156
1203
checkArgument (!error .isOk (), "the error status must not be OK" );
1157
1204
logger .log (Level .WARNING , "[{0}] Failed to resolve name. status={1}" ,
1158
1205
new Object [] {getLogId (), error });
1159
- channelExecutor .executeLater (new Runnable () {
1160
- @ Override
1161
- public void run () {
1162
- // Call LB only if it's not shutdown. If LB is shutdown, lbHelper won't match.
1163
- if (NameResolverListenerImpl .this .helper != ManagedChannelImpl .this .lbHelper ) {
1164
- return ;
1165
- }
1166
- balancer .handleNameResolutionError (error );
1167
- }
1168
- }).drain ();
1206
+ channelExecutor
1207
+ .executeLater (
1208
+ new Runnable () {
1209
+ @ Override
1210
+ public void run () {
1211
+ // Call LB only if it's not shutdown. If LB is shutdown, lbHelper won't match.
1212
+ if (NameResolverListenerImpl .this .helper != ManagedChannelImpl .this .lbHelper ) {
1213
+ return ;
1214
+ }
1215
+ balancer .handleNameResolutionError (error );
1216
+ if (nameResolverRefreshFuture != null ) {
1217
+ // The name resolver may invoke onError multiple times, but we only want to
1218
+ // schedule one backoff attempt
1219
+ // TODO(ericgribkoff) Update contract of NameResolver.Listener or decide if we
1220
+ // want to reset the backoff interval upon repeated onError() calls
1221
+ return ;
1222
+ }
1223
+ if (nameResolverBackoffPolicy == null ) {
1224
+ nameResolverBackoffPolicy = backoffPolicyProvider .get ();
1225
+ }
1226
+ long delayNanos = nameResolverBackoffPolicy .nextBackoffNanos ();
1227
+ if (logger .isLoggable (Level .FINE )) {
1228
+ logger .log (
1229
+ Level .FINE ,
1230
+ "[{0}] Scheduling DNS resolution backoff for {1} ns" ,
1231
+ new Object [] {logId , delayNanos });
1232
+ }
1233
+ nameResolverRefresh = new NameResolverRefresh ();
1234
+ nameResolverRefreshFuture =
1235
+ transportFactory
1236
+ .getScheduledExecutorService ()
1237
+ .schedule (nameResolverRefresh , delayNanos , TimeUnit .NANOSECONDS );
1238
+ }
1239
+ })
1240
+ .drain ();
1169
1241
}
1170
1242
}
1171
1243
0 commit comments