xds: populate envoy RetryPolicy with no retryOn to resolver (#8511)

Envoy RetryPolicy with empty retryOn should not be ignored as no retry config when selecting Route config. Therefore, if xDS update for a route contains a RetryPolicy that has no RetryOn value that we support, but the virtual host config does, xds client should choose the Envoy RetryPolicy from the route (even with no RetryOn), rather than choosing the one from virtual host, and try to convert it into grpc RetryPolicy, and end up with no retry.
This commit is contained in:
ZHANG Dapeng 2021-09-13 08:31:00 -07:00 committed by GitHub
parent 7a65c74283
commit 9ff54059d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 22 additions and 11 deletions

View File

@ -1273,14 +1273,11 @@ final class ClientXdsClient extends AbstractXdsClient {
retryableStatusCodesBuilder.add(code); retryableStatusCodesBuilder.add(code);
} }
List<Code> retryableStatusCodes = retryableStatusCodesBuilder.build(); List<Code> retryableStatusCodes = retryableStatusCodesBuilder.build();
if (!retryableStatusCodes.isEmpty()) {
return StructOrError.fromStruct( return StructOrError.fromStruct(
RetryPolicy.create( RetryPolicy.create(
maxAttempts, retryableStatusCodes, initialBackoff, maxBackoff, maxAttempts, retryableStatusCodes, initialBackoff, maxBackoff,
/* perAttemptRecvTimeout= */ null)); /* perAttemptRecvTimeout= */ null));
} }
return null;
}
@VisibleForTesting @VisibleForTesting
static StructOrError<ClusterWeight> parseClusterWeight( static StructOrError<ClusterWeight> parseClusterWeight(

View File

@ -182,13 +182,14 @@ final class XdsNameResolver extends NameResolver {
@VisibleForTesting @VisibleForTesting
static Map<String, ?> generateServiceConfigWithMethodConfig( static Map<String, ?> generateServiceConfigWithMethodConfig(
@Nullable Long timeoutNano, @Nullable RetryPolicy retryPolicy) { @Nullable Long timeoutNano, @Nullable RetryPolicy retryPolicy) {
if (timeoutNano == null && retryPolicy == null) { if (timeoutNano == null
&& (retryPolicy == null || retryPolicy.retryableStatusCodes().isEmpty())) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
ImmutableMap.Builder<String, Object> methodConfig = ImmutableMap.builder(); ImmutableMap.Builder<String, Object> methodConfig = ImmutableMap.builder();
methodConfig.put( methodConfig.put(
"name", Collections.singletonList(Collections.emptyMap())); "name", Collections.singletonList(Collections.emptyMap()));
if (retryPolicy != null) { if (retryPolicy != null && !retryPolicy.retryableStatusCodes().isEmpty()) {
ImmutableMap.Builder<String, Object> rawRetryPolicy = ImmutableMap.builder(); ImmutableMap.Builder<String, Object> rawRetryPolicy = ImmutableMap.builder();
rawRetryPolicy.put("maxAttempts", (double) retryPolicy.maxAttempts()); rawRetryPolicy.put("maxAttempts", (double) retryPolicy.maxAttempts());
rawRetryPolicy.put("initialBackoff", Durations.toString(retryPolicy.initialBackoff())); rawRetryPolicy.put("initialBackoff", Durations.toString(retryPolicy.initialBackoff()));

View File

@ -552,7 +552,8 @@ public class ClientXdsClientDataTest {
.setRetryPolicy(builder.build()) .setRetryPolicy(builder.build())
.build(); .build();
struct = ClientXdsClient.parseRouteAction(proto, filterRegistry, false); struct = ClientXdsClient.parseRouteAction(proto, filterRegistry, false);
assertThat(struct.getStruct().retryPolicy()).isNull(); assertThat(struct.getStruct().retryPolicy()).isNotNull();
assertThat(struct.getStruct().retryPolicy().retryableStatusCodes()).isEmpty();
// base_interval unset // base_interval unset
builder builder

View File

@ -988,6 +988,8 @@ public class XdsNameResolverTest {
RetryPolicy retryPolicy = RetryPolicy.create( RetryPolicy retryPolicy = RetryPolicy.create(
4, ImmutableList.of(Code.UNAVAILABLE, Code.CANCELLED), Durations.fromMillis(100), 4, ImmutableList.of(Code.UNAVAILABLE, Code.CANCELLED), Durations.fromMillis(100),
Durations.fromMillis(200), null); Durations.fromMillis(200), null);
RetryPolicy retryPolicyWithEmptyStatusCodes = RetryPolicy.create(
4, ImmutableList.<Code>of(), Durations.fromMillis(100), Durations.fromMillis(200), null);
// timeout only // timeout only
String expectedServiceConfigJson = "{\n" String expectedServiceConfigJson = "{\n"
@ -1001,6 +1003,11 @@ public class XdsNameResolverTest {
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(timeoutNano, null)) assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(timeoutNano, null))
.isEqualTo(expectedServiceConfig); .isEqualTo(expectedServiceConfig);
// timeout and retry with empty retriable status codes
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(
timeoutNano, retryPolicyWithEmptyStatusCodes))
.isEqualTo(expectedServiceConfig);
// retry only // retry only
expectedServiceConfigJson = "{\n" expectedServiceConfigJson = "{\n"
+ " \"methodConfig\": [{\n" + " \"methodConfig\": [{\n"
@ -1021,6 +1028,7 @@ public class XdsNameResolverTest {
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(null, retryPolicy)) assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(null, retryPolicy))
.isEqualTo(expectedServiceConfig); .isEqualTo(expectedServiceConfig);
// timeout and retry // timeout and retry
expectedServiceConfigJson = "{\n" expectedServiceConfigJson = "{\n"
+ " \"methodConfig\": [{\n" + " \"methodConfig\": [{\n"
@ -1043,12 +1051,16 @@ public class XdsNameResolverTest {
.isEqualTo(expectedServiceConfig); .isEqualTo(expectedServiceConfig);
// no timeout and no retry // no timeout and no retry
// timeout and retry
expectedServiceConfigJson = "{}"; expectedServiceConfigJson = "{}";
expectedServiceConfig = expectedServiceConfig =
(Map<String, ?>) JsonParser.parse(expectedServiceConfigJson); (Map<String, ?>) JsonParser.parse(expectedServiceConfigJson);
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(null, null)) assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(null, null))
.isEqualTo(expectedServiceConfig); .isEqualTo(expectedServiceConfig);
// retry with emtry retriable status codes only
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(
null, retryPolicyWithEmptyStatusCodes))
.isEqualTo(expectedServiceConfig);
} }
@Test @Test