Skip to content

Commit

Permalink
xDS: fix WeightedClusters total weight handling (grpc#32134)
Browse files Browse the repository at this point in the history
* xDS: fix WeightedClusters total weight handling

* iwyu
  • Loading branch information
markdroth authored and XuanWang-Amos committed May 1, 2023
1 parent bac5949 commit 7c6837e
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -688,11 +688,8 @@ XdsResolver::XdsConfigSelector::GetCallConfig(GetCallConfigArgs args) {
[&](const std::vector<
XdsRouteConfigResource::Route::RouteAction::ClusterWeight>&
/*weighted_clusters*/) {
const uint32_t key =
rand() %
entry
.weighted_cluster_state[entry.weighted_cluster_state.size() - 1]
.range_end;
const uint32_t key = absl::Uniform<uint32_t>(
absl::BitGen(), 0, entry.weighted_cluster_state.back().range_end);
// Find the index in weighted clusters corresponding to key.
size_t mid = 0;
size_t start_index = 0;
Expand Down
5 changes: 5 additions & 0 deletions src/core/ext/xds/xds_route_config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <stddef.h>
#include <stdint.h>

#include <limits>
#include <map>
#include <memory>
#include <set>
Expand Down Expand Up @@ -835,6 +836,7 @@ absl::optional<XdsRouteConfigResource::Route::RouteAction> RouteActionParse(
GPR_ASSERT(weighted_clusters_proto != nullptr);
std::vector<XdsRouteConfigResource::Route::RouteAction::ClusterWeight>
action_weighted_clusters;
uint64_t total_weight = 0;
size_t clusters_size;
const envoy_config_route_v3_WeightedCluster_ClusterWeight* const* clusters =
envoy_config_route_v3_WeightedCluster_clusters(weighted_clusters_proto,
Expand Down Expand Up @@ -874,12 +876,15 @@ absl::optional<XdsRouteConfigResource::Route::RouteAction> RouteActionParse(
} else {
cluster.weight = google_protobuf_UInt32Value_value(weight_proto);
if (cluster.weight == 0) continue;
total_weight += cluster.weight;
}
// Add entry to WeightedClusters.
action_weighted_clusters.emplace_back(std::move(cluster));
}
if (action_weighted_clusters.empty()) {
errors->AddError("no valid clusters specified");
} else if (total_weight > std::numeric_limits<uint32_t>::max()) {
errors->AddError("sum of cluster weights exceeds uint32 max");
}
route_action.action = std::move(action_weighted_clusters);
} else if (XdsRlsEnabled() &&
Expand Down
33 changes: 33 additions & 0 deletions test/core/xds/xds_route_config_resource_type_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
// limitations under the License.
//

#include <stdint.h>

#include <algorithm>
#include <limits>
#include <map>
#include <memory>
#include <string>
Expand Down Expand Up @@ -1627,6 +1630,36 @@ TEST_F(WeightedClusterTest, Invalid) {
<< decode_result.resource.status();
}

TEST_F(WeightedClusterTest, TotalWeightExceedsUint32Max) {
RouteConfiguration route_config;
route_config.set_name("foo");
auto* vhost = route_config.add_virtual_hosts();
vhost->add_domains("*");
auto* route_proto = vhost->add_routes();
route_proto->mutable_match()->set_prefix("");
auto* weighted_clusters_proto =
route_proto->mutable_route()->mutable_weighted_clusters();
auto* cluster_weight_proto = weighted_clusters_proto->add_clusters();
cluster_weight_proto->set_name("cluster1");
cluster_weight_proto->mutable_weight()->set_value(
std::numeric_limits<uint32_t>::max());
cluster_weight_proto = weighted_clusters_proto->add_clusters();
cluster_weight_proto->set_name("cluster2");
cluster_weight_proto->mutable_weight()->set_value(1);
std::string serialized_resource;
ASSERT_TRUE(route_config.SerializeToString(&serialized_resource));
auto* resource_type = XdsRouteConfigResourceType::Get();
auto decode_result =
resource_type->Decode(decode_context_, serialized_resource);
EXPECT_EQ(decode_result.resource.status().code(),
absl::StatusCode::kInvalidArgument);
EXPECT_EQ(decode_result.resource.status().message(),
"errors validating RouteConfiguration resource: ["
"field:virtual_hosts[0].routes[0].route.weighted_clusters "
"error:sum of cluster weights exceeds uint32 max]")
<< decode_result.resource.status();
}

//
// RLS tests
//
Expand Down
98 changes: 82 additions & 16 deletions test/cpp/end2end/xds/xds_routing_end2end_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -952,10 +952,6 @@ TEST_P(LdsRdsTest, XdsRoutingWeightedCluster) {
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster3->set_name(kNotUsedClusterName);
weighted_cluster3->mutable_weight()->set_value(0);
route1->mutable_route()
->mutable_weighted_clusters()
->mutable_total_weight()
->set_value(kWeight75 + kWeight25);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultClusterName);
Expand Down Expand Up @@ -984,6 +980,88 @@ TEST_P(LdsRdsTest, XdsRoutingWeightedCluster) {
::testing::DoubleNear(kWeight25Percent, kErrorTolerance));
}

TEST_P(LdsRdsTest, XdsRoutingWeightedClusterNoIntegerOverflow) {
CreateAndStartBackends(3);
const char* kNewCluster1Name = "new_cluster_1";
const char* kNewEdsService1Name = "new_eds_service_name_1";
const char* kNewCluster2Name = "new_cluster_2";
const char* kNewEdsService2Name = "new_eds_service_name_2";
const size_t kNumEchoRpcs = 10; // RPCs that will go to a fixed backend.
const uint32_t kWeight1 = std::numeric_limits<uint32_t>::max() / 3;
const uint32_t kWeight2 = std::numeric_limits<uint32_t>::max() - kWeight1;
const double kErrorTolerance = 0.05;
const double kWeight1Percent =
static_cast<double>(kWeight1) / std::numeric_limits<uint32_t>::max();
const double kWeight2Percent =
static_cast<double>(kWeight2) / std::numeric_limits<uint32_t>::max();
const size_t kNumEcho1Rpcs =
ComputeIdealNumRpcs(kWeight2Percent, kErrorTolerance);
// Populate new EDS resources.
EdsResourceArgs args({
{"locality0", CreateEndpointsForBackends(0, 1)},
});
EdsResourceArgs args1({
{"locality0", CreateEndpointsForBackends(1, 2)},
});
EdsResourceArgs args2({
{"locality0", CreateEndpointsForBackends(2, 3)},
});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args1, kNewEdsService1Name));
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args2, kNewEdsService2Name));
// Populate new CDS resources.
Cluster new_cluster1 = default_cluster_;
new_cluster1.set_name(kNewCluster1Name);
new_cluster1.mutable_eds_cluster_config()->set_service_name(
kNewEdsService1Name);
balancer_->ads_service()->SetCdsResource(new_cluster1);
Cluster new_cluster2 = default_cluster_;
new_cluster2.set_name(kNewCluster2Name);
new_cluster2.mutable_eds_cluster_config()->set_service_name(
kNewEdsService2Name);
balancer_->ads_service()->SetCdsResource(new_cluster2);
// Populating Route Configurations for LDS.
RouteConfiguration new_route_config = default_route_config_;
auto* route1 = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
route1->mutable_match()->set_prefix("/grpc.testing.EchoTest1Service/");
auto* weighted_cluster1 =
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster1->set_name(kNewCluster1Name);
weighted_cluster1->mutable_weight()->set_value(kWeight1);
auto* weighted_cluster2 =
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster2->set_name(kNewCluster2Name);
weighted_cluster2->mutable_weight()->set_value(kWeight2);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultClusterName);
SetRouteConfiguration(balancer_.get(), new_route_config);
WaitForAllBackends(DEBUG_LOCATION, 0, 1);
WaitForAllBackends(DEBUG_LOCATION, 1, 3, /*check_status=*/nullptr,
WaitForBackendOptions(),
RpcOptions().set_rpc_service(SERVICE_ECHO1));
CheckRpcSendOk(DEBUG_LOCATION, kNumEchoRpcs);
CheckRpcSendOk(DEBUG_LOCATION, kNumEcho1Rpcs,
RpcOptions().set_rpc_service(SERVICE_ECHO1));
// Make sure RPCs all go to the correct backend.
EXPECT_EQ(kNumEchoRpcs, backends_[0]->backend_service()->request_count());
EXPECT_EQ(0, backends_[0]->backend_service1()->request_count());
EXPECT_EQ(0, backends_[1]->backend_service()->request_count());
const int weight1_request_count =
backends_[1]->backend_service1()->request_count();
EXPECT_EQ(0, backends_[2]->backend_service()->request_count());
const int weight2_request_count =
backends_[2]->backend_service1()->request_count();
gpr_log(GPR_INFO, "target1 received %d rpcs and target2 received %d rpcs",
weight1_request_count, weight2_request_count);
EXPECT_THAT(static_cast<double>(weight1_request_count) / kNumEcho1Rpcs,
::testing::DoubleNear(kWeight1Percent, kErrorTolerance));
EXPECT_THAT(static_cast<double>(weight2_request_count) / kNumEcho1Rpcs,
::testing::DoubleNear(kWeight2Percent, kErrorTolerance));
}

