xds: ClusterResolverLoadBalancer handle update for both resolved addresses and errors via ResolutionResult (#11997)

This commit is contained in:
Kannan J 2025-04-04 16:38:29 +00:00 committed by GitHub
parent edc2bf7346
commit a13fca2bf2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 140 additions and 81 deletions

View File

@ -33,6 +33,7 @@ import io.grpc.LoadBalancerRegistry;
import io.grpc.NameResolver; import io.grpc.NameResolver;
import io.grpc.NameResolver.ResolutionResult; import io.grpc.NameResolver.ResolutionResult;
import io.grpc.Status; import io.grpc.Status;
import io.grpc.StatusOr;
import io.grpc.SynchronizationContext; import io.grpc.SynchronizationContext;
import io.grpc.SynchronizationContext.ScheduledHandle; import io.grpc.SynchronizationContext.ScheduledHandle;
import io.grpc.internal.BackoffPolicy; import io.grpc.internal.BackoffPolicy;
@ -657,16 +658,21 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
@Override @Override
public void onResult(final ResolutionResult resolutionResult) { public void onResult(final ResolutionResult resolutionResult) {
class NameResolved implements Runnable { syncContext.execute(() -> onResult2(resolutionResult));
@Override }
public void run() {
if (shutdown) { @Override
return; public Status onResult2(final ResolutionResult resolutionResult) {
if (shutdown) {
return Status.OK;
} }
backoffPolicy = null; // reset backoff sequence if succeeded
// Arbitrary priority notation for all DNS-resolved endpoints. // Arbitrary priority notation for all DNS-resolved endpoints.
String priorityName = priorityName(name, 0); // value doesn't matter String priorityName = priorityName(name, 0); // value doesn't matter
List<EquivalentAddressGroup> addresses = new ArrayList<>(); List<EquivalentAddressGroup> addresses = new ArrayList<>();
StatusOr<List<EquivalentAddressGroup>> addressesOrError =
resolutionResult.getAddressesOrError();
if (addressesOrError.hasValue()) {
backoffPolicy = null; // reset backoff sequence if succeeded
for (EquivalentAddressGroup eag : resolutionResult.getAddresses()) { for (EquivalentAddressGroup eag : resolutionResult.getAddresses()) {
// No weight attribute is attached, all endpoint-level LB policy should be able // No weight attribute is attached, all endpoint-level LB policy should be able
// to handle such it. // to handle such it.
@ -687,17 +693,19 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
resolved = true; resolved = true;
result = new ClusterResolutionResult(addresses, priorityName, priorityChildConfig); result = new ClusterResolutionResult(addresses, priorityName, priorityChildConfig);
handleEndpointResourceUpdate(); handleEndpointResourceUpdate();
return Status.OK;
} else {
handleErrorInSyncContext(addressesOrError.getStatus());
return addressesOrError.getStatus();
} }
} }
syncContext.execute(new NameResolved());
}
@Override @Override
public void onError(final Status error) { public void onError(final Status error) {
syncContext.execute(new Runnable() { syncContext.execute(() -> handleErrorInSyncContext(error));
@Override }
public void run() {
private void handleErrorInSyncContext(final Status error) {
if (shutdown) { if (shutdown) {
return; return;
} }
@ -729,8 +737,6 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
new DelayedNameResolverRefresh(), delayNanos, TimeUnit.NANOSECONDS, new DelayedNameResolverRefresh(), delayNanos, TimeUnit.NANOSECONDS,
timeService); timeService);
} }
});
}
} }
} }
} }

View File

