xds: parse timeout from RDS responses (#7257)

This commit is contained in:
Chengyuan Zhang 2020-07-31 19:12:00 +00:00 committed by GitHub
parent 800ef216a8
commit 14af76cab1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 19 deletions

View File

@ -26,6 +26,7 @@ import com.google.protobuf.ListValue;
import com.google.protobuf.NullValue;
import com.google.protobuf.Struct;
import com.google.protobuf.Value;
import com.google.protobuf.util.Durations;
import com.google.re2j.Pattern;
import com.google.re2j.PatternSyntaxException;
import io.envoyproxy.envoy.type.v3.FractionalPercent;
@ -40,6 +41,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
/**
@ -1067,6 +1069,7 @@ final class EnvoyProtoData {
/** See corresponding Envoy proto message {@link io.envoyproxy.envoy.api.v2.route.RouteAction}. */
static final class RouteAction {
private final long timeoutNano;
// Exactly one of the following fields is non-null.
@Nullable
private final String cluster;
@ -1074,11 +1077,20 @@ final class EnvoyProtoData {
private final List<ClusterWeight> weightedClusters;
@VisibleForTesting
RouteAction(@Nullable String cluster, @Nullable List<ClusterWeight> weightedClusters) {
RouteAction(
long timeoutNano,
@Nullable String cluster,
@Nullable List<ClusterWeight> weightedClusters) {
this.timeoutNano = timeoutNano;
this.cluster = cluster;
this.weightedClusters = weightedClusters;
}
Long getTimeoutNano() {
return timeoutNano;
}
@Nullable
String getCluster() {
return cluster;
@ -1098,18 +1110,20 @@ final class EnvoyProtoData {
return false;
}
RouteAction that = (RouteAction) o;
return Objects.equals(cluster, that.cluster)
return Objects.equals(timeoutNano, that.timeoutNano)
&& Objects.equals(cluster, that.cluster)
&& Objects.equals(weightedClusters, that.weightedClusters);
}
@Override
public int hashCode() {
return Objects.hash(cluster, weightedClusters);
return Objects.hash(timeoutNano, cluster, weightedClusters);
}
@Override
public String toString() {
ToStringHelper toStringHelper = MoreObjects.toStringHelper(this);
toStringHelper.add("timeout", timeoutNano + "ns");
if (cluster != null) {
toStringHelper.add("cluster", cluster);
}
@ -1146,7 +1160,16 @@ final class EnvoyProtoData {
return StructOrError.fromError(
"Unknown cluster specifier: " + proto.getClusterSpecifierCase());
}
return StructOrError.fromStruct(new RouteAction(cluster, weightedClusters));
long timeoutNano = TimeUnit.SECONDS.toNanos(15L); // default 15s
if (proto.hasMaxGrpcTimeout()) {
timeoutNano = Durations.toNanos(proto.getMaxGrpcTimeout());
} else if (proto.hasTimeout()) {
timeoutNano = Durations.toNanos(proto.getTimeout());
}
if (timeoutNano == 0) {
timeoutNano = Long.MAX_VALUE;
}
return StructOrError.fromStruct(new RouteAction(timeoutNano, cluster, weightedClusters));
}
}

View File

@ -24,6 +24,7 @@ import com.google.protobuf.BoolValue;
import com.google.protobuf.Struct;
import com.google.protobuf.UInt32Value;
import com.google.protobuf.Value;
import com.google.protobuf.util.Durations;
import com.google.re2j.Pattern;
import io.envoyproxy.envoy.config.core.v3.RuntimeFractionalPercent;
import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher;
@ -44,6 +45,7 @@ import io.grpc.xds.RouteMatch.HeaderMatcher;
import io.grpc.xds.RouteMatch.PathMatcher;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -206,7 +208,7 @@ public class EnvoyProtoDataTest {
new Route(
new RouteMatch(new PathMatcher("/service/method", null, null),
Collections.<HeaderMatcher>emptyList(), null),
new RouteAction("cluster-foo", null)));
new RouteAction(TimeUnit.SECONDS.toNanos(15L), "cluster-foo", null)));
io.envoyproxy.envoy.config.route.v3.Route unsupportedProto =
io.envoyproxy.envoy.config.route.v3.Route.newBuilder()
@ -393,27 +395,51 @@ public class EnvoyProtoDataTest {
@Test
public void convertRouteAction() {
// cluster_specifier = cluster
// cluster_specifier = cluster, default timeout
io.envoyproxy.envoy.config.route.v3.RouteAction proto1 =
io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
.setCluster("cluster-foo")
.build();
StructOrError<RouteAction> struct1 = RouteAction.fromEnvoyProtoRouteAction(proto1);
assertThat(struct1.getErrorDetail()).isNull();
assertThat(struct1.getStruct().getTimeoutNano())
.isEqualTo(TimeUnit.SECONDS.toNanos(15L)); // default value
assertThat(struct1.getStruct().getCluster()).isEqualTo("cluster-foo");
assertThat(struct1.getStruct().getWeightedCluster()).isNull();
// cluster_specifier = cluster_header
// cluster_specifier = cluster, infinity timeout
io.envoyproxy.envoy.config.route.v3.RouteAction proto2 =
io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
.setMaxGrpcTimeout(Durations.fromNanos(0))
.setTimeout(Durations.fromMicros(20L))
.setCluster("cluster-foo")
.build();
StructOrError<RouteAction> struct2 = RouteAction.fromEnvoyProtoRouteAction(proto2);
assertThat(struct2.getStruct().getTimeoutNano())
.isEqualTo(Long.MAX_VALUE); // infinite
// cluster_specifier = cluster, infinity timeout
io.envoyproxy.envoy.config.route.v3.RouteAction proto3 =
io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
.setTimeout(Durations.fromNanos(0))
.setCluster("cluster-foo")
.build();
StructOrError<RouteAction> struct3 = RouteAction.fromEnvoyProtoRouteAction(proto3);
assertThat(struct3.getStruct().getTimeoutNano()).isEqualTo(Long.MAX_VALUE); // infinite
// cluster_specifier = cluster_header
io.envoyproxy.envoy.config.route.v3.RouteAction proto4 =
io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
.setClusterHeader("cluster-bar")
.build();
StructOrError<RouteAction> struct2 = RouteAction.fromEnvoyProtoRouteAction(proto2);
assertThat(struct2).isNull();
StructOrError<RouteAction> struct4 = RouteAction.fromEnvoyProtoRouteAction(proto4);
assertThat(struct4).isNull();
// cluster_specifier = weighted_cluster
io.envoyproxy.envoy.config.route.v3.RouteAction proto3 =
io.envoyproxy.envoy.config.route.v3.RouteAction proto5 =
io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder()
.setMaxGrpcTimeout(Durations.fromSeconds(6L))
.setTimeout(Durations.fromMicros(20L))
.setWeightedClusters(
WeightedCluster.newBuilder()
.addClusters(
@ -422,10 +448,12 @@ public class EnvoyProtoDataTest {
.setName("cluster-baz")
.setWeight(UInt32Value.newBuilder().setValue(100))))
.build();
StructOrError<RouteAction> struct3 = RouteAction.fromEnvoyProtoRouteAction(proto3);
assertThat(struct3.getErrorDetail()).isNull();
assertThat(struct3.getStruct().getCluster()).isNull();
assertThat(struct3.getStruct().getWeightedCluster())
StructOrError<RouteAction> struct5 = RouteAction.fromEnvoyProtoRouteAction(proto5);
assertThat(struct5.getErrorDetail()).isNull();
assertThat(struct5.getStruct().getTimeoutNano())
.isEqualTo(TimeUnit.SECONDS.toNanos(6L));
assertThat(struct5.getStruct().getCluster()).isNull();
assertThat(struct5.getStruct().getWeightedCluster())
.containsExactly(new ClusterWeight("cluster-baz", 100));
// cluster_specifier unset

View File

@ -750,7 +750,8 @@ public class XdsClientImplTest {
new io.grpc.xds.RouteMatch(
/* prefix= */ null,
/* path= */ "/service1/method1"),
new EnvoyProtoData.RouteAction("cl1.googleapis.com", null)));
new EnvoyProtoData.RouteAction(
TimeUnit.SECONDS.toNanos(15L), "cl1.googleapis.com", null)));
assertThat(routes.get(1)).isEqualTo(
new EnvoyProtoData.Route(
// path match with weighted cluster route
@ -758,6 +759,7 @@ public class XdsClientImplTest {
/* prefix= */ null,
/* path= */ "/service2/method2"),
new EnvoyProtoData.RouteAction(
TimeUnit.SECONDS.toNanos(15L),
null,
ImmutableList.of(
new EnvoyProtoData.ClusterWeight("cl21.googleapis.com", 30),
@ -769,7 +771,8 @@ public class XdsClientImplTest {
new io.grpc.xds.RouteMatch(
/* prefix= */ "/service1/",
/* path= */ null),
new EnvoyProtoData.RouteAction("cl1.googleapis.com", null)));
new EnvoyProtoData.RouteAction(
TimeUnit.SECONDS.toNanos(15L), "cl1.googleapis.com", null)));
assertThat(routes.get(3)).isEqualTo(
new EnvoyProtoData.Route(
// default match with cluster route
@ -777,7 +780,7 @@ public class XdsClientImplTest {
/* prefix= */ "",
/* path= */ null),
new EnvoyProtoData.RouteAction(
"cluster.googleapis.com", null)));
TimeUnit.SECONDS.toNanos(15L), "cluster.googleapis.com", null)));
}
/**

View File

@ -83,7 +83,7 @@ public class XdsNameResolverTest {
new RouteMatch(
new PathMatcher(null, "", null), Collections.<HeaderMatcher>emptyList(),
new FractionMatcher(10, 20)),
new RouteAction("cluster-foo", null));
new RouteAction(15L, "cluster-foo", null));
Route r2 =
new Route(
new RouteMatch(
@ -92,6 +92,7 @@ public class XdsNameResolverTest {
new HeaderMatcher(":scheme", "https", null, null, null, null, null, false)),
null),
new RouteAction(
15L,
null,
Arrays.asList(
new ClusterWeight("cluster-foo", 20),
@ -134,7 +135,7 @@ public class XdsNameResolverTest {
new RouteMatch(
new PathMatcher("/service/method", null, null),
Collections.<HeaderMatcher>emptyList(), null),
new RouteAction("cluster-foo", null));
new RouteAction(15L, "cluster-foo", null));
Map<String, ?> config =
XdsNameResolver.generateXdsRoutingRawConfig(Arrays.asList(route, route));