• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #18783

pending completion
#18783

push

github-actions

web-flow
util: Outlier detection tracer delegation (#10459) (#10483)

OutlierDetectionLoadBalancer did not delegate calls to an existing
ClientStreamTracer from the tracer it installed. This change has the OD
tracer delegate all calls to the underlying one.

30639 of 34707 relevant lines covered (88.28%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.12
/../core/src/main/java/io/grpc/internal/RetryingNameResolver.java
1
/*
2
 * Copyright 2023 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.internal;
18

19
import com.google.common.annotations.VisibleForTesting;
20
import io.grpc.Attributes;
21
import io.grpc.NameResolver;
22
import io.grpc.Status;
23
import io.grpc.SynchronizationContext;
24

25
/**
26
 * This wrapper class can add retry capability to any polling {@link NameResolver} implementation
27
 * that supports calling {@link ResolutionResultListener}s with the outcome of each resolution.
28
 *
29
 * <p>The {@link NameResolver} used with this
30
 */
31
final class RetryingNameResolver extends ForwardingNameResolver {
32

33
  private final NameResolver retriedNameResolver;
34
  private final RetryScheduler retryScheduler;
35
  private final SynchronizationContext syncContext;
36

37
  static final Attributes.Key<ResolutionResultListener> RESOLUTION_RESULT_LISTENER_KEY
1✔
38
      = Attributes.Key.create(
1✔
39
          "io.grpc.internal.RetryingNameResolver.RESOLUTION_RESULT_LISTENER_KEY");
40

41
  /**
42
   * Creates a new {@link RetryingNameResolver}.
43
   *
44
   * @param retriedNameResolver A {@link NameResolver} that will have failed attempt retried.
45
   * @param retryScheduler Used to schedule the retry attempts.
46
   */
47
  RetryingNameResolver(NameResolver retriedNameResolver, RetryScheduler retryScheduler,
48
      SynchronizationContext syncContext) {
49
    super(retriedNameResolver);
1✔
50
    this.retriedNameResolver = retriedNameResolver;
1✔
51
    this.retryScheduler = retryScheduler;
1✔
52
    this.syncContext = syncContext;
1✔
53
  }
1✔
54

55
  @Override
56
  public void start(Listener2 listener) {
57
    super.start(new RetryingListener(listener));
1✔
58
  }
1✔
59

60
  @Override
61
  public void shutdown() {
62
    super.shutdown();
1✔
63
    retryScheduler.reset();
1✔
64
  }
1✔
65

66
  /**
67
   * Used to get the underlying {@link NameResolver} that is getting its failed attempts retried.
68
   */
69
  @VisibleForTesting
70
  NameResolver getRetriedNameResolver() {
71
    return retriedNameResolver;
1✔
72
  }
73

74
  @VisibleForTesting
75
  class DelayedNameResolverRefresh implements Runnable {
1✔
76
    @Override
77
    public void run() {
78
      refresh();
×
79
    }
×
80
  }
81

82
  private class RetryingListener extends Listener2 {
83
    private Listener2 delegateListener;
84

85
    RetryingListener(Listener2 delegateListener) {
1✔
86
      this.delegateListener = delegateListener;
1✔
87
    }
1✔
88

89
    @Override
90
    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) {
1✔
94
        throw new IllegalStateException(
1✔
95
            "RetryingNameResolver can only be used once to wrap a NameResolver");
96
      }
97

98
      delegateListener.onResult(resolutionResult.toBuilder().setAttributes(
1✔
99
              resolutionResult.getAttributes().toBuilder()
1✔
100
                  .set(RESOLUTION_RESULT_LISTENER_KEY, new ResolutionResultListener()).build())
1✔
101
          .build());
1✔
102
    }
1✔
103

104
    @Override
105
    public void onError(Status error) {
106
      delegateListener.onError(error);
1✔
107
      syncContext.execute(() -> retryScheduler.schedule(new DelayedNameResolverRefresh()));
1✔
108
    }
1✔
109
  }
110

111
  /**
112
   * Simple callback class to store in {@link ResolutionResult} attributes so that
113
   * ManagedChannel can indicate if the resolved addresses were accepted. Temporary until
114
   * the Listener2.onResult() API can be changed to return a boolean for this purpose.
115
   */
116
  class ResolutionResultListener {
1✔
117
    public void resolutionAttempted(boolean successful) {
118
      if (successful) {
1✔
119
        retryScheduler.reset();
1✔
120
      } else {
121
        retryScheduler.schedule(new DelayedNameResolverRefresh());
1✔
122
      }
123
    }
1✔
124
  }
125
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc