1 // Copyright 2023 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15
16 #include <gmock/gmock.h>
17 #include <grpc/event_engine/endpoint_config.h>
18 #include <grpcpp/ext/server_metric_recorder.h>
19 #include <gtest/gtest.h>
20
21 #include <string>
22 #include <vector>
23
24 #include "absl/log/log.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/str_format.h"
27 #include "envoy/extensions/load_balancing_policies/client_side_weighted_round_robin/v3/client_side_weighted_round_robin.pb.h"
28 #include "envoy/extensions/load_balancing_policies/wrr_locality/v3/wrr_locality.pb.h"
29 #include "src/core/client_channel/backup_poller.h"
30 #include "src/core/config/config_vars.h"
31 #include "test/core/test_util/fake_stats_plugin.h"
32 #include "test/core/test_util/scoped_env_var.h"
33 #include "test/cpp/end2end/xds/xds_end2end_test_lib.h"
34
35 namespace grpc {
36 namespace testing {
37 namespace {
38
39 using ::envoy::extensions::load_balancing_policies::
40 client_side_weighted_round_robin::v3::ClientSideWeightedRoundRobin;
41 using ::envoy::extensions::load_balancing_policies::wrr_locality::v3::
42 WrrLocality;
43
44 class WrrTest : public XdsEnd2endTest {
45 protected:
SetUp()46 void SetUp() override {
47 // No-op -- tests must explicitly call InitClient().
48 }
49 };
50
51 INSTANTIATE_TEST_SUITE_P(XdsTest, WrrTest, ::testing::Values(XdsTestType()),
52 &XdsTestType::Name);
53
TEST_P(WrrTest,Basic)54 TEST_P(WrrTest, Basic) {
55 InitClient();
56 CreateAndStartBackends(3);
57 // Expected weights = qps / (util + (eps/qps)) =
58 // 1/(0.2+0.2) : 1/(0.3+0.3) : 2/(1.5+0.1) = 6:4:3
59 backends_[0]->server_metric_recorder()->SetQps(100);
60 backends_[0]->server_metric_recorder()->SetEps(20);
61 backends_[0]->server_metric_recorder()->SetApplicationUtilization(0.2);
62 backends_[1]->server_metric_recorder()->SetQps(100);
63 backends_[1]->server_metric_recorder()->SetEps(30);
64 backends_[1]->server_metric_recorder()->SetApplicationUtilization(0.3);
65 backends_[2]->server_metric_recorder()->SetQps(200);
66 backends_[2]->server_metric_recorder()->SetEps(20);
67 backends_[2]->server_metric_recorder()->SetApplicationUtilization(1.5);
68 auto cluster = default_cluster_;
69 WrrLocality wrr_locality;
70 wrr_locality.mutable_endpoint_picking_policy()
71 ->add_policies()
72 ->mutable_typed_extension_config()
73 ->mutable_typed_config()
74 ->PackFrom(ClientSideWeightedRoundRobin());
75 cluster.mutable_load_balancing_policy()
76 ->add_policies()
77 ->mutable_typed_extension_config()
78 ->mutable_typed_config()
79 ->PackFrom(wrr_locality);
80 balancer_->ads_service()->SetCdsResource(cluster);
81 EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
82 balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
83 size_t num_picks = 0;
84 SendRpcsUntil(DEBUG_LOCATION, [&](const RpcResult&) {
85 if (++num_picks == 13) {
86 LOG(INFO) << "request counts: "
87 << backends_[0]->backend_service()->request_count() << " "
88 << backends_[1]->backend_service()->request_count() << " "
89 << backends_[2]->backend_service()->request_count();
90 if (backends_[0]->backend_service()->request_count() == 6 &&
91 backends_[1]->backend_service()->request_count() == 4 &&
92 backends_[2]->backend_service()->request_count() == 3) {
93 return false;
94 }
95 num_picks = 0;
96 ResetBackendCounters();
97 }
98 return true;
99 });
100 }
101
TEST_P(WrrTest,MetricsHaveLocalityLabel)102 TEST_P(WrrTest, MetricsHaveLocalityLabel) {
103 const auto kEndpointWeights =
104 grpc_core::GlobalInstrumentsRegistryTestPeer::
105 FindDoubleHistogramHandleByName("grpc.lb.wrr.endpoint_weights")
106 .value();
107 const std::string target = absl::StrCat("xds:", kServerName);
108 const absl::string_view kLabelValues[] = {/*target=*/target};
109 // Register stats plugin before initializing client.
110 auto stats_plugin = grpc_core::FakeStatsPluginBuilder()
111 .UseDisabledByDefaultMetrics(true)
112 .BuildAndRegister();
113 InitClient();
114 CreateAndStartBackends(2);
115 auto cluster = default_cluster_;
116 WrrLocality wrr_locality;
117 wrr_locality.mutable_endpoint_picking_policy()
118 ->add_policies()
119 ->mutable_typed_extension_config()
120 ->mutable_typed_config()
121 ->PackFrom(ClientSideWeightedRoundRobin());
122 cluster.mutable_load_balancing_policy()
123 ->add_policies()
124 ->mutable_typed_extension_config()
125 ->mutable_typed_config()
126 ->PackFrom(wrr_locality);
127 balancer_->ads_service()->SetCdsResource(cluster);
128 EdsResourceArgs args({{"locality0", CreateEndpointsForBackends(0, 1)},
129 {"locality1", CreateEndpointsForBackends(1, 2)}});
130 balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
131 WaitForAllBackends(DEBUG_LOCATION);
132 // Make sure we have a metric value for each of the two localities.
133 EXPECT_THAT(
134 stats_plugin->GetDoubleHistogramValue(kEndpointWeights, kLabelValues,
135 {LocalityNameString("locality0")}),
136 ::testing::Optional(::testing::Not(::testing::IsEmpty())));
137 EXPECT_THAT(
138 stats_plugin->GetDoubleHistogramValue(kEndpointWeights, kLabelValues,
139 {LocalityNameString("locality1")}),
140 ::testing::Optional(::testing::Not(::testing::IsEmpty())));
141 }
142
143 } // namespace
144 } // namespace testing
145 } // namespace grpc
146
main(int argc,char ** argv)147 int main(int argc, char** argv) {
148 grpc::testing::TestEnvironment env(&argc, argv);
149 ::testing::InitGoogleTest(&argc, argv);
150 // Make the backup poller poll very frequently in order to pick up
151 // updates from all the subchannels's FDs.
152 grpc_core::ConfigVars::Overrides overrides;
153 overrides.client_channel_backup_poll_interval_ms = 1;
154 grpc_core::ConfigVars::SetOverrides(overrides);
155 #if TARGET_OS_IPHONE
156 // Workaround Apple CFStream bug
157 grpc_core::SetEnv("grpc_cfstream", "0");
158 #endif
159 grpc_init();
160 const auto result = RUN_ALL_TESTS();
161 grpc_shutdown();
162 return result;
163 }
164