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

grpc / grpc-java / #19418

12 Aug 2024 10:40PM UTC coverage: 84.489% (+0.02%) from 84.469%
#19418

push

github

ejona86
Remove implicit requestConnection() on IDLE from MultiChildLB

One LB no longer needs to extend ChildLbState and one has to start, so
it is a bit of a wash. There are more LBs that need the auto-request
logic, but if we have an API where subclasses override it without
calling super then we can't change the implementation in the future.
Adding behavior on top of a base class allows subclasses to call super,
which lets the base class change over time.

33396 of 39527 relevant lines covered (84.49%)

0.84 hits per line

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

89.39
/../util/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java
1
/*
2
 * Copyright 2016 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.checkArgument;
20
import static io.grpc.ConnectivityState.CONNECTING;
21
import static io.grpc.ConnectivityState.IDLE;
22
import static io.grpc.ConnectivityState.READY;
23
import static io.grpc.ConnectivityState.TRANSIENT_FAILURE;
24

25
import com.google.common.annotations.VisibleForTesting;
26
import com.google.common.base.MoreObjects;
27
import com.google.common.base.Preconditions;
28
import io.grpc.ConnectivityState;
29
import io.grpc.EquivalentAddressGroup;
30
import io.grpc.LoadBalancer;
31
import io.grpc.NameResolver;
32
import java.util.ArrayList;
33
import java.util.Collection;
34
import java.util.HashSet;
35
import java.util.List;
36
import java.util.Random;
37
import java.util.concurrent.atomic.AtomicInteger;
38

39
/**
40
 * A {@link LoadBalancer} that provides round-robin load-balancing over the {@link
41
 * EquivalentAddressGroup}s from the {@link NameResolver}.
42
 */
43
final class RoundRobinLoadBalancer extends MultiChildLoadBalancer {
44
  private final AtomicInteger sequence = new AtomicInteger(new Random().nextInt());
1✔
45
  private SubchannelPicker currentPicker = new EmptyPicker();
1✔
46

47
  public RoundRobinLoadBalancer(Helper helper) {
48
    super(helper);
1✔
49
  }
1✔
50

51
  /**
52
   * Updates picker with the list of active subchannels (state == READY).
53
   */
54
  @Override
55
  protected void updateOverallBalancingState() {
56
    List<ChildLbState> activeList = getReadyChildren();
1✔
57
    if (activeList.isEmpty()) {
1✔
58
      // No READY subchannels
59

60
      // RRLB will request connection immediately on subchannel IDLE.
61
      boolean isConnecting = false;
1✔
62
      for (ChildLbState childLbState : getChildLbStates()) {
1✔
63
        ConnectivityState state = childLbState.getCurrentState();
1✔
64
        if (state == CONNECTING || state == IDLE) {
1✔
65
          isConnecting = true;
1✔
66
          break;
1✔
67
        }
68
      }
1✔
69

70
      if (isConnecting) {
1✔
71
        updateBalancingState(CONNECTING, new EmptyPicker());
1✔
72
      } else {
73
        updateBalancingState(TRANSIENT_FAILURE, createReadyPicker(getChildLbStates()));
1✔
74
      }
75
    } else {
1✔
76
      updateBalancingState(READY, createReadyPicker(activeList));
1✔
77
    }
78
  }
1✔
79

80
  private void updateBalancingState(ConnectivityState state, SubchannelPicker picker) {
81
    if (state != currentConnectivityState || !picker.equals(currentPicker)) {
1✔
82
      getHelper().updateBalancingState(state, picker);
1✔
83
      currentConnectivityState = state;
1✔
84
      currentPicker = picker;
1✔
85
    }
86
  }
1✔
87

88
  private SubchannelPicker createReadyPicker(Collection<ChildLbState> children) {
89
    List<SubchannelPicker> pickerList = new ArrayList<>();
1✔
90
    for (ChildLbState child : children) {
1✔
91
      SubchannelPicker picker = child.getCurrentPicker();
1✔
92
      pickerList.add(picker);
1✔
93
    }
1✔
94

95
    return new ReadyPicker(pickerList, sequence);
1✔
96
  }
97

98
  @Override
99
  protected ChildLbState createChildLbState(Object key, Object policyConfig,
100
      SubchannelPicker initialPicker, ResolvedAddresses resolvedAddresses) {
101
    return new ChildLbState(key, pickFirstLbProvider, policyConfig, initialPicker) {
1✔
102
      @Override
103
      protected ChildLbStateHelper createChildHelper() {
104
        return new ChildLbStateHelper() {
1✔
105
          @Override
106
          public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
107
            super.updateBalancingState(newState, newPicker);
1✔
108
            if (!resolvingAddresses && newState == IDLE) {
1✔
109
              getLb().requestConnection();
1✔
110
            }
111
          }
1✔
112
        };
113
      }
114
    };
115
  }