TEST_P(LdsRdsTest, RouteActionWeightedTargetDefaultRoute) {
CreateAndStartBackends(3);
const char* kNewCluster1Name = "new_cluster_1";
Expand Down Expand Up @@ -1035,10 +1113,6 @@ TEST_P(LdsRdsTest, RouteActionWeightedTargetDefaultRoute) {
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster2->set_name(kNewCluster2Name);
weighted_cluster2->mutable_weight()->set_value(kWeight25);
route1->mutable_route()
->mutable_weighted_clusters()
->mutable_total_weight()
->set_value(kWeight75 + kWeight25);
SetRouteConfiguration(balancer_.get(), new_route_config);
WaitForAllBackends(DEBUG_LOCATION, 1, 3);
CheckRpcSendOk(DEBUG_LOCATION, kNumEchoRpcs);
Expand Down Expand Up @@ -1124,10 +1198,6 @@ TEST_P(LdsRdsTest, XdsRoutingWeightedClusterUpdateWeights) {
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster2->set_name(kNewCluster2Name);
weighted_cluster2->mutable_weight()->set_value(kWeight25);
route1->mutable_route()
->mutable_weighted_clusters()
->mutable_total_weight()
->set_value(kWeight75 + kWeight25);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultClusterName);
Expand Down Expand Up @@ -1264,10 +1334,6 @@ TEST_P(LdsRdsTest, XdsRoutingWeightedClusterUpdateClusters) {
route1->mutable_route()->mutable_weighted_clusters()->add_clusters();
weighted_cluster2->set_name(kDefaultClusterName);
weighted_cluster2->mutable_weight()->set_value(kWeight25);
route1->mutable_route()
->mutable_weighted_clusters()
->mutable_total_weight()
->set_value(kWeight75 + kWeight25);
auto* default_route = new_route_config.mutable_virtual_hosts(0)->add_routes();
default_route->mutable_match()->set_prefix("");
default_route->mutable_route()->set_cluster(kDefaultClusterName);
Expand Down

0 comments on commit 7c6837e

Please sign in to comment.