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

grpc / grpc-java / #20136

06 Jan 2026 05:27AM UTC coverage: 88.693% (+0.01%) from 88.681%
#20136

push

github

web-flow
core: Implement oobChannel with resolvingOobChannel

The most important part of this change is to ensure that CallCredentials
are not propagated to the OOB channel. Because the authority of the OOB
channel doesn't match the parent channel, we must ensure that any bearer
tokens are not sent to the different server. However, this was not a
problem because resolvingOobChannel has the same constraint. (RLS has a
different constraint, but we were able to let RLS manage that itself.)

This commit does change the behavior of channelz, shutdown, and metrics
for the OOB channel. Previously the OOB channel was registered with
channelz, but it is only a TODO for resolving channel. Channel shutdown
no longer shuts down the OOB channel and it no longer waits for the OOB
channel to terminate before becoming terminated itself. That is also a
pre-existing TODO. Since ManagedChannelImplBuilder is now being used,
global configurators and census are enabled. The proper behavior here is
still being determined, but we would want it to be the same for
resolving OOB channel and OOB channel.

The OOB channel used to refresh the name resolution when the subchannel
went IDLE or TF. That is an older behavior from back when regular
subchannels would also cause the name resolver to refresh. Now-a-days
that goes though the LB tree. gRPC-LB already refreshes name resolution
when its RPC closes, so no longer doing it automatically should be fine.

balancerRpcExecutorPool no longer has its lifetime managed by the child.
It'd be easiest to not use it at all from OOB channel, which wouldn't
actually change the regular behavior, as channels already use the same
executor by default. However, the tests are making use of the executor
being injected, so some propagation needs to be preserved.

Lots of OOB channel tests were deleted, but these were either testing
OobChannel, which is now gone, or things like channelz, which are known
to no longer work like before.

35361 of 39869 relevant lines covered (88.69%)

0.89 hits per line

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

89.36
/../rls/src/main/java/io/grpc/rls/RlsLoadBalancer.java
1
/*
2
 * Copyright 2020 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.rls;
18

19
import static com.google.common.base.Preconditions.checkNotNull;
20

21
import com.google.common.annotations.VisibleForTesting;
22
import io.grpc.ChannelLogger;
23
import io.grpc.ChannelLogger.ChannelLogLevel;
24
import io.grpc.ConnectivityState;
25
import io.grpc.LoadBalancer;
26
import io.grpc.Status;
27
import javax.annotation.Nullable;
28

29
/**
30
 * Implementation of {@link LoadBalancer} backed by route lookup service.
31
 */
32
final class RlsLoadBalancer extends LoadBalancer {
33

34
  private final ChannelLogger logger;
35
  private final Helper helper;
36
  @VisibleForTesting
1✔
37
  CachingRlsLbClientBuilderProvider cachingRlsLbClientBuilderProvider =
38
      new DefaultCachingRlsLbClientBuilderProvider();
39
  @Nullable
40
  private LbPolicyConfiguration lbPolicyConfiguration;
41
  @Nullable
42
  private CachingRlsLbClient routeLookupClient;
43

44
  RlsLoadBalancer(Helper helper) {
1✔
45
    this.helper = checkNotNull(helper, "helper");
1✔
46
    logger = helper.getChannelLogger();
1✔
47
    logger.log(ChannelLogLevel.DEBUG, "Rls lb created. Authority: {0}", helper.getAuthority());
1✔
48
  }
1✔
49

50
  @Override
51
  public Status acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
52
    LbPolicyConfiguration lbPolicyConfiguration =
1✔
53
        (LbPolicyConfiguration) resolvedAddresses.getLoadBalancingPolicyConfig();
1✔
54
    checkNotNull(lbPolicyConfiguration, "Missing RLS LB config");
1✔
55
    if (!lbPolicyConfiguration.equals(this.lbPolicyConfiguration)) {
1✔
56
      logger.log(ChannelLogLevel.DEBUG, "A new RLS LB config received: {0}", lbPolicyConfiguration);
1✔
57
      boolean needToConnect = this.lbPolicyConfiguration == null
1✔
58
          || !this.lbPolicyConfiguration.getRouteLookupConfig().lookupService().equals(
1✔
59
          lbPolicyConfiguration.getRouteLookupConfig().lookupService());
×
60
      if (needToConnect) {
1✔
61
        logger.log(ChannelLogLevel.DEBUG, "RLS lookup service changed, need to connect");
1✔
62
        if (routeLookupClient != null) {
1✔
63
          routeLookupClient.close();
×
64
        }
65
        routeLookupClient =
1✔
66
            cachingRlsLbClientBuilderProvider
67
                .get()
1✔
68
                .setHelper(helper)
1✔
69
                .setLbPolicyConfig(lbPolicyConfiguration)
1✔
70
                .setResolvedAddressesFactory(
1✔
71
                    new ChildLbResolvedAddressFactory(
72
                        resolvedAddresses.getAddresses(), resolvedAddresses.getAttributes()))
1✔
73
                .build();
1✔
74
        logger.log(
1✔
75
            ChannelLogLevel.DEBUG, "LbPolicyConfiguration updated to {0}", lbPolicyConfiguration);
76
      }
77
      // TODO(creamsoup) allow incremental service config update. for initial use case, it is 
78
      //  not required.
79
      this.lbPolicyConfiguration = lbPolicyConfiguration;
1✔
80
    }
81
    return routeLookupClient.acceptResolvedAddressFactory(
1✔
82
        new ChildLbResolvedAddressFactory(
83
            resolvedAddresses.getAddresses(), resolvedAddresses.getAttributes()));
1✔
84
  }
85

86
  @Override
87
  public void requestConnection() {
88
    if (routeLookupClient != null) {
×
89
      routeLookupClient.requestConnection();
×
90
    }
91
  }
×
92

93
  @Override
94
  public void handleNameResolutionError(final Status error) {
95
    if (routeLookupClient != null) {
1✔
96
      logger.log(ChannelLogLevel.DEBUG, "closing the routeLookupClient on a name resolution error");
1✔
97
      routeLookupClient.close();
1✔
98
      routeLookupClient = null;
1✔
99
      lbPolicyConfiguration = null;
1✔
100
    }
101
    helper.updateBalancingState(
1✔
102
        ConnectivityState.TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(error)));
1✔
103
  }
1✔
104

105
  @Override
106
  public void shutdown() {
107
    if (routeLookupClient != null) {
1✔
108
      logger.log(ChannelLogLevel.DEBUG, "closing the routeLookupClient because of RLS LB shutdown");
1✔
109
      routeLookupClient.close();
1✔
110
      routeLookupClient = null;
1✔
111
    }
112
  }
1✔
113

114
  /**
115
   * Provides {@link CachingRlsLbClient.Builder} with default settings. This is useful for
116
   * testing.
117
   */
118
  interface CachingRlsLbClientBuilderProvider {
119
    CachingRlsLbClient.Builder get();
120
  }
121

122
  static final class DefaultCachingRlsLbClientBuilderProvider
1✔
123
      implements CachingRlsLbClientBuilderProvider {
124

125
    @Override
126
    public CachingRlsLbClient.Builder get() {
127
      return CachingRlsLbClient.newBuilder().setThrottler(AdaptiveThrottler.builder().build());
1✔
128
    }
129
  }
130
}
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

© 2026 Coveralls, Inc