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

grpc / grpc-java / #19440

29 Aug 2024 03:04PM UTC coverage: 84.491% (-0.002%) from 84.493%
#19440

push

github

ejona86
util: Remove child policy config from MultiChildLB state

The child policy config should be refreshed every address update, so it
shouldn't be stored in the ChildLbState. In addition, none of the
current usages actually used what was stored in the ChildLbState in a
meaningful way (it was always null).

ResolvedAddresses was also removed from createChildLbState(), as nothing
in it should be needed for creation; it varies over time and the values
passed at creation are immutable.

33402 of 39533 relevant lines covered (84.49%)

0.84 hits per line

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

91.94
/../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 FixedResultPicker(PickResult.withNoResult());
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 FixedResultPicker(PickResult.withNoResult()));
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) {
100
    return new ChildLbState(key, pickFirstLbProvider) {
1✔
101
      @Override
102
      protected ChildLbStateHelper createChildHelper() {
103
        return new ChildLbStateHelper() {
1✔
104
          @Override
105
          public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
106
            super.updateBalancingState(newState, newPicker);
1✔
107
            if (!resolvingAddresses && newState == IDLE) {
1✔
108
              getLb().requestConnection();
1✔
109
            }
110
          }
1✔
111
        };
112
      }
113
    };
114
  }
115

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

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

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

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

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

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

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

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

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