@ -200,6 +200,7 @@ public class ClusterResolverLoadBalancerTest {
private ArgumentCaptor<SubchannelPicker> pickerCaptor; private ArgumentCaptor<SubchannelPicker> pickerCaptor;
private int xdsClientRefs; private int xdsClientRefs;
private ClusterResolverLoadBalancer loadBalancer; private ClusterResolverLoadBalancer loadBalancer;
private NameResolverProvider fakeNameResolverProvider;
@Before @Before
public void setUp() throws URISyntaxException { public void setUp() throws URISyntaxException {
@ -216,7 +217,8 @@ public class ClusterResolverLoadBalancerTest {
.setServiceConfigParser(mock(ServiceConfigParser.class)) .setServiceConfigParser(mock(ServiceConfigParser.class))
.setChannelLogger(mock(ChannelLogger.class)) .setChannelLogger(mock(ChannelLogger.class))
.build(); .build();
nsRegistry.register(new FakeNameResolverProvider()); fakeNameResolverProvider = new FakeNameResolverProvider(false);
nsRegistry.register(fakeNameResolverProvider);
when(helper.getNameResolverRegistry()).thenReturn(nsRegistry); when(helper.getNameResolverRegistry()).thenReturn(nsRegistry);
when(helper.getNameResolverArgs()).thenReturn(args); when(helper.getNameResolverArgs()).thenReturn(args);
when(helper.getSynchronizationContext()).thenReturn(syncContext); when(helper.getSynchronizationContext()).thenReturn(syncContext);
@ -826,6 +828,17 @@ public class ClusterResolverLoadBalancerTest {
@Test @Test
public void onlyLogicalDnsCluster_endpointsResolved() { public void onlyLogicalDnsCluster_endpointsResolved() {
do_onlyLogicalDnsCluster_endpointsResolved();
}
@Test
public void oldListenerCallback_onlyLogicalDnsCluster_endpointsResolved() {
nsRegistry.deregister(fakeNameResolverProvider);
nsRegistry.register(new FakeNameResolverProvider(true));
do_onlyLogicalDnsCluster_endpointsResolved();
}
void do_onlyLogicalDnsCluster_endpointsResolved() {
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false); Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false);
deliverLbConfig(config); deliverLbConfig(config);
@ -854,7 +867,6 @@ public class ClusterResolverLoadBalancerTest {
.get(XdsAttributes.ATTR_ADDRESS_NAME)).isEqualTo(DNS_HOST_NAME); .get(XdsAttributes.ATTR_ADDRESS_NAME)).isEqualTo(DNS_HOST_NAME);
assertThat(childBalancer.addresses.get(1).getAttributes() assertThat(childBalancer.addresses.get(1).getAttributes()
.get(XdsAttributes.ATTR_ADDRESS_NAME)).isEqualTo(DNS_HOST_NAME); .get(XdsAttributes.ATTR_ADDRESS_NAME)).isEqualTo(DNS_HOST_NAME);
} }
@Test @Test
@ -874,7 +886,18 @@ public class ClusterResolverLoadBalancerTest {
} }
@Test @Test
public void onlyLogicalDnsCluster_resolutionError_backoffAndRefresh() { public void resolutionError_backoffAndRefresh() {
do_onlyLogicalDnsCluster_resolutionError_backoffAndRefresh();
}
@Test
public void oldListenerCallback_resolutionError_backoffAndRefresh() {
nsRegistry.deregister(fakeNameResolverProvider);
nsRegistry.register(new FakeNameResolverProvider(true));
do_onlyLogicalDnsCluster_resolutionError_backoffAndRefresh();
}
void do_onlyLogicalDnsCluster_resolutionError_backoffAndRefresh() {
InOrder inOrder = Mockito.inOrder(helper, backoffPolicyProvider, InOrder inOrder = Mockito.inOrder(helper, backoffPolicyProvider,
backoffPolicy1, backoffPolicy2); backoffPolicy1, backoffPolicy2);
ClusterResolverConfig config = new ClusterResolverConfig( ClusterResolverConfig config = new ClusterResolverConfig(
@ -1319,10 +1342,18 @@ public class ClusterResolverLoadBalancerTest {
} }
private class FakeNameResolverProvider extends NameResolverProvider { private class FakeNameResolverProvider extends NameResolverProvider {
private final boolean useOldListenerCallback;
private FakeNameResolverProvider(boolean useOldListenerCallback) {
this.useOldListenerCallback = useOldListenerCallback;
}
@Override @Override
public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) { public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
assertThat(targetUri.getScheme()).isEqualTo("dns"); assertThat(targetUri.getScheme()).isEqualTo("dns");
FakeNameResolver resolver = new FakeNameResolver(targetUri); FakeNameResolver resolver = useOldListenerCallback
? new FakeNameResolverUsingOldListenerCallback(targetUri)
: new FakeNameResolver(targetUri);
resolvers.add(resolver); resolvers.add(resolver);
return resolver; return resolver;
} }
@ -1343,9 +1374,10 @@ public class ClusterResolverLoadBalancerTest {
} }
} }
private class FakeNameResolver extends NameResolver { private class FakeNameResolver extends NameResolver {
private final URI targetUri; private final URI targetUri;
private Listener2 listener; protected Listener2 listener;
private int refreshCount; private int refreshCount;
private FakeNameResolver(URI targetUri) { private FakeNameResolver(URI targetUri) {
@ -1372,12 +1404,33 @@ public class ClusterResolverLoadBalancerTest {
resolvers.remove(this); resolvers.remove(this);
} }
private void deliverEndpointAddresses(List<EquivalentAddressGroup> addresses) { protected void deliverEndpointAddresses(List<EquivalentAddressGroup> addresses) {
syncContext.execute(() -> {
Status ret = listener.onResult2(ResolutionResult.newBuilder()
.setAddressesOrError(StatusOr.fromValue(addresses)).build());
assertThat(ret.getCode()).isEqualTo(Status.Code.OK);
});
}
protected void deliverError(Status error) {
syncContext.execute(() -> listener.onResult2(ResolutionResult.newBuilder()
.setAddressesOrError(StatusOr.fromStatus(error)).build()));
}
}
private class FakeNameResolverUsingOldListenerCallback extends FakeNameResolver {
private FakeNameResolverUsingOldListenerCallback(URI targetUri) {
super(targetUri);
}
@Override
protected void deliverEndpointAddresses(List<EquivalentAddressGroup> addresses) {
listener.onResult(ResolutionResult.newBuilder() listener.onResult(ResolutionResult.newBuilder()
.setAddressesOrError(StatusOr.fromValue(addresses)).build()); .setAddressesOrError(StatusOr.fromValue(addresses)).build());
} }
private void deliverError(Status error) { @Override
protected void deliverError(Status error) {
listener.onError(error); listener.onError(error);
} }
} }