Skip to content

Commit 199a7ea

Browse files
authored
xds: Improve XdsNR's selectConfig() variable handling
The variables from the do-while are no longer initialized to let the compiler verify that the loop sets each. Unnecessary comparisons to null are also removed and is more obvious as the variables are never set to null. Added a minor optimization of computing the RPCs path once instead of once for each route. The variable declarations were also sorted to match their initialization order. This does fix an unlikely bug where if the old code could successfully matched a route but fail to retain the cluster, then when trying a second time if the route was _not_ matched it would re-use the prior route and thus infinite-loop failing to retain that same cluster. It also adds a missing cast to unsigned long for a uint32 weight. The old code would detect if the _sum_ was negative, but a weight using 32 bits would have been negative and never selected.
1 parent ea3f644 commit 199a7ea

File tree

3 files changed

+28
-20
lines changed

3 files changed

+28
-20
lines changed

xds/src/main/java/io/grpc/xds/VirtualHost.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,13 @@ private static RouteAction create(
218218
abstract static class ClusterWeight {
219219
abstract String name();
220220

221-
abstract int weight();
221+
abstract long weight();
222222

223223
abstract ImmutableMap<String, FilterConfig> filterConfigOverrides();
224224

225225
static ClusterWeight create(
226-
String name, int weight, Map<String, FilterConfig> filterConfigOverrides) {
226+
String name, long weight, Map<String, FilterConfig> filterConfigOverrides) {
227+
checkArgument(weight >= 0, "weight must not be negative");
227228
return new AutoValue_VirtualHost_Route_RouteAction_ClusterWeight(
228229
name, weight, ImmutableMap.copyOf(filterConfigOverrides));
229230
}

xds/src/main/java/io/grpc/xds/XdsNameResolver.java

+19-15
Original file line numberDiff line numberDiff line change
@@ -384,17 +384,17 @@ static boolean matchHostName(String hostName, String pattern) {
384384
private final class ConfigSelector extends InternalConfigSelector {
385385
@Override
386386
public Result selectConfig(PickSubchannelArgs args) {
387-
String cluster = null;
388-
ClientInterceptor filters = null; // null iff cluster is null
389-
RouteData selectedRoute = null;
390387
RoutingConfig routingCfg;
388+
RouteData selectedRoute;
389+
String cluster;
390+
ClientInterceptor filters;
391391
Metadata headers = args.getHeaders();
392+
String path = "/" + args.getMethodDescriptor().getFullMethodName();
392393
do {
393394
routingCfg = routingConfig;
395+
selectedRoute = null;
394396
for (RouteData route : routingCfg.routes) {
395-
if (RoutingUtils.matchRoute(
396-
route.routeMatch, "/" + args.getMethodDescriptor().getFullMethodName(),
397-
headers, random)) {
397+
if (RoutingUtils.matchRoute(route.routeMatch, path, headers, random)) {
398398
selectedRoute = route;
399399
break;
400400
}
@@ -412,13 +412,14 @@ public Result selectConfig(PickSubchannelArgs args) {
412412
cluster = prefixedClusterName(action.cluster());
413413
filters = selectedRoute.filterChoices.get(0);
414414
} else if (action.weightedClusters() != null) {
415+
// XdsRouteConfigureResource verifies the total weight will not be 0 or exceed uint32
415416
long totalWeight = 0;
416417
for (ClusterWeight weightedCluster : action.weightedClusters()) {
417418
totalWeight += weightedCluster.weight();
418419
}
419420
long select = random.nextLong(totalWeight);
420421
long accumulator = 0;
421-
for (int i = 0; i < action.weightedClusters().size(); i++) {
422+
for (int i = 0; ; i++) {
422423
ClusterWeight weightedCluster = action.weightedClusters().get(i);
423424
accumulator += weightedCluster.weight();
424425
if (select < accumulator) {
@@ -431,22 +432,24 @@ public Result selectConfig(PickSubchannelArgs args) {
431432
cluster =
432433
prefixedClusterSpecifierPluginName(action.namedClusterSpecifierPluginConfig().name());
433434
filters = selectedRoute.filterChoices.get(0);
435+
} else {
436+
// updateRoutes() discards routes with unknown actions
437+
throw new AssertionError();
434438
}
435439
} while (!retainCluster(cluster));
440+
441+
final RouteAction routeAction = selectedRoute.routeAction;
436442
Long timeoutNanos = null;
437443
if (enableTimeout) {
438-
if (selectedRoute != null) {
439-
timeoutNanos = selectedRoute.routeAction.timeoutNano();
440-
}
444+
timeoutNanos = routeAction.timeoutNano();
441445
if (timeoutNanos == null) {
442446
timeoutNanos = routingCfg.fallbackTimeoutNano;
443447
}
444448
if (timeoutNanos <= 0) {
445449
timeoutNanos = null;
446450
}
447451
}
448-
RetryPolicy retryPolicy =
449-
selectedRoute == null ? null : selectedRoute.routeAction.retryPolicy();
452+
RetryPolicy retryPolicy = routeAction.retryPolicy();
450453
// TODO(chengyuanzhang): avoid service config generation and parsing for each call.
451454
Map<String, ?> rawServiceConfig =
452455
generateServiceConfigWithMethodConfig(timeoutNanos, retryPolicy);
@@ -459,8 +462,7 @@ public Result selectConfig(PickSubchannelArgs args) {
459462
"Failed to parse service config (method config)"));
460463
}
461464
final String finalCluster = cluster;
462-
final long hash = generateHash(selectedRoute.routeAction.hashPolicies(), headers);
463-
RouteData finalSelectedRoute = selectedRoute;
465+
final long hash = generateHash(routeAction.hashPolicies(), headers);
464466
class ClusterSelectionInterceptor implements ClientInterceptor {
465467
@Override
466468
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
@@ -469,7 +471,7 @@ public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
469471
CallOptions callOptionsForCluster =
470472
callOptions.withOption(CLUSTER_SELECTION_KEY, finalCluster)
471473
.withOption(RPC_HASH_KEY, hash);
472-
if (finalSelectedRoute.routeAction.autoHostRewrite()) {
474+
if (routeAction.autoHostRewrite()) {
473475
callOptionsForCluster = callOptionsForCluster.withOption(AUTO_HOST_REWRITE_KEY, true);
474476
}
475477
return new SimpleForwardingClientCall<ReqT, RespT>(
@@ -757,6 +759,8 @@ private void updateRoutes(List<VirtualHost> virtualHosts, long httpMaxStreamDura
757759
}
758760
ClientInterceptor filters = createFilters(filterConfigs, virtualHost, route, null);
759761
routesData.add(new RouteData(route.routeMatch(), route.routeAction(), filters));
762+
} else {
763+
// Discard route
760764
}
761765
}
762766

xds/src/main/java/io/grpc/xds/XdsRouteConfigureResource.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -496,8 +496,9 @@ static StructOrError<RouteAction> parseRouteAction(
496496
return StructOrError.fromError("RouteAction contains invalid ClusterWeight: "
497497
+ clusterWeightOrError.getErrorDetail());
498498
}
499-
clusterWeightSum += clusterWeight.getWeight().getValue();
500-
weightedClusters.add(clusterWeightOrError.getStruct());
499+
ClusterWeight parsedWeight = clusterWeightOrError.getStruct();
500+
clusterWeightSum += parsedWeight.weight();
501+
weightedClusters.add(parsedWeight);
501502
}
502503
if (clusterWeightSum <= 0) {
503504
return StructOrError.fromError("Sum of cluster weights should be above 0.");
@@ -609,7 +610,9 @@ static StructOrError<VirtualHost.Route.RouteAction.ClusterWeight> parseClusterWe
609610
+ overrideConfigs.getErrorDetail());
610611
}
611612
return StructOrError.fromStruct(VirtualHost.Route.RouteAction.ClusterWeight.create(
612-
proto.getName(), proto.getWeight().getValue(), overrideConfigs.getStruct()));
613+
proto.getName(),
614+
Integer.toUnsignedLong(proto.getWeight().getValue()),
615+
overrideConfigs.getStruct()));
613616
}
614617

615618
@Nullable // null if the plugin is not supported, but it's marked as optional.

0 commit comments

Comments
 (0)