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

grpc / grpc-java / #19754

28 Mar 2025 07:49PM UTC coverage: 88.598% (-0.003%) from 88.601%
#19754

push

github

web-flow
util: Replace BUFFER_PICKER with FixedResultPicker

I think at some point there were more usages in the tests. But now it
is pretty easy.

PriorityLb.ChildLbState.picker is initialized to
FixedResultPicker(NoResult). So now that GracefulSwitchLb is using the
same picker, equals() is able to de-dup an update.

34609 of 39063 relevant lines covered (88.6%)

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
  @Override
88
  public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
89
    Config config = (Config) resolvedAddresses.getLoadBalancingPolicyConfig();
1✔
90
    switchToInternal(config.childFactory);
1✔
91
    delegate().handleResolvedAddresses(
1✔
92
        resolvedAddresses.toBuilder()
1✔
93
          .setLoadBalancingPolicyConfig(config.childConfig)
1✔
94
          .build());
1✔
95
  }
1✔
96

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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