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

grpc / grpc-java / #20220

25 Mar 2026 06:04AM UTC coverage: 88.693%. Remained the same
#20220

push

github

web-flow
api: Deprecate LoadBalancer.handleResolvedAddresses() (#11623)

Also deprecate its companion
canHandleEmptyAddressListFromNameResolution().
Also fixup the Javadoc to align with the arguments/return values, so
that people would have a better idea of how to use it.

Fixes #11194

---------

Co-authored-by: MV Shiva Prasad <okshiva@google.com>
Co-authored-by: Eric Anderson <ejona@google.com>

35486 of 40010 relevant lines covered (88.69%)

0.89 hits per line

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

98.1
/../util/src/main/java/io/grpc/util/GracefulSwitchLoadBalancer.java
1
/*
2
 * Copyright 2019 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.util;
18

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

22
import com.google.common.base.MoreObjects;
23
import com.google.common.base.Objects;
24
import io.grpc.ConnectivityState;
25
import io.grpc.ConnectivityStateInfo;
26
import io.grpc.ExperimentalApi;
27
import io.grpc.LoadBalancer;
28
import io.grpc.LoadBalancerRegistry;
29
import io.grpc.NameResolver.ConfigOrError;
30
import io.grpc.Status;
31
import io.grpc.internal.ServiceConfigUtil;
32
import java.util.List;
33
import java.util.Map;
34
import javax.annotation.Nullable;
35
import javax.annotation.concurrent.NotThreadSafe;
36

37
/**
38
 * A load balancer that gracefully swaps to a new lb policy. If the channel is currently in a state
39
 * other than READY, the new policy will be swapped into place immediately.  Otherwise, the channel
40
 * will keep using the old policy until the new policy leaves CONNECTING or the old policy exits
41
 * READY.
42
 *
43
 * <p>The child balancer and configuration is specified using service config. Config objects are
44
 * generally created by calling {@link #parseLoadBalancingPolicyConfig(List)} from a
45
 * {@link io.grpc.LoadBalancerProvider#parseLoadBalancingPolicyConfig
46
 * provider's parseLoadBalancingPolicyConfig()} implementation.
47
 */
48
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/5999")
49
@NotThreadSafe // Must be accessed in SynchronizationContext
50
public final class GracefulSwitchLoadBalancer extends ForwardingLoadBalancer {
51
  private final LoadBalancer defaultBalancer = new LoadBalancer() {
1✔
52
    @Override
53
    public Status acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
54
      throw new AssertionError("real LB is called instead");
×
55
    }
56

57
    @Override
58
    public void handleNameResolutionError(final Status error) {
59
      helper.updateBalancingState(
1✔
60
          ConnectivityState.TRANSIENT_FAILURE,
61
          new FixedResultPicker(PickResult.withError(error)));
1✔
62
    }
1✔
63

64
    @Override
65
    public void shutdown() {}
1✔
66
  };
67

68
  private final Helper helper;
69

70
  // While the new policy is not fully switched on, the pendingLb is handling new updates from name
71
  // resolver, and the currentLb is updating channel state and picker for the given helper.
72
  // The current fields are guaranteed to be set after the initial swapTo().
73
  // The pending fields are cleared when it becomes current.
74
  @Nullable private LoadBalancer.Factory currentBalancerFactory;
75
  private LoadBalancer currentLb = defaultBalancer;
1✔
76
  @Nullable private LoadBalancer.Factory pendingBalancerFactory;
77
  private LoadBalancer pendingLb = defaultBalancer;
1✔
78
  private ConnectivityState pendingState;
79
  private SubchannelPicker pendingPicker;
80

81
  private boolean currentLbIsReady;
82

83
  public GracefulSwitchLoadBalancer(Helper helper) {
1✔
84
    this.helper = checkNotNull(helper, "helper");
1✔
85
  }
1✔
86

87
  @Deprecated
88
  @Override
89
  public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
90
    Config config = (Config) resolvedAddresses.getLoadBalancingPolicyConfig();
1✔
91
    switchToInternal(config.childFactory);
1✔
92
    delegate().handleResolvedAddresses(
1✔
93
        resolvedAddresses.toBuilder()
1✔
94
          .setLoadBalancingPolicyConfig(config.childConfig)
1✔
95
          .build());
1✔
96
  }
