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