use of io.grpc.xds.VirtualHost.Route.RouteAction.RetryPolicy in project grpc-java by grpc.
the class XdsNameResolverTest method retryPolicyInPerMethodConfigGeneratedByResolverIsValid.
@Test
public void retryPolicyInPerMethodConfigGeneratedByResolverIsValid() {
ServiceConfigParser realParser = new ScParser(true, 5, 5, new AutoConfiguredLoadBalancerFactory("pick-first"));
resolver = new XdsNameResolver(null, AUTHORITY, realParser, syncContext, scheduler, xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null);
resolver.start(mockListener);
FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient();
RetryPolicy retryPolicy = RetryPolicy.create(4, ImmutableList.of(Code.UNAVAILABLE), Durations.fromMillis(100), Durations.fromMillis(200), null);
xdsClient.deliverLdsUpdate(Collections.singletonList(Route.forAction(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), RouteAction.forCluster(cluster1, Collections.<HashPolicy>emptyList(), null, retryPolicy), ImmutableMap.<String, FilterConfig>of())));
verify(mockListener).onResult(resolutionResultCaptor.capture());
ResolutionResult result = resolutionResultCaptor.getValue();
InternalConfigSelector configSelector = result.getAttributes().get(InternalConfigSelector.KEY);
Result selectResult = configSelector.selectConfig(new PickSubchannelArgsImpl(call1.methodDescriptor, new Metadata(), CallOptions.DEFAULT));
Object config = selectResult.getConfig();
// Purely validating the data (io.grpc.internal.RetryPolicy).
// However, there's no public accessor methods the data object.
assertThat(config.getClass().getName()).isEqualTo("io.grpc.internal.ManagedChannelServiceConfig");
assertThat(config.toString()).contains(MoreObjects.toStringHelper("RetryPolicy").add("maxAttempts", 4).add("initialBackoffNanos", TimeUnit.MILLISECONDS.toNanos(100)).add("maxBackoffNanos", TimeUnit.MILLISECONDS.toNanos(200)).add("backoffMultiplier", 2D).add("perAttemptRecvTimeoutNanos", null).add("retryableStatusCodes", ImmutableList.of(Code.UNAVAILABLE)).toString());
}
use of io.grpc.xds.VirtualHost.Route.RouteAction.RetryPolicy in project grpc-java by grpc.
the class XdsNameResolverTest method generateServiceConfig_forPerMethodConfig.
@SuppressWarnings("unchecked")
@Test
public void generateServiceConfig_forPerMethodConfig() throws IOException {
// 1.0000000001s
long timeoutNano = TimeUnit.SECONDS.toNanos(1L) + 1L;
RetryPolicy retryPolicy = RetryPolicy.create(4, ImmutableList.of(Code.UNAVAILABLE, Code.CANCELLED), Durations.fromMillis(100), Durations.fromMillis(200), null);
RetryPolicy retryPolicyWithEmptyStatusCodes = RetryPolicy.create(4, ImmutableList.<Code>of(), Durations.fromMillis(100), Durations.fromMillis(200), null);
// timeout only
String expectedServiceConfigJson = "{\n" + " \"methodConfig\": [{\n" + " \"name\": [ {} ],\n" + " \"timeout\": \"1.000000001s\"\n" + " }]\n" + "}";
Map<String, ?> expectedServiceConfig = (Map<String, ?>) JsonParser.parse(expectedServiceConfigJson);
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(timeoutNano, null)).isEqualTo(expectedServiceConfig);
// timeout and retry with empty retriable status codes
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(timeoutNano, retryPolicyWithEmptyStatusCodes)).isEqualTo(expectedServiceConfig);
// retry only
expectedServiceConfigJson = "{\n" + " \"methodConfig\": [{\n" + " \"name\": [ {} ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\" : 4,\n" + " \"initialBackoff\": \"0.100s\",\n" + " \"maxBackoff\": \"0.200s\",\n" + " \"backoffMultiplier\": 2,\n" + " \"retryableStatusCodes\": [\n" + " \"UNAVAILABLE\", \"CANCELLED\"\n" + " ]\n" + " }\n" + " }]\n" + "}";
expectedServiceConfig = (Map<String, ?>) JsonParser.parse(expectedServiceConfigJson);
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(null, retryPolicy)).isEqualTo(expectedServiceConfig);
// timeout and retry
expectedServiceConfigJson = "{\n" + " \"methodConfig\": [{\n" + " \"name\": [ {} ],\n" + " \"retryPolicy\": {\n" + " \"maxAttempts\" : 4,\n" + " \"initialBackoff\": \"0.100s\",\n" + " \"maxBackoff\": \"0.200s\",\n" + " \"backoffMultiplier\": 2,\n" + " \"retryableStatusCodes\": [\n" + " \"UNAVAILABLE\", \"CANCELLED\"\n" + " ]\n" + " },\n" + " \"timeout\": \"1.000000001s\"\n" + " }]\n" + "}";
expectedServiceConfig = (Map<String, ?>) JsonParser.parse(expectedServiceConfigJson);
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(timeoutNano, retryPolicy)).isEqualTo(expectedServiceConfig);
// no timeout and no retry
expectedServiceConfigJson = "{}";
expectedServiceConfig = (Map<String, ?>) JsonParser.parse(expectedServiceConfigJson);
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(null, null)).isEqualTo(expectedServiceConfig);
// retry with emtry retriable status codes only
assertThat(XdsNameResolver.generateServiceConfigWithMethodConfig(null, retryPolicyWithEmptyStatusCodes)).isEqualTo(expectedServiceConfig);
}
use of io.grpc.xds.VirtualHost.Route.RouteAction.RetryPolicy in project grpc-java by grpc.
the class ClientXdsClient method parseRouteAction.
/**
* Parses the RouteAction config. The returned result may contain a (parsed form)
* {@link RouteAction} or an error message. Returns {@code null} if the RouteAction
* should be ignored.
*/
@VisibleForTesting
@Nullable
static StructOrError<RouteAction> parseRouteAction(io.envoyproxy.envoy.config.route.v3.RouteAction proto, FilterRegistry filterRegistry, boolean parseHttpFilter, Map<String, PluginConfig> pluginConfigMap) {
Long timeoutNano = null;
if (proto.hasMaxStreamDuration()) {
io.envoyproxy.envoy.config.route.v3.RouteAction.MaxStreamDuration maxStreamDuration = proto.getMaxStreamDuration();
if (maxStreamDuration.hasGrpcTimeoutHeaderMax()) {
timeoutNano = Durations.toNanos(maxStreamDuration.getGrpcTimeoutHeaderMax());
} else if (maxStreamDuration.hasMaxStreamDuration()) {
timeoutNano = Durations.toNanos(maxStreamDuration.getMaxStreamDuration());
}
}
RetryPolicy retryPolicy = null;
if (enableRetry && proto.hasRetryPolicy()) {
StructOrError<RetryPolicy> retryPolicyOrError = parseRetryPolicy(proto.getRetryPolicy());
if (retryPolicyOrError != null) {
if (retryPolicyOrError.errorDetail != null) {
return StructOrError.fromError(retryPolicyOrError.errorDetail);
}
retryPolicy = retryPolicyOrError.struct;
}
}
List<HashPolicy> hashPolicies = new ArrayList<>();
for (io.envoyproxy.envoy.config.route.v3.RouteAction.HashPolicy config : proto.getHashPolicyList()) {
HashPolicy policy = null;
boolean terminal = config.getTerminal();
switch(config.getPolicySpecifierCase()) {
case HEADER:
io.envoyproxy.envoy.config.route.v3.RouteAction.HashPolicy.Header headerCfg = config.getHeader();
Pattern regEx = null;
String regExSubstitute = null;
if (headerCfg.hasRegexRewrite() && headerCfg.getRegexRewrite().hasPattern() && headerCfg.getRegexRewrite().getPattern().hasGoogleRe2()) {
regEx = Pattern.compile(headerCfg.getRegexRewrite().getPattern().getRegex());
regExSubstitute = headerCfg.getRegexRewrite().getSubstitution();
}
policy = HashPolicy.forHeader(terminal, headerCfg.getHeaderName(), regEx, regExSubstitute);
break;
case FILTER_STATE:
if (config.getFilterState().getKey().equals(HASH_POLICY_FILTER_STATE_KEY)) {
policy = HashPolicy.forChannelId(terminal);
}
break;
default:
}
if (policy != null) {
hashPolicies.add(policy);
}
}
switch(proto.getClusterSpecifierCase()) {
case CLUSTER:
return StructOrError.fromStruct(RouteAction.forCluster(proto.getCluster(), hashPolicies, timeoutNano, retryPolicy));
case CLUSTER_HEADER:
return null;
case WEIGHTED_CLUSTERS:
List<io.envoyproxy.envoy.config.route.v3.WeightedCluster.ClusterWeight> clusterWeights = proto.getWeightedClusters().getClustersList();
if (clusterWeights.isEmpty()) {
return StructOrError.fromError("No cluster found in weighted cluster list");
}
List<ClusterWeight> weightedClusters = new ArrayList<>();
for (io.envoyproxy.envoy.config.route.v3.WeightedCluster.ClusterWeight clusterWeight : clusterWeights) {
StructOrError<ClusterWeight> clusterWeightOrError = parseClusterWeight(clusterWeight, filterRegistry, parseHttpFilter);
if (clusterWeightOrError.getErrorDetail() != null) {
return StructOrError.fromError("RouteAction contains invalid ClusterWeight: " + clusterWeightOrError.getErrorDetail());
}
weightedClusters.add(clusterWeightOrError.getStruct());
}
// TODO(chengyuanzhang): validate if the sum of weights equals to total weight.
return StructOrError.fromStruct(RouteAction.forWeightedClusters(weightedClusters, hashPolicies, timeoutNano, retryPolicy));
case CLUSTER_SPECIFIER_PLUGIN:
if (enableRouteLookup) {
String pluginName = proto.getClusterSpecifierPlugin();
PluginConfig pluginConfig = pluginConfigMap.get(pluginName);
if (pluginConfig == null) {
return StructOrError.fromError("ClusterSpecifierPlugin for [" + pluginName + "] not found");
}
NamedPluginConfig namedPluginConfig = NamedPluginConfig.create(pluginName, pluginConfig);
return StructOrError.fromStruct(RouteAction.forClusterSpecifierPlugin(namedPluginConfig, hashPolicies, timeoutNano, retryPolicy));
} else {
return StructOrError.fromError("Support for ClusterSpecifierPlugin not enabled");
}
case CLUSTERSPECIFIER_NOT_SET:
default:
return StructOrError.fromError("Unknown cluster specifier: " + proto.getClusterSpecifierCase());
}
}
Aggregations