Skip to content

Commit 8237ae2

Browse files
committed
util: Remove EAG conveniences from MultiChildLb
This is a step toward removing ResolvedAddresses from ChildLbState, which isn't actually used by MultiChildLb. Most usages of the EAG usages can be served more directly without peering into MultiChildLb's internals or even accessing ChildLbStates, which make the tests less sensitive to implementation changes. Some changes do leverage the new behavior of MultiChildLb where it preserves the order of the entries. This does fix an important bug in shutdown tests. The tests looped over the ChildLbStates after shutdown, but shutdown deleted all the children so it looped over an entry collection. Fixing that exposed that deliverSubchannelState() didn't function after shutdown, as the listener was removed from the map when the subchannel was shut down. Moving the listener onto the TestSubchannel allowed having access to the listener even after shutdown. A few places in LeastRequestLb lines were just deleted, but that's because an existing assertion already provided the same check but without digging into MultiChildLb.
1 parent 546efd7 commit 8237ae2

File tree

8 files changed

+134
-243
lines changed

8 files changed

+134
-243
lines changed

util/src/main/java/io/grpc/util/MultiChildLoadBalancer.java

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import static io.grpc.ConnectivityState.TRANSIENT_FAILURE;
2525

2626
import com.google.common.annotations.VisibleForTesting;
27-
import com.google.common.base.Objects;
2827
import com.google.common.collect.Maps;
2928
import io.grpc.Attributes;
3029
import io.grpc.ConnectivityState;
@@ -237,21 +236,6 @@ public final Collection<ChildLbState> getChildLbStates() {
237236
return childLbStates;
238237
}
239238

240-
@VisibleForTesting
241-
public final ChildLbState getChildLbState(Object key) {
242-
for (ChildLbState state : childLbStates) {
243-
if (Objects.equal(state.getKey(), key)) {
244-
return state;
245-
}
246-
}
247-
return null;
248-
}
249-
250-
@VisibleForTesting
251-
public final ChildLbState getChildLbStateEag(EquivalentAddressGroup eag) {
252-
return getChildLbState(new Endpoint(eag));
253-
}
254-
255239
/**
256240
* Filters out non-ready child load balancers (subchannels).
257241
*/
@@ -337,13 +321,6 @@ protected final void setCurrentPicker(SubchannelPicker newPicker) {
337321
currentPicker = newPicker;
338322
}
339323

