Skip to content

Commit

Permalink
xds: weighted target to delay picker updates while updating children
Browse files Browse the repository at this point in the history
  • Loading branch information
temawi committed Jun 22, 2022
1 parent 8e77936 commit fc222a0
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 14 deletions.
36 changes: 22 additions & 14 deletions xds/src/main/java/io/grpc/xds/WeightedTargetLoadBalancer.java
Expand Up @@ -28,7 +28,6 @@
import io.grpc.InternalLogId;
import io.grpc.LoadBalancer;
import io.grpc.Status;
import io.grpc.SynchronizationContext;
import io.grpc.util.ForwardingLoadBalancerHelper;
import io.grpc.util.GracefulSwitchLoadBalancer;
import io.grpc.xds.WeightedRandomPicker.WeightedChildPicker;
Expand All @@ -49,20 +48,29 @@ final class WeightedTargetLoadBalancer extends LoadBalancer {
private final Map<String, GracefulSwitchLoadBalancer> childBalancers = new HashMap<>();
private final Map<String, ChildHelper> childHelpers = new HashMap<>();
private final Helper helper;
private final SynchronizationContext syncContext;

private Map<String, WeightedPolicySelection> targets = ImmutableMap.of();
// Set to true if currently in the process of handling resolved addresses.
private boolean resolvingAddresses;

WeightedTargetLoadBalancer(Helper helper) {
this.helper = checkNotNull(helper, "helper");
this.syncContext = checkNotNull(helper.getSynchronizationContext(), "syncContext");
logger = XdsLogger.withLogId(
InternalLogId.allocate("weighted-target-lb", helper.getAuthority()));
logger.log(XdsLogLevel.INFO, "Created");
}

@Override
public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
try {
resolvingAddresses = true;
handleResolvedAddressesInternal(resolvedAddresses);
} finally {
resolvingAddresses = false;
}
}

public void handleResolvedAddressesInternal(ResolvedAddresses resolvedAddresses) {
logger.log(XdsLogLevel.DEBUG, "Received resolution result: {0}", resolvedAddresses);
Object lbConfig = resolvedAddresses.getLoadBalancingPolicyConfig();
checkNotNull(lbConfig, "missing weighted_target lb config");
Expand Down Expand Up @@ -191,17 +199,17 @@ private ChildHelper(String name) {
@Override
public void updateBalancingState(final ConnectivityState newState,
final SubchannelPicker newPicker) {
syncContext.execute(new Runnable() {
@Override
public void run() {
if (!childBalancers.containsKey(name)) {
return;
}
currentState = newState;
currentPicker = newPicker;
updateOverallBalancingState();
}
});
if (!childBalancers.containsKey(name)) {
return;
}
currentState = newState;
currentPicker = newPicker;

// 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();
}
}

@Override
Expand Down
69 changes: 69 additions & 0 deletions xds/src/test/java/io/grpc/xds/WeightedTargetLoadBalancerTest.java
Expand Up @@ -402,4 +402,73 @@ public void raceBetweenShutdownAndChildLbBalancingStateUpdate() {
weightedChildHelper0.updateBalancingState(READY, mock(SubchannelPicker.class));
verifyNoMoreInteractions(helper);
}

// When the ChildHelper is asked to update the overall balancing state, it should not do that if
// the update was triggered by the parent LB that will handle triggering the overall state update.
@Test
public void noDuplicateOverallBalancingStateUpdate() {
FakeLoadBalancerProvider fakeLbProvider = new FakeLoadBalancerProvider();

Map<String, WeightedPolicySelection> targets = ImmutableMap.of(
"target0", new WeightedPolicySelection(
weights[0], new PolicySelection(fakeLbProvider, configs[0])),
"target3", new WeightedPolicySelection(
weights[3], new PolicySelection(fakeLbProvider, configs[3])));
weightedTargetLb.handleResolvedAddresses(
ResolvedAddresses.newBuilder()
.setAddresses(ImmutableList.<EquivalentAddressGroup>of())
.setLoadBalancingPolicyConfig(new WeightedTargetConfig(targets))
.build());

// Both of the two child LB policies will call the helper to update the balancing state.
// But since those calls happen during the handling of teh resolved addresses of the parent
// WeightedTargetLLoadBalancer, the overall balancing state should only be updated once.
verify(helper, times(1)).updateBalancingState(any(), any());

}

private static class FakeLoadBalancerProvider extends LoadBalancerProvider {

@Override
public boolean isAvailable() {
return true;
}

@Override
public int getPriority() {
return 5;
}

@Override
public String getPolicyName() {
return "foo";
}

@Override
public LoadBalancer newLoadBalancer(Helper helper) {
return new FakeLoadBalancer(helper);
}
}

static class FakeLoadBalancer extends LoadBalancer {

private Helper helper;

FakeLoadBalancer(Helper helper) {
this.helper = helper;
}

@Override
public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
helper.updateBalancingState(TRANSIENT_FAILURE, new ErrorPicker(Status.INTERNAL));
}

@Override
public void handleNameResolutionError(Status error) {
}

@Override
public void shutdown() {
}
}
}

0 comments on commit fc222a0

Please sign in to comment.