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.
This commit is contained in:
Eric Anderson 2024-07-25 15:25:42 -07:00
parent 4ab34229fb
commit a6f8ebf33d
6 changed files with 48 additions and 52 deletions

View File

@ -456,8 +456,6 @@ public abstract class MultiChildLoadBalancer extends LoadBalancer {
/** /**
* Update current state and picker for this child and then use * Update current state and picker for this child and then use
* {@link #updateOverallBalancingState()} for the parent LB. * {@link #updateOverallBalancingState()} for the parent LB.
*
* <p/>Override this if you don't want to automatically request a connection when in IDLE
*/ */
@Override @Override
public void updateBalancingState(final ConnectivityState newState, public void updateBalancingState(final ConnectivityState newState,
@ -471,9 +469,6 @@ public abstract class MultiChildLoadBalancer extends LoadBalancer {
// If we are already in the process of resolving addresses, the overall balancing state // If we are already in the process of resolving addresses, the overall balancing state
// will be updated at the end of it, and we don't need to trigger that update here. // will be updated at the end of it, and we don't need to trigger that update here.
if (!resolvingAddresses) { if (!resolvingAddresses) {
if (newState == IDLE) {
lb.requestConnection();
}
updateOverallBalancingState(); updateOverallBalancingState();
} }
} }

View File

@ -95,6 +95,25 @@ final class RoundRobinLoadBalancer extends MultiChildLoadBalancer {
return new ReadyPicker(pickerList, sequence); return new ReadyPicker(pickerList, sequence);
} }
@Override
protected ChildLbState createChildLbState(Object key, Object policyConfig,
SubchannelPicker initialPicker, ResolvedAddresses resolvedAddresses) {
return new ChildLbState(key, pickFirstLbProvider, policyConfig, initialPicker) {
@Override
protected ChildLbStateHelper createChildHelper() {
return new ChildLbStateHelper() {
@Override
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
super.updateBalancingState(newState, newPicker);
if (!resolvingAddresses && newState == IDLE) {
getLb().requestConnection();
}
}
};
}
};
}
@VisibleForTesting @VisibleForTesting
static class ReadyPicker extends SubchannelPicker { static class ReadyPicker extends SubchannelPicker {
private final List<SubchannelPicker> subchannelPickers; // non-empty private final List<SubchannelPicker> subchannelPickers; // non-empty

View File

@ -328,5 +328,18 @@ final class LeastRequestLoadBalancer extends MultiChildLoadBalancer {
int getActiveRequests() { int getActiveRequests() {
return activeRequests.get(); return activeRequests.get();
} }
@Override
protected ChildLbStateHelper createChildHelper() {
return new ChildLbStateHelper() {
@Override
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
super.updateBalancingState(newState, newPicker);
if (!resolvingAddresses && newState == IDLE) {
getLb().requestConnection();
}
}
};
}
} }
} }

View File

