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

grpc / grpc-java / #19706

21 Feb 2025 12:06AM UTC coverage: 88.601% (-0.02%) from 88.616%
#19706

push

github

ejona86
util: Remove GracefulSwitchLb.switchTo()

It was deprecated in 85e0a01ec, so has been deprecated for six
releases/over six months.

34292 of 38704 relevant lines covered (88.6%)

0.89 hits per line

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

97.22
/../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.annotations.VisibleForTesting;
23
import com.google.common.base.MoreObjects;
24
import com.google.common.base.Objects;
25
import io.grpc.ConnectivityState;
26
import io.grpc.ConnectivityStateInfo;
27
import io.grpc.ExperimentalApi;
28
import io.grpc.LoadBalancer;
29
import io.grpc.LoadBalancerRegistry;
30
import io.grpc.NameResolver.ConfigOrError;
31
import io.grpc.Status;
32
import io.grpc.internal.ServiceConfigUtil;
33
import java.util.List;
34
import java.util.Map;
35
import javax.annotation.Nullable;
36
import javax.annotation.concurrent.NotThreadSafe;
37

38
/**
39
 * A load balancer that gracefully swaps to a new lb policy. If the channel is currently in a state
40
 * other than READY, the new policy will be swapped into place immediately.  Otherwise, the channel
41
 * will keep using the old policy until the new policy reports READY or the old policy exits 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
  @VisibleForTesting
69
  static final SubchannelPicker BUFFER_PICKER = new SubchannelPicker() {
1✔
70
    @Override
71
    public PickResult pickSubchannel(PickSubchannelArgs args) {
72
      return PickResult.withNoResult();
1✔
73
    }
74

75
    @Override
76
    public String toString() {
77
      return "BUFFER_PICKER";
×
78
    }
79
  };
80

81
  private final Helper helper;
82

83
  // While the new policy is not fully switched on, the pendingLb is handling new updates from name
84
  // resolver, and the currentLb is updating channel state and picker for the given helper.
85
  // The current fields are guaranteed to be set after the initial swapTo().
86
  // The pending fields are cleared when it becomes current.
87
  @Nullable private LoadBalancer.Factory currentBalancerFactory;
88
  private LoadBalancer currentLb = defaultBalancer;
1✔
89
  @Nullable private LoadBalancer.Factory pendingBalancerFactory;
90
  private LoadBalancer pendingLb = defaultBalancer;
1✔
91
  private ConnectivityState pendingState;
92
  private SubchannelPicker pendingPicker;
93

94
  private boolean currentLbIsReady;
95

96
  public GracefulSwitchLoadBalancer(Helper helper) {
1✔
97
    this.helper = checkNotNull(helper, "helper");
1✔
98
  }
1✔
99

100
  @Override
101
  public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
102
    Config config = (Config) resolvedAddresses.getLoadBalancingPolicyConfig();
1✔
103
    switchToInternal(config.childFactory);
1✔
104
    delegate().handleResolvedAddresses(
1✔
105
        resolvedAddresses.toBuilder()
1✔
106
          .setLoadBalancingPolicyConfig(config.childConfig)
1✔
107
          .build());
1✔
108
  }
1✔
109

110
  @Override
111
  public Status acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
112
    Config config = (Config) resolvedAddresses.getLoadBalancingPolicyConfig();
1✔
113
    switchToInternal(config.childFactory);
1✔
114
    return delegate().acceptResolvedAddresses(
1✔
115
        resolvedAddresses.toBuilder()
1✔
116
          .setLoadBalancingPolicyConfig(config.childConfig)
1✔
117
          .build());
1✔
118
  }
119

120
  private void switchToInternal(LoadBalancer.Factory newBalancerFactory) {
121
    checkNotNull(newBalancerFactory, "newBalancerFactory");
1✔
122

123
    if (newBalancerFactory.equals(pendingBalancerFactory)) {
1✔
124
      return;
1✔
125
    }
126
    pendingLb.shutdown();
1✔
127
    pendingLb = defaultBalancer;
1✔
128
    pendingBalancerFactory = null;
1✔
129
    pendingState = ConnectivityState.CONNECTING;
1✔
130
    pendingPicker = BUFFER_PICKER;
1✔
131

132
    if (newBalancerFactory.equals(currentBalancerFactory)) {
1✔
133
      return;
1✔
134
    }
135

136
    class PendingHelper extends ForwardingLoadBalancerHelper {
1✔
137
      LoadBalancer lb;
138

139
      @Override
140
      protected Helper delegate() {
141
        return helper;
1✔
142
      }
143

144
      @Override
145
      public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
146
        if (lb == pendingLb) {
1✔
147
          checkState(currentLbIsReady, "there's pending lb while current lb has been out of READY");
1✔
148
          pendingState = newState;
1✔
149
          pendingPicker = newPicker;
1✔
150
          if (newState == ConnectivityState.READY) {
1✔
151
            swap();
1✔
152
          }
153
        } else if (lb == currentLb) {
1✔
154
          currentLbIsReady = newState == ConnectivityState.READY;
1✔
155
          if (!currentLbIsReady && pendingLb != defaultBalancer) {
1✔
156
            swap(); // current policy exits READY, so swap
1✔
157
          } else {
158
            helper.updateBalancingState(newState, newPicker);
1✔
159
          }
160
        }
161
      }
1✔
162
    }
163

164
    PendingHelper pendingHelper = new PendingHelper();
1✔
165
    pendingHelper.lb = newBalancerFactory.newLoadBalancer(pendingHelper);
1✔
166
    pendingLb = pendingHelper.lb;
1✔
167
    pendingBalancerFactory = newBalancerFactory;
1✔
168
    if (!currentLbIsReady) {
1✔
169
      swap(); // the old policy is not READY at the moment, so swap to the new one right now
1✔
170
    }
171
  }
1✔
172

173
  private void swap() {
174
    helper.updateBalancingState(pendingState, pendingPicker);
1✔
175
    currentLb.shutdown();
1✔
176
    currentLb = pendingLb;
1✔
177
    currentBalancerFactory = pendingBalancerFactory;
1✔
178
    pendingLb = defaultBalancer;
1✔
179
    pendingBalancerFactory = null;
1✔
180
  }
1✔
181

182
  @Override
183
  protected LoadBalancer delegate() {
184
    return pendingLb == defaultBalancer ? currentLb : pendingLb;
1✔
185
  }
186

187
  @Override
188
  @Deprecated
189
  public void handleSubchannelState(
190
      Subchannel subchannel, ConnectivityStateInfo stateInfo) {
191
    throw new UnsupportedOperationException(
1✔
192
        "handleSubchannelState() is not supported by " + this.getClass().getName());
1✔
193
  }
194

195
  @Override
196
  public void shutdown() {
197
    pendingLb.shutdown();
1✔
198
    currentLb.shutdown();
1✔
199
  }
1✔
200

201
  public String delegateType() {
202
    return delegate().getClass().getSimpleName();
×
203
  }
204

205
  /**
206
   * Provided a JSON list of LoadBalancingConfigs, parse it into a config to pass to GracefulSwitch.
207
   */
