Skip to content

Commit cec9ee3

Browse files
authored
api: move SubchannelPicker.requestConnection() to LoadBalancer. (#5751)
We will require Subchannel.requestConnection() to be called from sync-context (#5722), but SubchannelPicker.requestConnection() is currently calling it with the assumption of thread-safety. Actually SubchannelPicker.requestConnection() is called already from sync-context by ChannelImpl, it makes more sense to move this method to LoadBalancer where all other methods are sync-context'ed, rather than making SubchannelPicker.requestConnection() sync-context'ed and fragmenting the SubchannelPicker API because pickSubchannel() is thread-safe. C++ also has the requestConnection() equivalent on their LoadBalancer interface.
1 parent f3bf250 commit cec9ee3

File tree

10 files changed

+61
-38
lines changed

10 files changed

+61
-38
lines changed

api/src/main/java/io/grpc/LoadBalancer.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,19 @@ public boolean canHandleEmptyAddressListFromNameResolution() {
362362
return false;
363363
}
364364

365+
/**
366+
* The channel asks the LoadBalancer to establish connections now (if applicable) so that the
367+
* upcoming RPC may then just pick a ready connection without waiting for connections. This
368+
* is triggered by {@link ManagedChannel#getState ManagedChannel.getState(true)}.
369+
*
370+
* <p>If LoadBalancer doesn't override it, this is no-op. If it infeasible to create connections
371+
* given the current state, e.g. no Subchannel has been created yet, LoadBalancer can ignore this
372+
* request.
373+
*
374+
* @since 1.22.0
375+
*/
376+
public void requestConnection() {}
377+
365378
/**
366379
* The main balancing logic. It <strong>must be thread-safe</strong>. Typically it should only
367380
* synchronize on its own state, and avoid synchronizing with the LoadBalancer's state.
@@ -385,8 +398,10 @@ public abstract static class SubchannelPicker {
385398
*
386399
* <p>No-op if unsupported.
387400
*
401+
* @deprecated override {@link LoadBalancer#requestConnection} instead.
388402
* @since 1.11.0
389403
*/
404+
@Deprecated
390405
public void requestConnection() {}
391406
}
392407

core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ public boolean canHandleEmptyAddressListFromNameResolution() {
175175
return true;
176176
}
177177

178+
@Override
179+
public void requestConnection() {
180+
getDelegate().requestConnection();
181+
}
182+
178183
@Override
179184
public void shutdown() {
180185
delegate.shutdown();

core/src/main/java/io/grpc/internal/ManagedChannelImpl.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,7 @@ private void maybeTerminateChannel() {
882882
}
883883

884884
@Override
885+
@SuppressWarnings("deprecation")
885886
public ConnectivityState getState(boolean requestConnection) {
886887
ConnectivityState savedChannelState = channelStateManager.getState();
887888
if (requestConnection && savedChannelState == IDLE) {
@@ -892,6 +893,9 @@ public void run() {
892893
if (subchannelPicker != null) {
893894
subchannelPicker.requestConnection();
894895
}
896+
if (lbHelper != null) {
897+
lbHelper.lb.requestConnection();
898+
}
895899
}
896900
}
897901

core/src/main/java/io/grpc/internal/PickFirstLoadBalancer.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ public void shutdown() {
110110
}
111111
}
112112

113+
@Override
114+
public void requestConnection() {
115+
if (subchannel != null) {
116+
subchannel.requestConnection();
117+
}
118+
}
119+
113120
/**
114121
* No-op picker which doesn't add any custom picking logic. It just passes already known result
115122
* received in constructor.
@@ -140,10 +147,5 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
140147
subchannel.requestConnection();
141148
return PickResult.withNoResult();
142149
}
143-
144-
@Override
145-
public void requestConnection() {
146-
subchannel.requestConnection();
147-
}
148150
}
149151
}

core/src/main/java/io/grpc/util/ForwardingLoadBalancer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ public boolean canHandleEmptyAddressListFromNameResolution() {
6767
return delegate().canHandleEmptyAddressListFromNameResolution();
6868
}
6969

70+
@Override
71+
public void requestConnection() {
72+
delegate().requestConnection();
73+
}
74+
7075
@Override
7176
public String toString() {
7277
return MoreObjects.toStringHelper(this).add("delegate", delegate()).toString();

core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1920,6 +1920,7 @@ public void getState_withRequestConnect() {
19201920
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
19211921
}
19221922

1923+
@SuppressWarnings("deprecation")
19231924
@Test
19241925
public void getState_withRequestConnect_IdleWithLbRunning() {
19251926
channelBuilder.nameResolverFactory(
@@ -1932,6 +1933,7 @@ public void getState_withRequestConnect_IdleWithLbRunning() {
19321933
assertEquals(IDLE, channel.getState(true));
19331934
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
19341935
verify(mockPicker).requestConnection();
1936+
verify(mockLoadBalancer).requestConnection();
19351937
}
19361938

19371939
@Test

core/src/test/java/io/grpc/internal/PickFirstLoadBalancerTest.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,18 @@ public void nameResolutionErrorWithStateChanges() throws Exception {
262262

263263
@Test
264264
public void requestConnection() {
265+
loadBalancer.requestConnection();
266+
verify(mockSubchannel, never()).requestConnection();
267+
265268
loadBalancer.handleResolvedAddresses(
266269
ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(affinity).build());
270+
verify(mockSubchannel).requestConnection();
271+
267272
loadBalancer.handleSubchannelState(mockSubchannel, ConnectivityStateInfo.forNonError(IDLE));
268-
verify(mockHelper).updateBalancingState(eq(IDLE), pickerCaptor.capture());
269-
SubchannelPicker picker = pickerCaptor.getValue();
273+
verify(mockHelper).updateBalancingState(eq(IDLE), any(SubchannelPicker.class));
270274

271275
verify(mockSubchannel).requestConnection();
272-
picker.requestConnection();
276+
loadBalancer.requestConnection();
273277
verify(mockSubchannel, times(2)).requestConnection();
274278
}
275279

grpclb/src/main/java/io/grpc/grpclb/GrpclbLoadBalancer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
115115
grpclbState.handleAddresses(newLbAddressGroups, newBackendServers);
116116
}
117117

118+
@Override
119+
public void requestConnection() {
120+
if (grpclbState != null) {
121+
grpclbState.requestConnection();
122+
}
123+
}
124+
118125
@VisibleForTesting
119126
static Mode retrieveModeFromLbConfig(
120127
@Nullable Map<String, ?> rawLbConfigValue, ChannelLogger channelLogger) {

grpclb/src/main/java/io/grpc/grpclb/GrpclbState.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,14 @@ void handleAddresses(
224224
maybeUpdatePicker();
225225
}
226226

227+
void requestConnection() {
228+
for (RoundRobinEntry entry : currentPicker.pickList) {
229+
if (entry instanceof IdleSubchannelEntry) {
230+
((IdleSubchannelEntry) entry).subchannel.requestConnection();
231+
}
232+
}
233+
}
234+
227235
private void maybeUseFallbackBackends() {
228236
if (balancerWorking) {
229237
return;
@@ -1025,13 +1033,5 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
10251033
}
10261034
}
10271035

1028-
@Override
1029-
public void requestConnection() {
1030-
for (RoundRobinEntry entry : pickList) {
1031-
if (entry instanceof IdleSubchannelEntry) {
1032-
((IdleSubchannelEntry) entry).subchannel.requestConnection();
1033-
}
1034-
}
1035-
}
10361036
}
10371037
}

grpclb/src/test/java/io/grpc/grpclb/GrpclbLoadBalancerTest.java

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@
7171
import io.grpc.grpclb.GrpclbState.ErrorEntry;
7272
import io.grpc.grpclb.GrpclbState.IdleSubchannelEntry;
7373
import io.grpc.grpclb.GrpclbState.Mode;
74-
import io.grpc.grpclb.GrpclbState.RoundRobinEntry;
7574
import io.grpc.grpclb.GrpclbState.RoundRobinPicker;
7675
import io.grpc.inprocess.InProcessChannelBuilder;
7776
import io.grpc.inprocess.InProcessServerBuilder;
@@ -455,26 +454,6 @@ public void roundRobinPickerWithIdleEntry_andDrop() {
455454
verify(subchannel, times(2)).requestConnection();
456455
}
457456

458-
@Test
459-
public void roundRobinPicker_requestConnection() {
460-
// requestConnection() on RoundRobinPicker is only passed to IdleSubchannelEntry
461-
462-
Subchannel subchannel1 = mock(Subchannel.class);
463-
Subchannel subchannel2 = mock(Subchannel.class);
464-
465-
RoundRobinPicker picker = new RoundRobinPicker(
466-
Collections.<DropEntry>emptyList(),
467-
Arrays.<RoundRobinEntry>asList(
468-
new BackendEntry(subchannel1), new IdleSubchannelEntry(subchannel2),
469-
new ErrorEntry(Status.UNAVAILABLE)));
470-
471-
verify(subchannel2, never()).requestConnection();
472-
473-
picker.requestConnection();
474-
verify(subchannel2).requestConnection();
475-
verify(subchannel1, never()).requestConnection();
476-
}
477-
478457
@Test
479458
public void loadReporting() {
480459
Metadata headers = new Metadata();
@@ -1815,7 +1794,7 @@ public void grpclbWorking_pickFirstMode() throws Exception {
18151794
verify(subchannel).requestConnection();
18161795

18171796
// ... or requested by application
1818-
picker5.requestConnection();
1797+
balancer.requestConnection();
18191798
verify(subchannel, times(2)).requestConnection();
18201799

18211800
// PICK_FIRST doesn't use subchannelPool

0 commit comments

Comments
 (0)