116

117
  @VisibleForTesting
118
  static class ReadyPicker extends SubchannelPicker {
119
    private final List<SubchannelPicker> subchannelPickers; // non-empty
120
    private final AtomicInteger index;
121
    private final int hashCode;
122

123
    public ReadyPicker(List<SubchannelPicker> list, AtomicInteger index) {
1✔
124
      checkArgument(!list.isEmpty(), "empty list");
1✔
125
      this.subchannelPickers = list;
1✔
126
      this.index = Preconditions.checkNotNull(index, "index");
1✔
127

128
      // Every created picker is checked for equality in updateBalancingState() at least once.
129
      // Pre-compute the hash so it can be checked cheaply. Using the hash in equals() makes it very
130
      // fast except when the pickers are (very likely) equal.
131
      //
132
      // For equality we treat children as a set; use hash code as defined by Set
133
      int sum = 0;
1✔
134
      for (SubchannelPicker picker : subchannelPickers) {
1✔
135
        sum += picker.hashCode();
1✔
136
      }
1✔
137
      this.hashCode = sum;
1✔
138
    }
1✔
139

140
    @Override
141
    public PickResult pickSubchannel(PickSubchannelArgs args) {
142
      return subchannelPickers.get(nextIndex()).pickSubchannel(args);
1✔
143
    }
144

145
    @Override
146
    public String toString() {
147
      return MoreObjects.toStringHelper(ReadyPicker.class)
×
148
          .add("subchannelPickers", subchannelPickers)
×
149
          .toString();
×
150
    }
151

152
    private int nextIndex() {
153
      int i = index.getAndIncrement() & Integer.MAX_VALUE;
1✔
154
      return i % subchannelPickers.size();
1✔
155
    }
156

157
    @VisibleForTesting
158
    List<SubchannelPicker> getSubchannelPickers() {
159
      return subchannelPickers;
1✔
160
    }
161

162
    @Override
163
    public int hashCode() {
164
      return hashCode;
×
165
    }
166

167
    @Override
168
    public boolean equals(Object o) {
169
      if (!(o instanceof ReadyPicker)) {
1✔
170
        return false;
1✔
171
      }
172
      ReadyPicker other = (ReadyPicker) o;
1✔
173
      if (other == this) {
1✔
174
        return true;
×
175
      }
176
      // the lists cannot contain duplicate subchannels
177
      return hashCode == other.hashCode
1✔
178
          && index == other.index
179
          && subchannelPickers.size() == other.subchannelPickers.size()
1✔
180
          && new HashSet<>(subchannelPickers).containsAll(other.subchannelPickers);
1✔
181
    }
182
  }
183

184
  @VisibleForTesting
185
  static final class EmptyPicker extends SubchannelPicker {
1✔
186
    @Override
187
    public PickResult pickSubchannel(PickSubchannelArgs args) {
188
      return PickResult.withNoResult();
×
189
    }
190

191
    @Override
192
    public int hashCode() {
193
      return getClass().hashCode();
×
194
    }
195

196
    @Override
197
    public boolean equals(Object o) {
198
      return o instanceof EmptyPicker;
1✔
199
    }
200
  }
201
}
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