1✔
97

98
  @Override
99
  public Status acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
100
    Config config = (Config) resolvedAddresses.getLoadBalancingPolicyConfig();
1✔
101
    switchToInternal(config.childFactory);
1✔
102
    return delegate().acceptResolvedAddresses(
1✔
103
        resolvedAddresses.toBuilder()
1✔
104
          .setLoadBalancingPolicyConfig(config.childConfig)
1✔
105
          .build());
1✔
106
  }
107

108
  private void switchToInternal(LoadBalancer.Factory newBalancerFactory) {
109
    checkNotNull(newBalancerFactory, "newBalancerFactory");
1✔
110

111
    if (newBalancerFactory.equals(pendingBalancerFactory)) {
1✔
112
      return;
1✔
113
    }
114
    pendingLb.shutdown();
1✔
115
    pendingLb = defaultBalancer;
1✔
116
    pendingBalancerFactory = null;
1✔
117
    pendingState = ConnectivityState.CONNECTING;
1✔
118
    pendingPicker = new FixedResultPicker(PickResult.withNoResult());
1✔
119

120
    if (newBalancerFactory.equals(currentBalancerFactory)) {
1✔
121
      return;
1✔
122
    }
123

124
    class PendingHelper extends ForwardingLoadBalancerHelper {
1✔
125
      LoadBalancer lb;
126

127
      @Override
128
      protected Helper delegate() {
129
        return helper;
1✔
130
      }
131

132
      @Override
133
      public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
134
        if (lb == pendingLb) {
1✔
135
          checkState(currentLbIsReady, "there's pending lb while current lb has been out of READY");
1✔
136
          pendingState = newState;
1✔
137
          pendingPicker = newPicker;
1✔
138
          if (newState != ConnectivityState.CONNECTING) {
1✔
139
            swap();
1✔
140
          }
141
        } else if (lb == currentLb) {
1✔
142
          currentLbIsReady = newState == ConnectivityState.READY;
1✔
143
          if (!currentLbIsReady && pendingLb != defaultBalancer) {
1✔
144
            swap(); // current policy exits READY, so swap
1✔
145
          } else {
146
            helper.updateBalancingState(newState, newPicker);
1✔
147
          }
148
        }
149
      }
1✔
150
    }
151

152
    PendingHelper pendingHelper = new PendingHelper();
1✔
153
    pendingHelper.lb = newBalancerFactory.newLoadBalancer(pendingHelper);
1✔
154
    pendingLb = pendingHelper.lb;
1✔
155
    pendingBalancerFactory = newBalancerFactory;
1✔
156
    if (!currentLbIsReady) {
1✔
157
      swap(); // the old policy is not READY at the moment, so swap to the new one right now
1✔
158
    }
159
  }
1✔
160

161
  private void swap() {
162
    helper.updateBalancingState(pendingState, pendingPicker);
1✔
163
    currentLb.shutdown();
1✔
164
    currentLb = pendingLb;
1✔
165
    currentBalancerFactory = pendingBalancerFactory;
1✔
166
    pendingLb = defaultBalancer;
1✔
167
    pendingBalancerFactory = null;
1✔
168
  }
1✔
169

170
  @Override
171
  protected LoadBalancer delegate() {
172
    return pendingLb == defaultBalancer ? currentLb : pendingLb;
1✔
173
  }
174

175
  @Override
176
  @Deprecated
177
  public void handleSubchannelState(
178
      Subchannel subchannel, ConnectivityStateInfo stateInfo) {
179
    throw new UnsupportedOperationException(
1✔
180
        "handleSubchannelState() is not supported by " + this.getClass().getName());
1✔
181
  }
182

183
  @Override
184
  public void shutdown() {
185
    pendingLb.shutdown();
1✔
186
    currentLb.shutdown();
1✔
187
  }