@ -229,7 +229,7 @@ final class RingHashLoadBalancer extends MultiChildLoadBalancer {
@Override @Override
protected ChildLbState createChildLbState(Object key, Object policyConfig, protected ChildLbState createChildLbState(Object key, Object policyConfig,
SubchannelPicker initialPicker, ResolvedAddresses resolvedAddresses) { SubchannelPicker initialPicker, ResolvedAddresses resolvedAddresses) {
return new RingHashChildLbState((Endpoint)key); return new ChildLbState(key, lazyLbFactory, null, EMPTY_PICKER);
} }
private Status validateAddrList(List<EquivalentAddressGroup> addrList) { private Status validateAddrList(List<EquivalentAddressGroup> addrList) {
@ -358,7 +358,7 @@ final class RingHashLoadBalancer extends MultiChildLoadBalancer {
this.ring = ring; this.ring = ring;
pickableSubchannels = new HashMap<>(subchannels.size()); pickableSubchannels = new HashMap<>(subchannels.size());
for (Map.Entry<Object, ChildLbState> entry : subchannels.entrySet()) { for (Map.Entry<Object, ChildLbState> entry : subchannels.entrySet()) {
RingHashChildLbState childLbState = (RingHashChildLbState) entry.getValue(); ChildLbState childLbState = entry.getValue();
pickableSubchannels.put((Endpoint)entry.getKey(), pickableSubchannels.put((Endpoint)entry.getKey(),
new SubchannelView(childLbState, childLbState.getCurrentState())); new SubchannelView(childLbState, childLbState.getCurrentState()));
} }
@ -405,7 +405,7 @@ final class RingHashLoadBalancer extends MultiChildLoadBalancer {
for (int i = 0; i < ring.size(); i++) { for (int i = 0; i < ring.size(); i++) {
int index = (targetIndex + i) % ring.size(); int index = (targetIndex + i) % ring.size();
SubchannelView subchannelView = pickableSubchannels.get(ring.get(index).addrKey); SubchannelView subchannelView = pickableSubchannels.get(ring.get(index).addrKey);
RingHashChildLbState childLbState = subchannelView.childLbState; ChildLbState childLbState = subchannelView.childLbState;
if (subchannelView.connectivityState == READY) { if (subchannelView.connectivityState == READY) {
return childLbState.getCurrentPicker().pickSubchannel(args); return childLbState.getCurrentPicker().pickSubchannel(args);
@ -427,7 +427,7 @@ final class RingHashLoadBalancer extends MultiChildLoadBalancer {
} }
// return the pick from the original subchannel hit by hash, which is probably an error // return the pick from the original subchannel hit by hash, which is probably an error
RingHashChildLbState originalSubchannel = ChildLbState originalSubchannel =
pickableSubchannels.get(ring.get(targetIndex).addrKey).childLbState; pickableSubchannels.get(ring.get(targetIndex).addrKey).childLbState;
return originalSubchannel.getCurrentPicker().pickSubchannel(args); return originalSubchannel.getCurrentPicker().pickSubchannel(args);
} }
@ -439,10 +439,10 @@ final class RingHashLoadBalancer extends MultiChildLoadBalancer {
* state changes. * state changes.
*/ */
private static final class SubchannelView { private static final class SubchannelView {
private final RingHashChildLbState childLbState; private final ChildLbState childLbState;
private final ConnectivityState connectivityState; private final ConnectivityState connectivityState;
private SubchannelView(RingHashChildLbState childLbState, ConnectivityState state) { private SubchannelView(ChildLbState childLbState, ConnectivityState state) {
this.childLbState = childLbState; this.childLbState = childLbState;
this.connectivityState = state; this.connectivityState = state;
} }
@ -487,41 +487,4 @@ final class RingHashLoadBalancer extends MultiChildLoadBalancer {
.toString(); .toString();
} }
} }
class RingHashChildLbState extends MultiChildLoadBalancer.ChildLbState {
public RingHashChildLbState(Endpoint key) {
super(key, lazyLbFactory, null, EMPTY_PICKER);
}
@Override
protected ChildLbStateHelper createChildHelper() {
return new RingHashChildHelper();
}
// Need to expose this to the LB class
@Override
protected void shutdown() {
super.shutdown();
}
private class RingHashChildHelper extends ChildLbStateHelper {
@Override
public void updateBalancingState(final ConnectivityState newState,
final SubchannelPicker newPicker) {
setCurrentState(newState);
setCurrentPicker(newPicker);
if (getChildLbState(getKey()) == null) {
return;
}
// If we are already in the process of resolving addresses, the overall balancing state
// will be updated at the end of it, and we don't need to trigger that update here.
if (!resolvingAddresses) {
updateOverallBalancingState();
}
}
}
}
} }

View File

@ -340,6 +340,14 @@ final class WeightedRoundRobinLoadBalancer extends MultiChildLoadBalancer {
public Subchannel createSubchannel(CreateSubchannelArgs args) { public Subchannel createSubchannel(CreateSubchannelArgs args) {
return new WrrSubchannel(super.createSubchannel(args), WeightedChildLbState.this); return new WrrSubchannel(super.createSubchannel(args), WeightedChildLbState.this);
} }
@Override
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
super.updateBalancingState(newState, newPicker);
if (!resolvingAddresses && newState == ConnectivityState.IDLE) {
getLb().requestConnection();
}
}
} }
final class OrcaReportListener implements OrcaPerRequestReportListener, OrcaOobReportListener { final class OrcaReportListener implements OrcaPerRequestReportListener, OrcaOobReportListener {

View File

@ -66,7 +66,6 @@ import io.grpc.internal.PickSubchannelArgsImpl;
import io.grpc.testing.TestMethodDescriptors; import io.grpc.testing.TestMethodDescriptors;
import io.grpc.util.AbstractTestHelper; import io.grpc.util.AbstractTestHelper;
import io.grpc.util.MultiChildLoadBalancer.ChildLbState; import io.grpc.util.MultiChildLoadBalancer.ChildLbState;
import io.grpc.xds.RingHashLoadBalancer.RingHashChildLbState;
import io.grpc.xds.RingHashLoadBalancer.RingHashConfig; import io.grpc.xds.RingHashLoadBalancer.RingHashConfig;
import java.lang.Thread.UncaughtExceptionHandler; import java.lang.Thread.UncaughtExceptionHandler;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -177,8 +176,7 @@ public class RingHashLoadBalancerTest {
assertThat(addressesAcceptanceStatus.isOk()).isTrue(); assertThat(addressesAcceptanceStatus.isOk()).isTrue();
verify(helper).updateBalancingState(eq(IDLE), pickerCaptor.capture()); verify(helper).updateBalancingState(eq(IDLE), pickerCaptor.capture());
RingHashChildLbState childLbState = ChildLbState childLbState = loadBalancer.getChildLbStates().iterator().next();
(RingHashChildLbState) loadBalancer.getChildLbStates().iterator().next();
assertThat(subchannels.get(Collections.singletonList(childLbState.getEag()))).isNull(); assertThat(subchannels.get(Collections.singletonList(childLbState.getEag()))).isNull();
// Picking subchannel triggers connection. // Picking subchannel triggers connection.
@ -422,7 +420,7 @@ public class RingHashLoadBalancerTest {
assertThat(addressesAcceptanceStatus.isOk()).isTrue(); assertThat(addressesAcceptanceStatus.isOk()).isTrue();
// Create subchannel for the first address // Create subchannel for the first address
((RingHashChildLbState) loadBalancer.getChildLbStateEag(servers.get(0))).getCurrentPicker() loadBalancer.getChildLbStateEag(servers.get(0)).getCurrentPicker()
.pickSubchannel(getDefaultPickSubchannelArgs(hashFunc.hashVoid())); .pickSubchannel(getDefaultPickSubchannelArgs(hashFunc.hashVoid()));
verifyConnection(1); verifyConnection(1);