208
  public static ConfigOrError parseLoadBalancingPolicyConfig(
209
      List<Map<String, ?>> loadBalancingConfigs) {
210
    return parseLoadBalancingPolicyConfig(
1✔
211
        loadBalancingConfigs, LoadBalancerRegistry.getDefaultRegistry());
1✔
212
  }
213

214
  /**
215
   * Provided a JSON list of LoadBalancingConfigs, parse it into a config to pass to GracefulSwitch.
216
   */
217
  public static ConfigOrError parseLoadBalancingPolicyConfig(
218
      List<Map<String, ?>> loadBalancingConfigs, LoadBalancerRegistry lbRegistry) {
219
    List<ServiceConfigUtil.LbConfig> childConfigCandidates =
1✔
220
        ServiceConfigUtil.unwrapLoadBalancingConfigList(loadBalancingConfigs);
1✔
221
    if (childConfigCandidates == null || childConfigCandidates.isEmpty()) {
1✔
222
      return ConfigOrError.fromError(
1✔
223
          Status.INTERNAL.withDescription("No child LB config specified"));
1✔
224
    }
225
    ConfigOrError selectedConfig =
1✔
226
        ServiceConfigUtil.selectLbPolicyFromList(childConfigCandidates, lbRegistry);
1✔
227
    if (selectedConfig.getError() != null) {
1✔
228
      Status error = selectedConfig.getError();
1✔
229
      return ConfigOrError.fromError(
1✔
230
          Status.INTERNAL
231
              .withCause(error.getCause())
1✔
232
              .withDescription(error.getDescription())
1✔
233
              .augmentDescription("Failed to select child config"));
1✔
234
    }
235
    ServiceConfigUtil.PolicySelection selection =
1✔
236
        (ServiceConfigUtil.PolicySelection) selectedConfig.getConfig();
1✔
237
    return ConfigOrError.fromConfig(
1✔
238
        createLoadBalancingPolicyConfig(selection.getProvider(), selection.getConfig()));
1✔
239
  }
240

241
  /**
242
   * Directly create a config to pass to GracefulSwitch. The object returned is the same as would be
243
   * found in {@code ConfigOrError.getConfig()}.
244
   */
245
  public static Object createLoadBalancingPolicyConfig(
246
      LoadBalancer.Factory childFactory, @Nullable Object childConfig) {
247
    return new Config(childFactory, childConfig);
1✔
248
  }
249

250
  static final class Config {
251
    final LoadBalancer.Factory childFactory;
252
    @Nullable
253
    final Object childConfig;
254

255
    public Config(LoadBalancer.Factory childFactory, @Nullable Object childConfig) {
1✔
256
      this.childFactory = checkNotNull(childFactory, "childFactory");
1✔
257
      this.childConfig = childConfig;
1✔
258
    }
1✔
259

260
    @Override
261
    public boolean equals(Object o) {
262
      if (this == o) {
1✔
263
        return true;
1✔
264
      }
265
      if (!(o instanceof Config)) {
1✔
266
        return false;
1✔
267
      }
268
      Config that = (Config) o;
1✔
269
      return Objects.equal(childFactory, that.childFactory)
1✔
270
          && Objects.equal(childConfig, that.childConfig);
1✔
271
    }
272

273
    @Override
274
    public int hashCode() {
275
      return Objects.hashCode(childFactory, childConfig);
1✔
276
    }
277

278
    @Override
279
    public String toString() {
280
      return MoreObjects.toStringHelper("GracefulSwitchLoadBalancer.Config")
1✔
281
          .add("childFactory", childFactory)
1✔
282
          .add("childConfig", childConfig)
1✔
283
          .toString();
1✔
284
    }
285
  }
286
}
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