340-
public final EquivalentAddressGroup getEag() {
341-
if (resolvedAddresses == null || resolvedAddresses.getAddresses().isEmpty()) {
342-
return null;
343-
}
344-
return resolvedAddresses.getAddresses().get(0);
345-
}
346-
347324
protected final void setResolvedAddresses(ResolvedAddresses newAddresses) {
348325
checkNotNull(newAddresses, "Missing address list for child");
349326
resolvedAddresses = newAddresses;

util/src/test/java/io/grpc/util/MultiChildLoadBalancerTest.java

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
import java.util.List;
5353
import java.util.Map;
5454
import java.util.concurrent.ConcurrentHashMap;
55-
import java.util.stream.Collectors;
5655
import org.junit.Before;
5756
import org.junit.Rule;
5857
import org.junit.Test;
@@ -153,8 +152,7 @@ public void pickAfterResolvedUpdatedHosts() {
153152
LoadBalancer.Subchannel removedSubchannel = getSubchannel(removedEag);
154153
LoadBalancer.Subchannel oldSubchannel = getSubchannel(oldEag1);
155154
LoadBalancer.SubchannelStateListener removedListener =
156-
testHelperInst.getSubchannelStateListeners()
157-
.get(testHelperInst.getRealForMockSubChannel(removedSubchannel));
155+
testHelperInst.getSubchannelStateListener(removedSubchannel);
158156

159157
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
160158

@@ -168,8 +166,6 @@ public void pickAfterResolvedUpdatedHosts() {
168166
verify(removedSubchannel, times(1)).requestConnection();
169167
verify(oldSubchannel, times(1)).requestConnection();
170168

171-
assertThat(getChildEags(loadBalancer)).containsExactly(removedEag, oldEag1);
172-
173169
// This time with Attributes
174170
List<EquivalentAddressGroup> latestServers = Lists.newArrayList(oldEag2, newEag);
175171

@@ -186,10 +182,10 @@ public void pickAfterResolvedUpdatedHosts() {
186182
removedListener.onSubchannelState(ConnectivityStateInfo.forNonError(SHUTDOWN));
187183
deliverSubchannelState(newSubchannel, ConnectivityStateInfo.forNonError(READY));
188184

189-
assertThat(getChildEags(loadBalancer)).containsExactly(oldEag2, newEag);
190-
191185
verify(mockHelper, times(3)).createSubchannel(any(LoadBalancer.CreateSubchannelArgs.class));
192186
inOrder.verify(mockHelper, times(2)).updateBalancingState(eq(READY), pickerCaptor.capture());
187+
picker = pickerCaptor.getValue();
188+
assertThat(getList(picker)).containsExactly(oldSubchannel, newSubchannel);
193189

194190
AbstractTestHelper.verifyNoMoreMeaningfulInteractions(mockHelper);
195191
}
@@ -328,12 +324,6 @@ private LoadBalancer.Subchannel getSubchannel(EquivalentAddressGroup eag) {
328324
return null;
329325
}
330326

331-
private static List<Object> getChildEags(MultiChildLoadBalancer loadBalancer) {
332-
return loadBalancer.getChildLbStates().stream()
333-
.map(ChildLbState::getEag)
334-
.collect(Collectors.toList());
335-
}
336-
337327
private void deliverSubchannelState(LoadBalancer.Subchannel subchannel,
338328
ConnectivityStateInfo newState) {
339329
testHelperInst.deliverSubchannelState(subchannel, newState);
@@ -348,13 +338,16 @@ protected TestLb(Helper mockHelper) {
348338
protected void updateOverallBalancingState() {
349339
ConnectivityState overallState = null;
350340
final Map<Object, SubchannelPicker> childPickers = new HashMap<>();
341+
final Map<Object, ConnectivityState> childConnStates = new HashMap<>();
351342
for (ChildLbState childLbState : getChildLbStates()) {
352343
childPickers.put(childLbState.getKey(), childLbState.getCurrentPicker());
344+
childConnStates.put(childLbState.getKey(), childLbState.getCurrentState());
353345
overallState = aggregateState(overallState, childLbState.getCurrentState());
354346
}
355347

356348
if (overallState != null) {
357-
getHelper().updateBalancingState(overallState, new TestSubchannelPicker(childPickers));
349+
getHelper().updateBalancingState(
350+
overallState, new TestSubchannelPicker(childPickers, childConnStates));
358351
currentConnectivityState = overallState;
359352
}
360353

@@ -364,18 +357,17 @@ private class TestSubchannelPicker extends SubchannelPicker {
364357
Map<Object, SubchannelPicker> childPickerMap;
365358
Map<Object, ConnectivityState> childStates = new HashMap<>();
366359

367-
TestSubchannelPicker(Map<Object, SubchannelPicker> childPickers) {
368-
childPickerMap = childPickers;
369-
for (Object key : childPickerMap.keySet()) {
370-
childStates.put(key, getChildLbState(key).getCurrentState());
371-
}
360+
TestSubchannelPicker(
361+
Map<Object, SubchannelPicker> childPickers, Map<Object, ConnectivityState> childStates) {
362+
this.childPickerMap = childPickers;
363+
this.childStates = childStates;
372364
}
373365

374366
List<Subchannel> getReadySubchannels() {
375367
List<Subchannel> readySubchannels = new ArrayList<>();
376368
for ( Map.Entry<Object, ConnectivityState> cur : childStates.entrySet()) {
377369
if (cur.getValue() == READY) {
378-
Subchannel s = subchannels.get(Arrays.asList(getChildLbState(cur.getKey()).getEag()));
370+
Subchannel s = childPickerMap.get(cur.getKey()).pickSubchannel(null).getSubchannel();
379371
readySubchannels.add(s);
380372
}
381373
}

util/src/test/java/io/grpc/util/RoundRobinLoadBalancerTest.java

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
import java.util.ArrayList;
6262
import java.util.Arrays;
6363
import java.util.Collections;
64-
import java.util.HashMap;
6564
import java.util.Iterator;
6665
import java.util.List;
6766
import java.util.Map;
@@ -207,10 +206,6 @@ public void pickAfterResolvedUpdatedHosts() throws Exception {
207206
assertThat(childLbState.getResolvedAddresses().getAttributes().get(IS_PETIOLE_POLICY))
208207
.isTrue();
209208
}
210-
assertThat(loadBalancer.getChildLbStateEag(removedEag).getCurrentPicker().pickSubchannel(null)
211-
.getSubchannel()).isEqualTo(removedSubchannel);
212-
assertThat(loadBalancer.getChildLbStateEag(oldEag1).getCurrentPicker().pickSubchannel(null)
213-
.getSubchannel()).isEqualTo(oldSubchannel);
214209

215210
// This time with Attributes
216211
List<EquivalentAddressGroup> latestServers = Lists.newArrayList(oldEag2, newEag);
@@ -225,12 +220,6 @@ public void pickAfterResolvedUpdatedHosts() throws Exception {
225220

226221
deliverSubchannelState(newSubchannel, ConnectivityStateInfo.forNonError(READY));
227222

228-
assertThat(loadBalancer.getChildLbStates().size()).isEqualTo(2);
229-
assertThat(loadBalancer.getChildLbStateEag(newEag).getCurrentPicker()
230-
.pickSubchannel(null).getSubchannel()).isEqualTo(newSubchannel);
231-
assertThat(loadBalancer.getChildLbStateEag(oldEag2).getCurrentPicker()
232-
.pickSubchannel(null).getSubchannel()).isEqualTo(oldSubchannel);
233-
234223
verify(mockHelper, times(6)).createSubchannel(any(CreateSubchannelArgs.class));
235224
inOrder.verify(mockHelper, times(2)).updateBalancingState(eq(READY), pickerCaptor.capture());
236225

@@ -243,35 +232,35 @@ public void pickAfterResolvedUpdatedHosts() throws Exception {
243232
@Test
244233
public void pickAfterStateChange() throws Exception {
245234
InOrder inOrder = inOrder(mockHelper);
246-
Status addressesAcceptanceStatus = acceptAddresses(servers, Attributes.EMPTY);
235+
Status addressesAcceptanceStatus =
236+
acceptAddresses(Arrays.asList(servers.get(0)), Attributes.EMPTY);
247237
assertThat(addressesAcceptanceStatus.isOk()).isTrue();
238+
inOrder.verify(mockHelper).createSubchannel(any(CreateSubchannelArgs.class));
248239

249240
// TODO figure out if this method testing the right things
250241

251-
ChildLbState childLbState = loadBalancer.getChildLbStates().iterator().next();
252-
Subchannel subchannel = subchannels.get(Arrays.asList(childLbState.getEag()));
242+
assertThat(subchannels).hasSize(1);
243+
Subchannel subchannel = subchannels.values().iterator().next();
253244

254245
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), eq(EMPTY_PICKER));
255-
assertThat(childLbState.getCurrentState()).isEqualTo(CONNECTING);
256246

257247
deliverSubchannelState(subchannel, ConnectivityStateInfo.forNonError(READY));
258248
inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture());
259249
assertThat(pickerCaptor.getValue()).isInstanceOf(ReadyPicker.class);
260-
assertThat(childLbState.getCurrentState()).isEqualTo(READY);
261250

262251
Status error = Status.UNKNOWN.withDescription(\\_(ツ)_//¯");
263252
deliverSubchannelState(subchannel,
264253
ConnectivityStateInfo.forTransientFailure(error));
265-
assertThat(childLbState.getCurrentState()).isEqualTo(TRANSIENT_FAILURE);
266-
AbstractTestHelper.refreshInvokedAndUpdateBS(inOrder, CONNECTING, mockHelper, pickerCaptor);
267-
assertThat(pickerCaptor.getValue()).isEqualTo(EMPTY_PICKER);
254+
AbstractTestHelper.refreshInvokedAndUpdateBS(
255+
inOrder, TRANSIENT_FAILURE, mockHelper, pickerCaptor);
256+
assertThat(pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus()).isEqualTo(error);
268257

269258
deliverSubchannelState(subchannel, ConnectivityStateInfo.forNonError(IDLE));
270259
inOrder.verify(mockHelper).refreshNameResolution();
271-
assertThat(childLbState.getCurrentState()).isEqualTo(TRANSIENT_FAILURE);
260+
inOrder.verify(mockHelper, never())
261+
.updateBalancingState(eq(TRANSIENT_FAILURE), any(SubchannelPicker.class));
272262

273263
verify(subchannel, atLeastOnce()).requestConnection();
274-
verify(mockHelper, times(3)).createSubchannel(any(CreateSubchannelArgs.class));
275264
AbstractTestHelper.verifyNoMoreMeaningfulInteractions(mockHelper);
276265
}
277266

@@ -282,10 +271,10 @@ public void ignoreShutdownSubchannelStateChange() {
282271
assertThat(addressesAcceptanceStatus.isOk()).isTrue();
283272
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), eq(EMPTY_PICKER));
284273

274+
List<Subchannel> savedSubchannels = new ArrayList<>(subchannels.values());
285275
loadBalancer.shutdown();
286-
for (ChildLbState child : loadBalancer.getChildLbStates()) {
287-
Subchannel sc = child.getCurrentPicker().pickSubchannel(null).getSubchannel();
288-
verify(child).shutdown();
276+
for (Subchannel sc : savedSubchannels) {
277+
verify(sc).shutdown();
289278
// When the subchannel is being shut down, a SHUTDOWN connectivity state is delivered
290279
// back to the subchannel state listener.
291280
deliverSubchannelState(sc, ConnectivityStateInfo.forNonError(SHUTDOWN));
@@ -300,34 +289,27 @@ public void stayTransientFailureUntilReady() {
300289
Status addressesAcceptanceStatus = acceptAddresses(servers, Attributes.EMPTY);
301290
assertThat(addressesAcceptanceStatus.isOk()).isTrue();
302291

292+
inOrder.verify(mockHelper, times(3)).createSubchannel(any(CreateSubchannelArgs.class));
303293
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), eq(EMPTY_PICKER));
304294

305-
Map<ChildLbState, Subchannel> childToSubChannelMap = new HashMap<>();
306295
// Simulate state transitions for each subchannel individually.
307-
for ( ChildLbState child : loadBalancer.getChildLbStates()) {
308-
Subchannel sc = subchannels.get(Arrays.asList(child.getEag()));
309-
childToSubChannelMap.put(child, sc);
296+
for (Subchannel sc : subchannels.values()) {
310297
Status error = Status.UNKNOWN.withDescription("connection broken");
311298
deliverSubchannelState(
312299
sc,
313300
ConnectivityStateInfo.forTransientFailure(error));
314-
assertEquals(TRANSIENT_FAILURE, child.getCurrentState());
315301
deliverSubchannelState(
316302
sc,
317303
ConnectivityStateInfo.forNonError(CONNECTING));
318-
assertEquals(TRANSIENT_FAILURE, child.getCurrentState());
319304
}
320305
inOrder.verify(mockHelper).updateBalancingState(eq(TRANSIENT_FAILURE), isA(ReadyPicker.class));
321306
inOrder.verify(mockHelper, atLeast(0)).refreshNameResolution();
322307
inOrder.verifyNoMoreInteractions();
323308

324-
ChildLbState child = loadBalancer.getChildLbStates().iterator().next();
325-
Subchannel subchannel = childToSubChannelMap.get(child);
309+
Subchannel subchannel = subchannels.values().iterator().next();
326310
deliverSubchannelState(subchannel, ConnectivityStateInfo.forNonError(READY));
327-
assertThat(child.getCurrentState()).isEqualTo(READY);
328311
inOrder.verify(mockHelper).updateBalancingState(eq(READY), isA(ReadyPicker.class));
329312

330-
verify(mockHelper, times(3)).createSubchannel(any(CreateSubchannelArgs.class));
331313
inOrder.verify(mockHelper, atLeast(0)).refreshNameResolution();
332314
inOrder.verifyNoMoreInteractions();
333315
}
@@ -342,8 +324,7 @@ public void refreshNameResolutionWhenSubchannelConnectionBroken() {
342324
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), eq(EMPTY_PICKER));
343325

344326
// Simulate state transitions for each subchannel individually.
345-
for (ChildLbState child : loadBalancer.getChildLbStates()) {
346-
Subchannel sc = subchannels.get(Arrays.asList(child.getEag()));
327+
for (Subchannel sc : subchannels.values()) {
347328
verify(sc).requestConnection();
348329
deliverSubchannelState(sc, ConnectivityStateInfo.forNonError(CONNECTING));
349330
Status error = Status.UNKNOWN.withDescription("connection broken");

util/src/testFixtures/java/io/grpc/util/AbstractTestHelper.java

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616

1717
package io.grpc.util;
1818

19+
import static com.google.common.base.Preconditions.checkNotNull;
1920
import static org.mockito.AdditionalAnswers.delegatesTo;
2021
import static org.mockito.ArgumentMatchers.eq;
2122
import static org.mockito.Mockito.atLeast;
2223
import static org.mockito.Mockito.mock;
2324
import static org.mockito.Mockito.verify;
2425
import static org.mockito.Mockito.verifyNoMoreInteractions;
2526

26-
import com.google.common.collect.Maps;
2727
import io.grpc.Attributes;
2828
import io.grpc.Channel;
2929
import io.grpc.ChannelLogger;
@@ -63,10 +63,8 @@
6363
*/
6464
public abstract class AbstractTestHelper extends ForwardingLoadBalancerHelper {
6565

66-
private final Map<Subchannel, Subchannel> mockToRealSubChannelMap = new HashMap<>();
66+
private final Map<Subchannel, TestSubchannel> mockToRealSubChannelMap = new HashMap<>();
6767
protected final Map<Subchannel, Subchannel> realToMockSubChannelMap = new HashMap<>();
68-
private final Map<Subchannel, SubchannelStateListener> subchannelStateListeners =
69-
Maps.newLinkedHashMap();
7068
private final FakeClock fakeClock;
7169
private final SynchronizationContext syncContext;
7270

@@ -87,22 +85,14 @@ public AbstractTestHelper(FakeClock fakeClock, SynchronizationContext syncContex
8785
this.syncContext = syncContext;
8886
}
8987

90-
public Map<Subchannel, Subchannel> getMockToRealSubChannelMap() {
91-
return mockToRealSubChannelMap;
92-
}
93-
94-
public Subchannel getRealForMockSubChannel(Subchannel mock) {
95-
Subchannel realSc = getMockToRealSubChannelMap().get(mock);
88+
private TestSubchannel getRealForMockSubChannel(Subchannel mock) {
89+
TestSubchannel realSc = mockToRealSubChannelMap.get(mock);
9690
if (realSc == null) {
97-
realSc = mock;
91+
realSc = (TestSubchannel) mock;
9892
}
9993
return realSc;
10094
}
10195

102-
public Map<Subchannel, SubchannelStateListener> getSubchannelStateListeners() {
103-
return subchannelStateListeners;
104-
}
105-
10696
public static final FakeClock.TaskFilter NOT_START_NEXT_CONNECTION =
10797
new FakeClock.TaskFilter() {
10898
@Override
@@ -116,15 +106,15 @@ public static int getNumFilteredPendingTasks(FakeClock fakeClock) {
116106
}
117107

118108
public void deliverSubchannelState(Subchannel subchannel, ConnectivityStateInfo newState) {
119-
Subchannel realSc = getMockToRealSubChannelMap().get(subchannel);
120-
if (realSc == null) {
121-
realSc = subchannel;
122-
}
123-
SubchannelStateListener listener = getSubchannelStateListeners().get(realSc);
109+
getSubchannelStateListener(subchannel).onSubchannelState(newState);
110+
}
111+
112+
public SubchannelStateListener getSubchannelStateListener(Subchannel subchannel) {
113+
SubchannelStateListener listener = getRealForMockSubChannel(subchannel).listener;
124114
if (listener == null) {
125-
throw new IllegalArgumentException("subchannel does not have a matching listener");
115+
throw new IllegalArgumentException("subchannel has not been started");
126116
}
127-
listener.onSubchannelState(newState);
117+
return listener;
128118
}
129119

130120
@Override
@@ -144,7 +134,7 @@ public Subchannel createSubchannel(CreateSubchannelArgs args) {
144134
TestSubchannel delegate = createRealSubchannel(args);
145135
subchannel = mock(Subchannel.class, delegatesTo(delegate));
146136
getSubchannelMap().put(args.getAddresses(), subchannel);
147-
getMockToRealSubChannelMap().put(subchannel, delegate);
137+
mockToRealSubChannelMap.put(subchannel, delegate);
148138
realToMockSubChannelMap.put(delegate, subchannel);
149139
}
150140

@@ -161,7 +151,7 @@ public void refreshNameResolution() {
161151
}
162152

163153
public void setChannel(Subchannel subchannel, Channel channel) {
164-
((TestSubchannel)subchannel).channel = channel;
154+
getRealForMockSubChannel(subchannel).channel = channel;
165155
}
166156

167157
@Override
@@ -208,6 +198,7 @@ public static void verifyNoMoreMeaningfulInteractions(Helper helper, InOrder inO
208198

209199
protected class TestSubchannel extends ForwardingSubchannel {
210200
CreateSubchannelArgs args;
201+
SubchannelStateListener listener;
211202
Channel channel;
212203

213204
public TestSubchannel(CreateSubchannelArgs args) {
@@ -250,12 +241,11 @@ public void updateAddresses(List<EquivalentAddressGroup> addrs) {
250241

251242
@Override
252243
public void start(SubchannelStateListener listener) {
253-
getSubchannelStateListeners().put(this, listener);
244+
this.listener = checkNotNull(listener, "listener");
254245
}
255246

256247
@Override
257248
public void shutdown() {
258-
getSubchannelStateListeners().remove(this);
259249
for (EquivalentAddressGroup eag : getAllAddresses()) {
260250
getSubchannelMap().remove(Collections.singletonList(eag));
261251
}

0 commit comments

Comments
 (0)