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

grpc / grpc-java / #19167

22 Apr 2024 02:48PM UTC coverage: 88.084% (-0.01%) from 88.096%
#19167

push

github

ejona86
util: Remove deactivation and GracefulSwitchLb from MultiChildLb

It is easy to manage these things outside of MultiChildLb and it makes
the shared code easier and use less memory. In particular, we don't want
to use many instances of GracefulSwitchLb in virtually every policy
simply because it was needed in one or two cases.

31195 of 35415 relevant lines covered (88.08%)

0.88 hits per line

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

94.92
/../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 io.grpc.ConnectivityState;
24
import io.grpc.ConnectivityStateInfo;
25
import io.grpc.ExperimentalApi;
26
import io.grpc.LoadBalancer;
27
import io.grpc.Status;
28
import javax.annotation.Nullable;
29
import javax.annotation.concurrent.NotThreadSafe;
30

31
/**
32
 * A load balancer that gracefully swaps to a new lb policy. If the channel is currently in a state
33
 * other than READY, the new policy will be swapped into place immediately.  Otherwise, the channel
34
 * will keep using the old policy until the new policy reports READY or the old policy exits READY.
35
 *
36
 * <p>The balancer must {@link #switchTo(LoadBalancer.Factory) switch to} a policy prior to {@link
37
 * LoadBalancer#handleResolvedAddresses(ResolvedAddresses) handling resolved addresses} for the
38
 * first time.
39
 */
40
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/5999")
41
@NotThreadSafe // Must be accessed in SynchronizationContext
42
public final class GracefulSwitchLoadBalancer extends ForwardingLoadBalancer {
43
  private final LoadBalancer defaultBalancer = new LoadBalancer() {
1✔
44
    @Override
45
    public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
46
      //  Most LB policies using this class will receive child policy configuration within the
47
      //  service config, so they are naturally calling switchTo() just before
48
      //  handleResolvedAddresses(), within their own handleResolvedAddresses(). If switchTo() is
49
      //  not called immediately after construction that does open up potential for bugs in the
50
      //  parent policies, where they fail to call switchTo(). So we will use the exception to try
51
      //  to notice those bugs quickly, as it will fail very loudly.
52
      throw new IllegalStateException(
×
53
          "GracefulSwitchLoadBalancer must switch to a load balancing policy before handling"
54
              + " ResolvedAddresses");
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
  /**
101
   * Gracefully switch to a new policy defined by the given factory, if the given factory isn't
102
   * equal to the current one.
103
   */
104
  public void switchTo(LoadBalancer.Factory newBalancerFactory) {
105
    checkNotNull(newBalancerFactory, "newBalancerFactory");
1✔
106

107
    if (newBalancerFactory.equals(pendingBalancerFactory)) {
1✔
108
      return;
1✔
109
    }
110
    pendingLb.shutdown();
1✔
111
    pendingLb = defaultBalancer;
1✔
112
    pendingBalancerFactory = null;
1✔
113
    pendingState = ConnectivityState.CONNECTING;
1✔
114
    pendingPicker = BUFFER_PICKER;
1✔
115

116
    if (newBalancerFactory.equals(currentBalancerFactory)) {
1✔
117
      return;
1✔
118
    }
119

120
    class PendingHelper extends ForwardingLoadBalancerHelper {
1✔
121
      LoadBalancer lb;
122

123
      @Override
124
      protected Helper delegate() {
125
        return helper;
1✔
126
      }
127

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

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

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

166
  @Override
167
  protected LoadBalancer delegate() {
168
    return pendingLb == defaultBalancer ? currentLb : pendingLb;
1✔
169
  }
170

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

179
  @Override
180
  public void shutdown() {
181
    pendingLb.shutdown();
1✔
182
    currentLb.shutdown();
1✔
183
  }
1✔
184

185
  public String delegateType() {
186
    return delegate().getClass().getSimpleName();
×
187
  }
188
}
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