1✔
188

189
  public String delegateType() {
190
    return delegate().getClass().getSimpleName();
×
191
  }
192

193
  /**
194
   * Provided a JSON list of LoadBalancingConfigs, parse it into a config to pass to GracefulSwitch.
195
   */
196
  public static ConfigOrError parseLoadBalancingPolicyConfig(
197
      List<Map<String, ?>> loadBalancingConfigs) {
198
    return parseLoadBalancingPolicyConfig(
1✔
199
        loadBalancingConfigs, LoadBalancerRegistry.getDefaultRegistry());
1✔
200
  }
201

202
  /**
203
   * Provided a JSON list of LoadBalancingConfigs, parse it into a config to pass to GracefulSwitch.
204
   */
205
  public static ConfigOrError parseLoadBalancingPolicyConfig(
206
      List<Map<String, ?>> loadBalancingConfigs, LoadBalancerRegistry lbRegistry) {
207
    List<ServiceConfigUtil.LbConfig> childConfigCandidates =
1✔
208
        ServiceConfigUtil.unwrapLoadBalancingConfigList(loadBalancingConfigs);
1✔
209
    if (childConfigCandidates == null || childConfigCandidates.isEmpty()) {
1✔
210
      return ConfigOrError.fromError(
1✔
211
          Status.UNAVAILABLE.withDescription("No child LB config specified"));
1✔
212
    }
213
    ConfigOrError selectedConfig =
1✔
214
        ServiceConfigUtil.selectLbPolicyFromList(childConfigCandidates, lbRegistry);
1✔
215
    if (selectedConfig.getError() != null) {
1✔
216
      Status error = selectedConfig.getError();
1✔
217
      return ConfigOrError.fromError(
1✔
218
          Status.UNAVAILABLE
219
              .withCause(error.getCause())
1✔
220
              .withDescription(error.getDescription())
1✔
221
              .augmentDescription("Failed to select child config"));
1✔
222
    }
223
    ServiceConfigUtil.PolicySelection selection =
1✔
224
        (ServiceConfigUtil.PolicySelection) selectedConfig.getConfig();
1✔
225
    return ConfigOrError.fromConfig(
1✔
226
        createLoadBalancingPolicyConfig(selection.getProvider(), selection.getConfig()));
1✔
227
  }
228

229
  /**
230
   * Directly create a config to pass to GracefulSwitch. The object returned is the same as would be
231
   * found in {@code ConfigOrError.getConfig()}.
232
   */
233
  public static Object createLoadBalancingPolicyConfig(
234
      LoadBalancer.Factory childFactory, @Nullable Object childConfig) {
235
    return new Config(childFactory, childConfig);
1✔
236
  }
237

238
  static final class Config {
239
    final LoadBalancer.Factory childFactory;
240
    @Nullable
241
    final Object childConfig;
242

243
    public Config(LoadBalancer.Factory childFactory, @Nullable Object childConfig) {
1✔
244
      this.childFactory = checkNotNull(childFactory, "childFactory");
1✔
245
      this.childConfig = childConfig;
1✔
246
    }
1✔
247

248
    @Override
249
    public boolean equals(Object o) {
250
      if (this == o) {
1✔
251
        return true;
1✔
252
      }
253
      if (!(o instanceof Config)) {
1✔
254
        return false;
1✔
255
      }
256
      Config that = (Config) o;
1✔
257
      return Objects.equal(childFactory, that.childFactory)
1✔
258
          && Objects.equal(childConfig, that.childConfig);
1✔
259
    }
260

261
    @Override
262
    public int hashCode() {
263
      return Objects.hashCode(childFactory, childConfig);
1✔
264
    }
265

266
    @Override
267
    public String toString() {
268
      return MoreObjects.toStringHelper("GracefulSwitchLoadBalancer.Config")
1✔
269
          .add("childFactory", childFactory)
1✔
270
          .add("childConfig", childConfig)
1✔
271
          .toString();
1✔
272
    }
273
  }
274
}
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