xds_outlier_detection_end2end_test.cc
Go to the documentation of this file.
1 // Copyright 2017 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 #include <chrono>
16 #include <string>
17 #include <thread>
18 #include <vector>
19 
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22 
24 #include "src/proto/grpc/testing/xds/v3/cluster.grpc.pb.h"
25 #include "src/proto/grpc/testing/xds/v3/fault.grpc.pb.h"
26 #include "src/proto/grpc/testing/xds/v3/outlier_detection.grpc.pb.h"
27 #include "src/proto/grpc/testing/xds/v3/router.grpc.pb.h"
30 
31 namespace grpc {
32 namespace testing {
33 namespace {
34 
35 class OutlierDetectionTest : public XdsEnd2endTest {
36  protected:
37  std::string CreateMetadataValueThatHashesToBackend(int index) {
38  return absl::StrCat(ipv6_only_ ? "[::1]" : "127.0.0.1", ":",
39  backends_[index]->port(), "_0");
40  }
41 };
42 
43 INSTANTIATE_TEST_SUITE_P(XdsTest, OutlierDetectionTest,
44  ::testing::Values(XdsTestType()), &XdsTestType::Name);
45 // TODO(donnadionne): add non-xds test a new
46 // test/cpp/end2end/outlier_detection_end2end_test.cc
47 
48 // Tests SuccessRateEjectionAndUnejection:
49 // 1. Use ring hash policy that hashes using a
50 // header value to ensure rpcs go to all backends.
51 // 2. Cause a single error on 1
52 // backend and wait for 1 outlier detection interval to pass.
53 // 3. We should skip
54 // exactly 1 backend due to ejection and all the loads sticky to that backend
55 // should go to 1 other backend.
56 // 4. Let the ejection period pass and verify we can go back to both backends
57 // after the uneject.
58 TEST_P(OutlierDetectionTest, SuccessRateEjectionAndUnejection) {
59  ScopedExperimentalEnvVar env_var(
60  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
61  CreateAndStartBackends(2);
62  auto cluster = default_cluster_;
63  cluster.set_lb_policy(Cluster::RING_HASH);
64  // Setup outlier failure percentage parameters.
65  // Any failure will cause an potential ejection with the probability of 100%
66  // (to eliminate flakiness of the test).
67  auto* interval = cluster.mutable_outlier_detection()->mutable_interval();
68  auto* base_time =
69  cluster.mutable_outlier_detection()->mutable_base_ejection_time();
70  interval->set_nanos(100000000 * grpc_test_slowdown_factor());
71  base_time->set_seconds(1 * grpc_test_slowdown_factor());
72  cluster.mutable_outlier_detection()
73  ->mutable_success_rate_stdev_factor()
74  ->set_value(100);
75  cluster.mutable_outlier_detection()
76  ->mutable_enforcing_success_rate()
77  ->set_value(100);
78  cluster.mutable_outlier_detection()
79  ->mutable_success_rate_minimum_hosts()
80  ->set_value(1);
81  cluster.mutable_outlier_detection()
82  ->mutable_success_rate_request_volume()
83  ->set_value(1);
84  balancer_->ads_service()->SetCdsResource(cluster);
85  auto new_route_config = default_route_config_;
86  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
87  auto* hash_policy = route->mutable_route()->add_hash_policy();
88  hash_policy->mutable_header()->set_header_name("address_hash");
89  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
90  new_route_config);
91  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
92  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
93  // Note each type of RPC will contains a header value that will always be
94  // hashed to a specific backend as the header value matches the value used
95  // to create the entry in the ring.
96  std::vector<std::pair<std::string, std::string>> metadata = {
97  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
98  std::vector<std::pair<std::string, std::string>> metadata1 = {
99  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
100  const auto rpc_options = RpcOptions().set_metadata(metadata);
101  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
102  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
103  WaitForBackendOptions(), rpc_options);
104  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
105  WaitForBackendOptions(), rpc_options1);
106  // Cause an error and wait for 1 outlier detection interval to pass
107  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
108  RpcOptions()
109  .set_metadata(std::move(metadata))
110  .set_server_expected_error(StatusCode::CANCELLED));
112  ResetBackendCounters();
113  // 1 backend is ejected, rpc destinated to it are now hashed to the other
114  // backend.
115  // Success rate enforcement_percentage of 100% is honored as this test will
116  // consistently reject 1 backend
117  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
118  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
119  // Let base ejection period pass and see that we are no longer ejecting, rpcs
120  // going to their expectedly hashed backends.
122  ResetBackendCounters();
123  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
124  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
125  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
126  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
127 }
128 
129 // We don't eject more than max_ejection_percent (default 10%) of the backends
130 // beyond the first one.
131 TEST_P(OutlierDetectionTest, SuccessRateMaxPercent) {
132  ScopedExperimentalEnvVar env_var(
133  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
134  CreateAndStartBackends(4);
135  auto cluster = default_cluster_;
136  cluster.set_lb_policy(Cluster::RING_HASH);
137  // Setup outlier failure percentage parameters.
138  // Any failure will cause an potential ejection with the probability of 100%
139  // (to eliminate flakiness of the test).
140  auto* duration = cluster.mutable_outlier_detection()->mutable_interval();
141  duration->set_nanos(100000000 * grpc_test_slowdown_factor());
142  cluster.mutable_outlier_detection()
143  ->mutable_success_rate_stdev_factor()
144  ->set_value(100);
145  cluster.mutable_outlier_detection()
146  ->mutable_enforcing_success_rate()
147  ->set_value(100);
148  cluster.mutable_outlier_detection()
149  ->mutable_success_rate_minimum_hosts()
150  ->set_value(1);
151  cluster.mutable_outlier_detection()
152  ->mutable_success_rate_request_volume()
153  ->set_value(1);
154  balancer_->ads_service()->SetCdsResource(cluster);
155  auto new_route_config = default_route_config_;
156  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
157  auto* hash_policy = route->mutable_route()->add_hash_policy();
158  hash_policy->mutable_header()->set_header_name("address_hash");
159  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
160  new_route_config);
161  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
162  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
163  // Note each type of RPC will contains a header value that will always be
164  // hashed to a specific backend as the header value matches the value used
165  // to create the entry in the ring.
166  std::vector<std::pair<std::string, std::string>> metadata = {
167  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
168  std::vector<std::pair<std::string, std::string>> metadata1 = {
169  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
170  std::vector<std::pair<std::string, std::string>> metadata2 = {
171  {"address_hash", CreateMetadataValueThatHashesToBackend(2)}};
172  std::vector<std::pair<std::string, std::string>> metadata3 = {
173  {"address_hash", CreateMetadataValueThatHashesToBackend(3)}};
174  const auto rpc_options = RpcOptions().set_metadata(metadata);
175  const auto rpc_options1 = RpcOptions().set_metadata(metadata1);
176  const auto rpc_options2 = RpcOptions().set_metadata(metadata2);
177  const auto rpc_options3 = RpcOptions().set_metadata(metadata3);
178  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
179  WaitForBackendOptions(), rpc_options);
180  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
181  WaitForBackendOptions(), rpc_options1);
182  WaitForBackend(DEBUG_LOCATION, 2, /*check_status=*/nullptr,
183  WaitForBackendOptions(), rpc_options2);
184  WaitForBackend(DEBUG_LOCATION, 3, /*check_status=*/nullptr,
185  WaitForBackendOptions(), rpc_options3);
186  // Cause 2 error and wait for 1 outlier detection interval to pass to cause
187  // the backend to be ejected.
188  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
189  RpcOptions()
190  .set_metadata(std::move(metadata))
191  .set_server_expected_error(StatusCode::CANCELLED));
192  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
193  RpcOptions()
194  .set_metadata(std::move(metadata1))
195  .set_server_expected_error(StatusCode::CANCELLED));
196  CheckRpcSendOk(DEBUG_LOCATION, 1, rpc_options2);
197  CheckRpcSendOk(DEBUG_LOCATION, 1, rpc_options3);
199  ResetBackendCounters();
200  // 1 backend should be ejected, trafficed picked up by another backend.
201  // No other backend should be ejected.
202  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
203  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
204  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options2);
205  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options3);
206  size_t empty_load_backend_count = 0;
207  size_t double_load_backend_count = 0;
208  size_t regular_load_backend_count = 0;
209  for (size_t i = 0; i < backends_.size(); ++i) {
210  if (backends_[i]->backend_service()->request_count() == 0) {
211  ++empty_load_backend_count;
212  } else if (backends_[i]->backend_service()->request_count() == 200) {
213  ++double_load_backend_count;
214  } else if (backends_[i]->backend_service()->request_count() == 100) {
215  ++regular_load_backend_count;
216  } else {
217  GPR_ASSERT(1);
218  }
219  }
220  EXPECT_EQ(1, empty_load_backend_count);
221  EXPECT_EQ(1, double_load_backend_count);
222  EXPECT_EQ(2, regular_load_backend_count);
223 }
224 
225 // Success rate stdev_factor is honored, a higher value would ensure ejection
226 // does not occur.
227 TEST_P(OutlierDetectionTest, SuccessRateStdevFactor) {
228  ScopedExperimentalEnvVar env_var(
229  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
230  CreateAndStartBackends(2);
231  auto cluster = default_cluster_;
232  cluster.set_lb_policy(Cluster::RING_HASH);
233  // Setup outlier failure percentage parameters.
234  // Any failure will cause an potential ejection with the probability of 100%
235  // (to eliminate flakiness of the test).
236  auto* interval = cluster.mutable_outlier_detection()->mutable_interval();
237  auto* base_time =
238  cluster.mutable_outlier_detection()->mutable_base_ejection_time();
239  interval->set_nanos(100000000 * grpc_test_slowdown_factor());
240  base_time->set_seconds(1 * grpc_test_slowdown_factor());
241  // We know a stdev factor of 100 will ensure the ejection occurs, so setting
242  // it to something higher like 1000 to test that ejection will not occur.
243  // Note this parameter is the only difference between this test and
244  // SuccessRateEjectionAndUnejection (ejection portion, value set to 100) and
245  // this one value changes means the difference between not ejecting in this
246  // test and ejecting in the other test.
247  cluster.mutable_outlier_detection()
248  ->mutable_success_rate_stdev_factor()
249  ->set_value(1000);
250  cluster.mutable_outlier_detection()
251  ->mutable_enforcing_success_rate()
252  ->set_value(100);
253  cluster.mutable_outlier_detection()
254  ->mutable_success_rate_minimum_hosts()
255  ->set_value(1);
256  cluster.mutable_outlier_detection()
257  ->mutable_success_rate_request_volume()
258  ->set_value(1);
259  balancer_->ads_service()->SetCdsResource(cluster);
260  auto new_route_config = default_route_config_;
261  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
262  auto* hash_policy = route->mutable_route()->add_hash_policy();
263  hash_policy->mutable_header()->set_header_name("address_hash");
264  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
265  new_route_config);
266  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
267  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
268  // Note each type of RPC will contains a header value that will always be
269  // hashed to a specific backend as the header value matches the value used
270  // to create the entry in the ring.
271  std::vector<std::pair<std::string, std::string>> metadata = {
272  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
273  std::vector<std::pair<std::string, std::string>> metadata1 = {
274  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
275  const auto rpc_options = RpcOptions().set_metadata(metadata);
276  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
277  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
278  WaitForBackendOptions(), rpc_options);
279  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
280  WaitForBackendOptions(), rpc_options1);
281  // Cause an error and wait for 1 outlier detection interval to pass
282  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
283  RpcOptions()
284  .set_metadata(std::move(metadata))
285  .set_server_expected_error(StatusCode::CANCELLED));
287  ResetBackendCounters();
288  // 1 backend experenced failure, but since the stdev_factor is high, no
289  // backend will be noticed as an outlier so no ejection.
290  // Both backends are still getting the RPCs intended for them.
291  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
292  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
293  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
294  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
295 }
296 
297 // Success rate enforcement percentage is honored, setting it to 0 so guarantee
298 // the randomized number between 1 to 100 will always be great, so nothing will
299 // be ejected.
300 TEST_P(OutlierDetectionTest, SuccessRateEnforcementPercentage) {
301  ScopedExperimentalEnvVar env_var(
302  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
303  CreateAndStartBackends(2);
304  auto cluster = default_cluster_;
305  cluster.set_lb_policy(Cluster::RING_HASH);
306  auto* interval = cluster.mutable_outlier_detection()->mutable_interval();
307  auto* base_time =
308  cluster.mutable_outlier_detection()->mutable_base_ejection_time();
309  interval->set_nanos(100000000 * grpc_test_slowdown_factor());
310  base_time->set_seconds(1 * grpc_test_slowdown_factor());
311  cluster.mutable_outlier_detection()
312  ->mutable_success_rate_stdev_factor()
313  ->set_value(100);
314  // Setting enforcing_success_rate to 0 to ensure we will never eject.
315  // Note this parameter is the only difference between this test and
316  // SuccessRateEjectionAndUnejection (ejection portion, value set to 100) and
317  // this one value changes means the difference between guaranteed not ejecting
318  // in this test and guaranteed ejecting in the other test.
319  cluster.mutable_outlier_detection()
320  ->mutable_enforcing_success_rate()
321  ->set_value(0);
322  cluster.mutable_outlier_detection()
323  ->mutable_success_rate_minimum_hosts()
324  ->set_value(1);
325  cluster.mutable_outlier_detection()
326  ->mutable_success_rate_request_volume()
327  ->set_value(1);
328  balancer_->ads_service()->SetCdsResource(cluster);
329  auto new_route_config = default_route_config_;
330  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
331  auto* hash_policy = route->mutable_route()->add_hash_policy();
332  hash_policy->mutable_header()->set_header_name("address_hash");
333  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
334  new_route_config);
335  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
336  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
337  // Note each type of RPC will contains a header value that will always be
338  // hashed to a specific backend as the header value matches the value used
339  // to create the entry in the ring.
340  std::vector<std::pair<std::string, std::string>> metadata = {
341  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
342  std::vector<std::pair<std::string, std::string>> metadata1 = {
343  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
344  const auto rpc_options = RpcOptions().set_metadata(metadata);
345  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
346  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
347  WaitForBackendOptions(), rpc_options);
348  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
349  WaitForBackendOptions(), rpc_options1);
350  // Cause an error and wait for 1 outlier detection interval to pass
351  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
352  RpcOptions()
353  .set_metadata(std::move(metadata))
354  .set_server_expected_error(StatusCode::CANCELLED));
356  ResetBackendCounters();
357  // 1 backend experenced failure, but since the enforcement percentage is 0, no
358  // backend will be ejected.
359  // Both backends are still getting the RPCs intended for them.
360  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
361  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
362  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
363  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
364 }
365 
366 // Success rate does not eject if there are less than minimum_hosts backends
367 // Set success_rate_minimum_hosts to 3 when we only have 2 backends
368 TEST_P(OutlierDetectionTest, SuccessRateMinimumHosts) {
369  ScopedExperimentalEnvVar env_var(
370  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
371  CreateAndStartBackends(2);
372  auto cluster = default_cluster_;
373  cluster.set_lb_policy(Cluster::RING_HASH);
374  // Setup outlier failure percentage parameters.
375  // Any failure will cause an potential ejection with the probability of 100%
376  // (to eliminate flakiness of the test).
377  auto* duration = cluster.mutable_outlier_detection()->mutable_interval();
378  duration->set_nanos(100000000 * grpc_test_slowdown_factor());
379  cluster.mutable_outlier_detection()
380  ->mutable_success_rate_stdev_factor()
381  ->set_value(100);
382  cluster.mutable_outlier_detection()
383  ->mutable_enforcing_success_rate()
384  ->set_value(100);
385  // Set success_rate_minimum_hosts to 3 when we only have 2 backends
386  // Note this parameter is the only difference between this test and
387  // SuccessRateEjectionAndUnejection (ejection portion, value set to 1) and
388  // this one value changes means the difference between not ejecting in this
389  // test and ejecting in the other test.
390  cluster.mutable_outlier_detection()
391  ->mutable_success_rate_minimum_hosts()
392  ->set_value(3);
393  cluster.mutable_outlier_detection()
394  ->mutable_success_rate_request_volume()
395  ->set_value(1);
396  balancer_->ads_service()->SetCdsResource(cluster);
397  auto new_route_config = default_route_config_;
398  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
399  auto* hash_policy = route->mutable_route()->add_hash_policy();
400  hash_policy->mutable_header()->set_header_name("address_hash");
401  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
402  new_route_config);
403  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
404  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
405  // Note each type of RPC will contains a header value that will always be
406  // hashed to a specific backend as the header value matches the value used
407  // to create the entry in the ring.
408  std::vector<std::pair<std::string, std::string>> metadata = {
409  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
410  std::vector<std::pair<std::string, std::string>> metadata1 = {
411  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
412  const auto rpc_options = RpcOptions().set_metadata(metadata);
413  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
414  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
415  WaitForBackendOptions(), rpc_options);
416  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
417  WaitForBackendOptions(), rpc_options1);
418  // Cause an error and wait for 1 outlier detection interval to pass
419  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
420  RpcOptions()
421  .set_metadata(std::move(metadata))
422  .set_server_expected_error(StatusCode::CANCELLED));
424  ResetBackendCounters();
425  // All traffic still reaching the original backends and no backends are
426  // ejected.
427  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
428  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
429  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
430  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
431 }
432 
433 // Success rate does not eject if there are less than request_volume requests
434 // Set success_rate_request_volume to 4 when we only send 3 RPC in the
435 // interval.
436 TEST_P(OutlierDetectionTest, SuccessRateRequestVolume) {
437  ScopedExperimentalEnvVar env_var(
438  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
439  CreateAndStartBackends(2);
440  auto cluster = default_cluster_;
441  cluster.set_lb_policy(Cluster::RING_HASH);
442  // Setup outlier failure percentage parameters.
443  // Any failure will cause an potential ejection with the probability of 100%
444  // (to eliminate flakiness of the test).
445  auto* duration = cluster.mutable_outlier_detection()->mutable_interval();
446  duration->set_nanos(100000000 * grpc_test_slowdown_factor());
447  cluster.mutable_outlier_detection()
448  ->mutable_success_rate_stdev_factor()
449  ->set_value(100);
450  cluster.mutable_outlier_detection()
451  ->mutable_enforcing_success_rate()
452  ->set_value(100);
453  cluster.mutable_outlier_detection()
454  ->mutable_success_rate_minimum_hosts()
455  ->set_value(1);
456  // Set success_rate_request_volume to 4 when we only send 3 RPC in the
457  // interval.
458  // Note this parameter is the only difference between this test and
459  // SuccessRateEjectionAndUnejection (ejection portion, value set to 1) and
460  // this one value changes means the difference between not ejecting in this
461  // test and ejecting in the other test.
462  cluster.mutable_outlier_detection()
463  ->mutable_success_rate_request_volume()
464  ->set_value(4);
465  balancer_->ads_service()->SetCdsResource(cluster);
466  auto new_route_config = default_route_config_;
467  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
468  auto* hash_policy = route->mutable_route()->add_hash_policy();
469  hash_policy->mutable_header()->set_header_name("address_hash");
470  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
471  new_route_config);
472  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
473  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
474  // Note each type of RPC will contains a header value that will always be
475  // hashed to a specific backend as the header value matches the value used
476  // to create the entry in the ring.
477  std::vector<std::pair<std::string, std::string>> metadata = {
478  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
479  std::vector<std::pair<std::string, std::string>> metadata1 = {
480  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
481  const auto rpc_options = RpcOptions().set_metadata(metadata);
482  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
483  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
484  WaitForBackendOptions(), rpc_options);
485  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
486  WaitForBackendOptions(), rpc_options1);
487  // Cause an error and wait for 1 outlier detection interval to pass
488  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
489  RpcOptions()
490  .set_metadata(std::move(metadata))
491  .set_server_expected_error(StatusCode::CANCELLED));
493  ResetBackendCounters();
494  // All traffic still reaching the original backends and no backends are
495  // ejected.
496  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
497  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
498  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
499  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
500 }
501 
502 // Tests FailurePercentageEjectionAndUnejection:
503 // 1. Use ring hash policy that hashes using a
504 // header value to ensure rpcs go to all backends.
505 // 2. Cause a single error on 1
506 // backend and wait for 1 outlier detection interval to pass.
507 // 3. We should skip
508 // exactly 1 backend due to ejection and all the loads sticky to that backend
509 // should go to 1 other backend.
510 // 4. Let the ejection period pass and verify that traffic will again go both
511 // backends as we have unejected the backend.
512 TEST_P(OutlierDetectionTest, FailurePercentageEjectionAndUnejection) {
513  ScopedExperimentalEnvVar env_var(
514  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
515  CreateAndStartBackends(2);
516  auto cluster = default_cluster_;
517  cluster.set_lb_policy(Cluster::RING_HASH);
518  // Setup outlier failure percentage parameters.
519  // Any failure will cause an potential ejection with the probability of 100%
520  // (to eliminate flakiness of the test).
521  auto* interval = cluster.mutable_outlier_detection()->mutable_interval();
522  interval->set_nanos(100000000 * grpc_test_slowdown_factor());
523  auto* base_time =
524  cluster.mutable_outlier_detection()->mutable_base_ejection_time();
525  base_time->set_seconds(1 * grpc_test_slowdown_factor());
526  cluster.mutable_outlier_detection()
527  ->mutable_failure_percentage_threshold()
528  ->set_value(0);
529  cluster.mutable_outlier_detection()
530  ->mutable_enforcing_failure_percentage()
531  ->set_value(100);
532  cluster.mutable_outlier_detection()
533  ->mutable_failure_percentage_minimum_hosts()
534  ->set_value(1);
535  cluster.mutable_outlier_detection()
536  ->mutable_failure_percentage_request_volume()
537  ->set_value(1);
538  balancer_->ads_service()->SetCdsResource(cluster);
539  auto new_route_config = default_route_config_;
540  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
541  auto* hash_policy = route->mutable_route()->add_hash_policy();
542  hash_policy->mutable_header()->set_header_name("address_hash");
543  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
544  new_route_config);
545  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
546  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
547  // Note each type of RPC will contains a header value that will always be
548  // hashed to a specific backend as the header value matches the value used
549  // to create the entry in the ring.
550  std::vector<std::pair<std::string, std::string>> metadata = {
551  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
552  std::vector<std::pair<std::string, std::string>> metadata1 = {
553  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
554  const auto rpc_options = RpcOptions().set_metadata(metadata);
555  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
556  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
557  WaitForBackendOptions(), rpc_options);
558  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
559  WaitForBackendOptions(), rpc_options1);
560  // Cause an error and wait for 1 outlier detection interval to pass to cause
561  // the backend to be ejected.
562  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
563  RpcOptions()
564  .set_metadata(std::move(metadata))
565  .set_server_expected_error(StatusCode::CANCELLED));
567  ResetBackendCounters();
568  // 1 backend is ejected all traffic going to the ejected backend should now
569  // all be going to the other backend.
570  // failure percentage enforcement_percentage of 100% is honored as this test
571  // will consistently reject 1 backend.
572  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
573  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
574  // Let ejection period pass and see that we are no longer ejecting, rpcs going
575  // to their expectedly hashed backends.
577  ResetBackendCounters();
578  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
579  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
580  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
581  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
582 }
583 
584 // We don't eject more than max_ejection_percent (default 10%) of the backends
585 // beyond the first one.
586 TEST_P(OutlierDetectionTest, FailurePercentageMaxPercentage) {
587  ScopedExperimentalEnvVar env_var(
588  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
589  CreateAndStartBackends(4);
590  auto cluster = default_cluster_;
591  cluster.set_lb_policy(Cluster::RING_HASH);
592  // Setup outlier failure percentage parameters.
593  // Any failure will cause an potential ejection with the probability of 100%
594  // (to eliminate flakiness of the test).
595  auto* duration = cluster.mutable_outlier_detection()->mutable_interval();
596  duration->set_nanos(100000000 * grpc_test_slowdown_factor());
597  cluster.mutable_outlier_detection()
598  ->mutable_failure_percentage_threshold()
599  ->set_value(0);
600  cluster.mutable_outlier_detection()
601  ->mutable_enforcing_failure_percentage()
602  ->set_value(100);
603  cluster.mutable_outlier_detection()
604  ->mutable_failure_percentage_minimum_hosts()
605  ->set_value(1);
606  cluster.mutable_outlier_detection()
607  ->mutable_failure_percentage_request_volume()
608  ->set_value(1);
609  balancer_->ads_service()->SetCdsResource(cluster);
610  auto new_route_config = default_route_config_;
611  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
612  auto* hash_policy = route->mutable_route()->add_hash_policy();
613  hash_policy->mutable_header()->set_header_name("address_hash");
614  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
615  new_route_config);
616  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
617  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
618  // Note each type of RPC will contains a header value that will always be
619  // hashed to a specific backend as the header value matches the value used
620  // to create the entry in the ring.
621  std::vector<std::pair<std::string, std::string>> metadata = {
622  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
623  std::vector<std::pair<std::string, std::string>> metadata1 = {
624  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
625  std::vector<std::pair<std::string, std::string>> metadata2 = {
626  {"address_hash", CreateMetadataValueThatHashesToBackend(2)}};
627  std::vector<std::pair<std::string, std::string>> metadata3 = {
628  {"address_hash", CreateMetadataValueThatHashesToBackend(3)}};
629  const auto rpc_options = RpcOptions().set_metadata(metadata);
630  const auto rpc_options1 = RpcOptions().set_metadata(metadata1);
631  const auto rpc_options2 = RpcOptions().set_metadata(metadata2);
632  const auto rpc_options3 = RpcOptions().set_metadata(metadata3);
633  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
634  WaitForBackendOptions(), rpc_options);
635  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
636  WaitForBackendOptions(), rpc_options1);
637  WaitForBackend(DEBUG_LOCATION, 2, /*check_status=*/nullptr,
638  WaitForBackendOptions(), rpc_options2);
639  WaitForBackend(DEBUG_LOCATION, 3, /*check_status=*/nullptr,
640  WaitForBackendOptions(), rpc_options3);
641  // Cause 2 error and wait for 1 outlier detection interval to pass to cause
642  // the backend to be ejected.
643  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
644  RpcOptions()
645  .set_metadata(std::move(metadata))
646  .set_server_expected_error(StatusCode::CANCELLED));
647  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
648  RpcOptions()
649  .set_metadata(std::move(metadata1))
650  .set_server_expected_error(StatusCode::CANCELLED));
652  ResetBackendCounters();
653  // 1 backend should be ejected, trafficed picked up by another backend.
654  // No other backend should be ejected.
655  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
656  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
657  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options2);
658  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options3);
659  size_t empty_load_backend_count = 0;
660  size_t double_load_backend_count = 0;
661  size_t regular_load_backend_count = 0;
662  for (size_t i = 0; i < backends_.size(); ++i) {
663  if (backends_[i]->backend_service()->request_count() == 0) {
664  ++empty_load_backend_count;
665  } else if (backends_[i]->backend_service()->request_count() == 200) {
666  ++double_load_backend_count;
667  } else if (backends_[i]->backend_service()->request_count() == 100) {
668  ++regular_load_backend_count;
669  } else {
670  GPR_ASSERT(1);
671  }
672  }
673  EXPECT_EQ(1, empty_load_backend_count);
674  EXPECT_EQ(1, double_load_backend_count);
675  EXPECT_EQ(2, regular_load_backend_count);
676 }
677 
678 // Failure percentage threshold is honored, a higher value would ensure ejection
679 // does not occur
680 TEST_P(OutlierDetectionTest, FailurePercentageThreshold) {
681  ScopedExperimentalEnvVar env_var(
682  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
683  CreateAndStartBackends(2);
684  auto cluster = default_cluster_;
685  cluster.set_lb_policy(Cluster::RING_HASH);
686  auto* interval = cluster.mutable_outlier_detection()->mutable_interval();
687  interval->set_nanos(100000000 * grpc_test_slowdown_factor());
688  auto* base_time =
689  cluster.mutable_outlier_detection()->mutable_base_ejection_time();
690  base_time->set_seconds(1 * grpc_test_slowdown_factor());
691  // Setup outlier failure percentage parameter to 50
692  // Note this parameter is the only difference between this test and
693  // FailurePercentageEjectionAndUnejection (ejection portion, value set to 0)
694  // and this one value changes means the difference between not ejecting in
695  // this test and ejecting in the other test.
696  cluster.mutable_outlier_detection()
697  ->mutable_failure_percentage_threshold()
698  ->set_value(50);
699  cluster.mutable_outlier_detection()
700  ->mutable_enforcing_failure_percentage()
701  ->set_value(100);
702  cluster.mutable_outlier_detection()
703  ->mutable_failure_percentage_minimum_hosts()
704  ->set_value(1);
705  cluster.mutable_outlier_detection()
706  ->mutable_failure_percentage_request_volume()
707  ->set_value(1);
708  balancer_->ads_service()->SetCdsResource(cluster);
709  auto new_route_config = default_route_config_;
710  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
711  auto* hash_policy = route->mutable_route()->add_hash_policy();
712  hash_policy->mutable_header()->set_header_name("address_hash");
713  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
714  new_route_config);
715  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
716  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
717  // Note each type of RPC will contains a header value that will always be
718  // hashed to a specific backend as the header value matches the value used
719  // to create the entry in the ring.
720  std::vector<std::pair<std::string, std::string>> metadata = {
721  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
722  std::vector<std::pair<std::string, std::string>> metadata1 = {
723  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
724  const auto rpc_options = RpcOptions().set_metadata(metadata);
725  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
726  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
727  WaitForBackendOptions(), rpc_options);
728  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
729  WaitForBackendOptions(), rpc_options1);
730  // Cause an error and wait for 1 outlier detection interval to pass to cause
731  // the backend to be ejected.
732  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
733  RpcOptions()
734  .set_metadata(std::move(metadata))
735  .set_server_expected_error(StatusCode::CANCELLED));
737  ResetBackendCounters();
738  // 1 backend experenced 1 failure, but since the threshold is 50 % no
739  // backend will be noticed as an outlier so no ejection.
740  // Both backends are still getting the RPCs intended for them.
741  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
742  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
743  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
744  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
745 }
746 
747 // Failure percentage enforcement percentage is honored, setting it to 0 so
748 // guarantee the randomized number between 1 to 100 will always be great, so
749 // nothing will be ejected.
750 TEST_P(OutlierDetectionTest, FailurePercentageEnforcementPercentage) {
751  ScopedExperimentalEnvVar env_var(
752  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
753  CreateAndStartBackends(2);
754  auto cluster = default_cluster_;
755  cluster.set_lb_policy(Cluster::RING_HASH);
756  auto* interval = cluster.mutable_outlier_detection()->mutable_interval();
757  interval->set_nanos(100000000 * grpc_test_slowdown_factor());
758  auto* base_time =
759  cluster.mutable_outlier_detection()->mutable_base_ejection_time();
760  base_time->set_seconds(1 * grpc_test_slowdown_factor());
761  cluster.mutable_outlier_detection()
762  ->mutable_failure_percentage_threshold()
763  ->set_value(0);
764  // Setting enforcing_success_rate to 0 to ensure we will never eject.
765  // Note this parameter is the only difference between this test and
766  // FailurePercentageEjectionAndUnejection (ejection portion, value set to 100)
767  // and this one value changes means the difference between guaranteed not
768  // ejecting in this test and guaranteed ejecting in the other test.
769  cluster.mutable_outlier_detection()
770  ->mutable_enforcing_failure_percentage()
771  ->set_value(0);
772  cluster.mutable_outlier_detection()
773  ->mutable_failure_percentage_minimum_hosts()
774  ->set_value(1);
775  cluster.mutable_outlier_detection()
776  ->mutable_failure_percentage_request_volume()
777  ->set_value(1);
778  balancer_->ads_service()->SetCdsResource(cluster);
779  auto new_route_config = default_route_config_;
780  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
781  auto* hash_policy = route->mutable_route()->add_hash_policy();
782  hash_policy->mutable_header()->set_header_name("address_hash");
783  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
784  new_route_config);
785  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
786  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
787  // Note each type of RPC will contains a header value that will always be
788  // hashed to a specific backend as the header value matches the value used
789  // to create the entry in the ring.
790  std::vector<std::pair<std::string, std::string>> metadata = {
791  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
792  std::vector<std::pair<std::string, std::string>> metadata1 = {
793  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
794  const auto rpc_options = RpcOptions().set_metadata(metadata);
795  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
796  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
797  WaitForBackendOptions(), rpc_options);
798  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
799  WaitForBackendOptions(), rpc_options1);
800  // Cause an error and wait for 1 outlier detection interval to pass to cause
801  // the backend to be ejected.
802  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
803  RpcOptions()
804  .set_metadata(std::move(metadata))
805  .set_server_expected_error(StatusCode::CANCELLED));
807  ResetBackendCounters();
808  // 1 backend experenced failure, but since the enforcement percentage is 0, no
809  // backend will be ejected.
810  // Both backends are still getting the RPCs intended for them.
811  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
812  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
813  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
814  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
815 }
816 
817 // Failure percentage does not eject if there are less than minimum_hosts
818 // backends Set success_rate_minimum_hosts to 3 when we only have 2 backends
819 TEST_P(OutlierDetectionTest, FailurePercentageMinimumHosts) {
820  ScopedExperimentalEnvVar env_var(
821  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
822  CreateAndStartBackends(2);
823  auto cluster = default_cluster_;
824  cluster.set_lb_policy(Cluster::RING_HASH);
825  // Setup outlier failure percentage parameters.
826  // Any failure will cause an potential ejection with the probability of 100%
827  // (to eliminate flakiness of the test).
828  auto* duration = cluster.mutable_outlier_detection()->mutable_interval();
829  duration->set_nanos(100000000 * grpc_test_slowdown_factor());
830  cluster.mutable_outlier_detection()
831  ->mutable_failure_percentage_threshold()
832  ->set_value(0);
833  cluster.mutable_outlier_detection()
834  ->mutable_enforcing_failure_percentage()
835  ->set_value(100);
836  // Set failure_percentage_minimum_hosts to 3 when we only have 2 backends
837  // Note this parameter is the only difference between this test and
838  // FailurePercentageEjectionAndUnejection (ejection portion, value set to 1)
839  // and this one value changes means the difference between not ejecting in
840  // this test and ejecting in the other test.
841  cluster.mutable_outlier_detection()
842  ->mutable_failure_percentage_minimum_hosts()
843  ->set_value(3);
844  cluster.mutable_outlier_detection()
845  ->mutable_failure_percentage_request_volume()
846  ->set_value(1);
847  balancer_->ads_service()->SetCdsResource(cluster);
848  auto new_route_config = default_route_config_;
849  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
850  auto* hash_policy = route->mutable_route()->add_hash_policy();
851  hash_policy->mutable_header()->set_header_name("address_hash");
852  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
853  new_route_config);
854  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
855  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
856  // Note each type of RPC will contains a header value that will always be
857  // hashed to a specific backend as the header value matches the value used
858  // to create the entry in the ring.
859  std::vector<std::pair<std::string, std::string>> metadata = {
860  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
861  std::vector<std::pair<std::string, std::string>> metadata1 = {
862  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
863  const auto rpc_options = RpcOptions().set_metadata(metadata);
864  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
865  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
866  WaitForBackendOptions(), rpc_options);
867  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
868  WaitForBackendOptions(), rpc_options1);
869  // Cause an error and wait for 1 outlier detection interval to pass to cause
870  // the backend to be ejected.
871  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
872  RpcOptions()
873  .set_metadata(std::move(metadata))
874  .set_server_expected_error(StatusCode::CANCELLED));
876  ResetBackendCounters();
877  // All traffic still reaching the original backends and no backends are
878  // ejected.
879  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
880  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
881  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
882  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
883 }
884 
885 // Failure percentage does not eject if there are less than request_volume
886 // requests
887 // Set success_rate_request_volume to 4 when we only send 3 RPC in the
888 // interval.
889 TEST_P(OutlierDetectionTest, FailurePercentageRequestVolume) {
890  ScopedExperimentalEnvVar env_var(
891  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
892  CreateAndStartBackends(2);
893  auto cluster = default_cluster_;
894  cluster.set_lb_policy(Cluster::RING_HASH);
895  // Setup outlier failure percentage parameters.
896  // Any failure will cause an potential ejection with the probability of 100%
897  // (to eliminate flakiness of the test).
898  auto* duration = cluster.mutable_outlier_detection()->mutable_interval();
899  duration->set_nanos(100000000 * grpc_test_slowdown_factor());
900  cluster.mutable_outlier_detection()
901  ->mutable_failure_percentage_threshold()
902  ->set_value(0);
903  cluster.mutable_outlier_detection()
904  ->mutable_enforcing_failure_percentage()
905  ->set_value(100);
906  cluster.mutable_outlier_detection()
907  ->mutable_failure_percentage_minimum_hosts()
908  ->set_value(1);
909  // Set failure_percentage_request_volume to 4 when we only send 3 RPC in the
910  // interval.
911  // // Note this parameter is the only difference between this test and
912  // FailurePercentageEjectionAndUnejection (ejection portion, value set to 1)
913  // and this one value changes means the difference between not ejecting in
914  // this test and ejecting in the other test.
915  cluster.mutable_outlier_detection()
916  ->mutable_failure_percentage_request_volume()
917  ->set_value(4);
918  balancer_->ads_service()->SetCdsResource(cluster);
919  auto new_route_config = default_route_config_;
920  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
921  auto* hash_policy = route->mutable_route()->add_hash_policy();
922  hash_policy->mutable_header()->set_header_name("address_hash");
923  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
924  new_route_config);
925  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
926  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
927  // Note each type of RPC will contains a header value that will always be
928  // hashed to a specific backend as the header value matches the value used
929  // to create the entry in the ring.
930  std::vector<std::pair<std::string, std::string>> metadata = {
931  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
932  std::vector<std::pair<std::string, std::string>> metadata1 = {
933  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
934  const auto rpc_options = RpcOptions().set_metadata(metadata);
935  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
936  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
937  WaitForBackendOptions(), rpc_options);
938  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
939  WaitForBackendOptions(), rpc_options1);
940  // Cause an error and wait for 1 outlier detection interval to pass to cause
941  // the backend to be ejected.
942  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
943  RpcOptions()
944  .set_metadata(std::move(metadata))
945  .set_server_expected_error(StatusCode::CANCELLED));
947  ResetBackendCounters();
948  // All traffic still reaching the original backends and no backends are
949  // ejected.
950  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
951  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
952  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
953  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
954 }
955 
956 // Tests SuccessRate and FailurePercentage both configured
957 // Configure max_ejection_percent to 50% which means max 2/4 backends can be
958 // ejected.
959 // Configure success rate to eject 1 and failure percentage to eject 2.
960 // Verify that maximum 2 backends are ejected, not 3!
961 TEST_P(OutlierDetectionTest, SuccessRateAndFailurePercentage) {
962  ScopedExperimentalEnvVar env_var(
963  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
964  CreateAndStartBackends(4);
965  auto cluster = default_cluster_;
966  cluster.set_lb_policy(Cluster::RING_HASH);
967  // Setup outlier failure percentage parameters.
968  // Any failure will cause an potential ejection with the probability of 100%
969  // (to eliminate flakiness of the test).
970  auto* interval = cluster.mutable_outlier_detection()->mutable_interval();
971  interval->set_nanos(100000000 * grpc_test_slowdown_factor());
972  cluster.mutable_outlier_detection()
973  ->mutable_max_ejection_percent()
974  ->set_value(50);
975  // This stdev of 500 will ensure the number of ok RPC and error RPC we send
976  // will make 1 outlier out of the 4 backends.
977  cluster.mutable_outlier_detection()
978  ->mutable_success_rate_stdev_factor()
979  ->set_value(500);
980  cluster.mutable_outlier_detection()
981  ->mutable_enforcing_success_rate()
982  ->set_value(100);
983  cluster.mutable_outlier_detection()
984  ->mutable_success_rate_minimum_hosts()
985  ->set_value(1);
986  cluster.mutable_outlier_detection()
987  ->mutable_success_rate_request_volume()
988  ->set_value(1);
989  cluster.mutable_outlier_detection()
990  ->mutable_failure_percentage_threshold()
991  ->set_value(0);
992  cluster.mutable_outlier_detection()
993  ->mutable_enforcing_failure_percentage()
994  ->set_value(100);
995  cluster.mutable_outlier_detection()
996  ->mutable_failure_percentage_minimum_hosts()
997  ->set_value(1);
998  cluster.mutable_outlier_detection()
999  ->mutable_failure_percentage_request_volume()
1000  ->set_value(1);
1001  balancer_->ads_service()->SetCdsResource(cluster);
1002  auto new_route_config = default_route_config_;
1003  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
1004  auto* hash_policy = route->mutable_route()->add_hash_policy();
1005  hash_policy->mutable_header()->set_header_name("address_hash");
1006  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
1007  new_route_config);
1008  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
1009  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
1010  // Note each type of RPC will contains a header value that will always be
1011  // hashed to a specific backend as the header value matches the value used
1012  // to create the entry in the ring.
1013  std::vector<std::pair<std::string, std::string>> metadata = {
1014  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
1015  std::vector<std::pair<std::string, std::string>> metadata1 = {
1016  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
1017  std::vector<std::pair<std::string, std::string>> metadata2 = {
1018  {"address_hash", CreateMetadataValueThatHashesToBackend(2)}};
1019  std::vector<std::pair<std::string, std::string>> metadata3 = {
1020  {"address_hash", CreateMetadataValueThatHashesToBackend(3)}};
1021  const auto rpc_options = RpcOptions().set_metadata(metadata);
1022  const auto rpc_options1 = RpcOptions().set_metadata(metadata1);
1023  const auto rpc_options2 = RpcOptions().set_metadata(metadata2);
1024  const auto rpc_options3 = RpcOptions().set_metadata(metadata3);
1025  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
1026  WaitForBackendOptions(), rpc_options);
1027  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
1028  WaitForBackendOptions(), rpc_options1);
1029  WaitForBackend(DEBUG_LOCATION, 2, /*check_status=*/nullptr,
1030  WaitForBackendOptions(), rpc_options2);
1031  WaitForBackend(DEBUG_LOCATION, 3, /*check_status=*/nullptr,
1032  WaitForBackendOptions(), rpc_options3);
1033  // Cause 2 errors on 1 backend and 1 error on 2 backends and wait for 1
1034  // outlier detection interval to pass. The 2 errors to the 1 backend will make
1035  // exactly 1 outlier from the success rate algorithm; all 4 errors will make 3
1036  // outliers from the failure pecentage algorithm because the threahold is set
1037  // to 0. I have verified through debug logs we eject 1 backend because of
1038  // success rate, 1 backend because of failure percentage; but as we attempt to
1039  // eject another backend because of failure percentage we will stop as we have
1040  // reached our 50% limit.
1041  CheckRpcSendFailure(
1043  RpcOptions().set_metadata(metadata).set_server_expected_error(
1045  CheckRpcSendFailure(
1047  RpcOptions().set_metadata(metadata).set_server_expected_error(
1049  CheckRpcSendFailure(
1051  RpcOptions().set_metadata(metadata1).set_server_expected_error(
1053  CheckRpcSendFailure(
1055  RpcOptions().set_metadata(metadata2).set_server_expected_error(
1058  ResetBackendCounters();
1059  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
1060  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
1061  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options2);
1062  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options3);
1063  size_t empty_load_backend_count = 0;
1064  size_t double_load_backend_count = 0;
1065  for (size_t i = 0; i < backends_.size(); ++i) {
1066  if (backends_[i]->backend_service()->request_count() == 0) {
1067  ++empty_load_backend_count;
1068  } else if (backends_[i]->backend_service()->request_count() >= 100) {
1069  // The extra load could go to 2 remaining backends or just 1 of them.
1070  ++double_load_backend_count;
1071  } else if (backends_[i]->backend_service()->request_count() > 300) {
1072  GPR_ASSERT(1);
1073  }
1074  }
1075  EXPECT_EQ(2, empty_load_backend_count);
1076  EXPECT_EQ(2, double_load_backend_count);
1077 }
1078 
1079 // Tests SuccessRate and FailurePercentage both unconfigured;
1080 // This is the case where according to the RFC we need to instruct the picker
1081 // not to do counting or even start the timer. The result of not counting is
1082 // that there will be no ejection taking place since we can't do any
1083 // calculations.
1084 TEST_P(OutlierDetectionTest, SuccessRateAndFailurePercentageBothDisabled) {
1085  ScopedExperimentalEnvVar env_var(
1086  "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
1087  CreateAndStartBackends(2);
1088  auto cluster = default_cluster_;
1089  cluster.set_lb_policy(Cluster::RING_HASH);
1090  // Setup outlier failure percentage parameters.
1091  // Any failure will cause an potential ejection with the probability of 100%
1092  // (to eliminate flakiness of the test).
1093  auto* interval = cluster.mutable_outlier_detection()->mutable_interval();
1094  auto* base_time =
1095  cluster.mutable_outlier_detection()->mutable_base_ejection_time();
1096  interval->set_nanos(100000000 * grpc_test_slowdown_factor());
1097  base_time->set_seconds(1 * grpc_test_slowdown_factor());
1098  balancer_->ads_service()->SetCdsResource(cluster);
1099  auto new_route_config = default_route_config_;
1100  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
1101  auto* hash_policy = route->mutable_route()->add_hash_policy();
1102  hash_policy->mutable_header()->set_header_name("address_hash");
1103  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
1104  new_route_config);
1105  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
1106  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
1107  // Note each type of RPC will contains a header value that will always be
1108  // hashed to a specific backend as the header value matches the value used
1109  // to create the entry in the ring.
1110  std::vector<std::pair<std::string, std::string>> metadata = {
1111  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
1112  std::vector<std::pair<std::string, std::string>> metadata1 = {
1113  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
1114  const auto rpc_options = RpcOptions().set_metadata(metadata);
1115  const auto rpc_options1 = RpcOptions().set_metadata(std::move(metadata1));
1116  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
1117  WaitForBackendOptions(), rpc_options);
1118  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
1119  WaitForBackendOptions(), rpc_options1);
1120  // Cause an error and wait for 1 outlier detection interval to pass
1121  CheckRpcSendFailure(DEBUG_LOCATION, StatusCode::CANCELLED, "",
1122  RpcOptions()
1123  .set_metadata(std::move(metadata))
1124  .set_server_expected_error(StatusCode::CANCELLED));
1126  ResetBackendCounters();
1127  // 1 backend experenced failure, but since there is no counting there is no
1128  // ejection. Both backends are still getting the RPCs intended for them.
1129  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
1130  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
1131  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
1132  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
1133 }
1134 
1135 // GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION not configured so feature
1136 // disabled.
1137 TEST_P(OutlierDetectionTest,
1138  SuccessRateAndFailurePercentageEjectionPolicyDisabled) {
1139  CreateAndStartBackends(4);
1140  auto cluster = default_cluster_;
1141  cluster.set_lb_policy(Cluster::RING_HASH);
1142  // Setup outlier failure percentage parameters.
1143  // Any failure will cause an potential ejection with the probability of 100%
1144  // (to eliminate flakiness of the test).
1145  auto* interval = cluster.mutable_outlier_detection()->mutable_interval();
1146  interval->set_nanos(100000000 * grpc_test_slowdown_factor());
1147  cluster.mutable_outlier_detection()
1148  ->mutable_max_ejection_percent()
1149  ->set_value(50);
1150  // This stdev of 500 will ensure the number of ok RPC and error RPC we send
1151  // will make 1 outlier out of the 4 backends.
1152  cluster.mutable_outlier_detection()
1153  ->mutable_success_rate_stdev_factor()
1154  ->set_value(500);
1155  cluster.mutable_outlier_detection()
1156  ->mutable_enforcing_success_rate()
1157  ->set_value(100);
1158  cluster.mutable_outlier_detection()
1159  ->mutable_success_rate_minimum_hosts()
1160  ->set_value(1);
1161  cluster.mutable_outlier_detection()
1162  ->mutable_success_rate_request_volume()
1163  ->set_value(1);
1164  cluster.mutable_outlier_detection()
1165  ->mutable_failure_percentage_threshold()
1166  ->set_value(0);
1167  cluster.mutable_outlier_detection()
1168  ->mutable_enforcing_failure_percentage()
1169  ->set_value(100);
1170  cluster.mutable_outlier_detection()
1171  ->mutable_failure_percentage_minimum_hosts()
1172  ->set_value(1);
1173  cluster.mutable_outlier_detection()
1174  ->mutable_failure_percentage_request_volume()
1175  ->set_value(1);
1176  balancer_->ads_service()->SetCdsResource(cluster);
1177  auto new_route_config = default_route_config_;
1178  auto* route = new_route_config.mutable_virtual_hosts(0)->mutable_routes(0);
1179  auto* hash_policy = route->mutable_route()->add_hash_policy();
1180  hash_policy->mutable_header()->set_header_name("address_hash");
1181  SetListenerAndRouteConfiguration(balancer_.get(), default_listener_,
1182  new_route_config);
1183  EdsResourceArgs args({{"locality0", CreateEndpointsForBackends()}});
1184  balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
1185  // Note each type of RPC will contains a header value that will always be
1186  // hashed to a specific backend as the header value matches the value used
1187  // to create the entry in the ring.
1188  std::vector<std::pair<std::string, std::string>> metadata = {
1189  {"address_hash", CreateMetadataValueThatHashesToBackend(0)}};
1190  std::vector<std::pair<std::string, std::string>> metadata1 = {
1191  {"address_hash", CreateMetadataValueThatHashesToBackend(1)}};
1192  std::vector<std::pair<std::string, std::string>> metadata2 = {
1193  {"address_hash", CreateMetadataValueThatHashesToBackend(2)}};
1194  std::vector<std::pair<std::string, std::string>> metadata3 = {
1195  {"address_hash", CreateMetadataValueThatHashesToBackend(3)}};
1196  const auto rpc_options = RpcOptions().set_metadata(metadata);
1197  const auto rpc_options1 = RpcOptions().set_metadata(metadata1);
1198  const auto rpc_options2 = RpcOptions().set_metadata(metadata2);
1199  const auto rpc_options3 = RpcOptions().set_metadata(metadata3);
1200  WaitForBackend(DEBUG_LOCATION, 0, /*check_status=*/nullptr,
1201  WaitForBackendOptions(), rpc_options);
1202  WaitForBackend(DEBUG_LOCATION, 1, /*check_status=*/nullptr,
1203  WaitForBackendOptions(), rpc_options1);
1204  WaitForBackend(DEBUG_LOCATION, 2, /*check_status=*/nullptr,
1205  WaitForBackendOptions(), rpc_options2);
1206  WaitForBackend(DEBUG_LOCATION, 3, /*check_status=*/nullptr,
1207  WaitForBackendOptions(), rpc_options3);
1208  // Cause 2 errors on 1 backend and 1 error on 2 backends and wait for 1
1209  // outlier detection interval to pass. The errors should have caused 2
1210  // ejctionss but since the policy is disabled we are not ejecting any and
1211  // traffic flow as usual and RPCs reach destinated backends.
1212  CheckRpcSendFailure(
1214  RpcOptions().set_metadata(metadata).set_server_expected_error(
1216  CheckRpcSendFailure(
1218  RpcOptions().set_metadata(metadata).set_server_expected_error(
1220  CheckRpcSendFailure(
1222  RpcOptions().set_metadata(metadata1).set_server_expected_error(
1224  CheckRpcSendFailure(
1226  RpcOptions().set_metadata(metadata2).set_server_expected_error(
1229  ResetBackendCounters();
1230  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options);
1231  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options1);
1232  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options2);
1233  CheckRpcSendOk(DEBUG_LOCATION, 100, rpc_options3);
1234  EXPECT_EQ(100, backends_[0]->backend_service()->request_count());
1235  EXPECT_EQ(100, backends_[1]->backend_service()->request_count());
1236  EXPECT_EQ(100, backends_[2]->backend_service()->request_count());
1237  EXPECT_EQ(100, backends_[3]->backend_service()->request_count());
1238 }
1239 
1240 } // namespace
1241 } // namespace testing
1242 } // namespace grpc
1243 
1244 int main(int argc, char** argv) {
1245  grpc::testing::TestEnvironment env(&argc, argv);
1246  ::testing::InitGoogleTest(&argc, argv);
1247  // Make the backup poller poll very frequently in order to pick up
1248  // updates from all the subchannels's FDs.
1249  GPR_GLOBAL_CONFIG_SET(grpc_client_channel_backup_poll_interval_ms, 1);
1250 #if TARGET_OS_IPHONE
1251  // Workaround Apple CFStream bug
1252  gpr_setenv("grpc_cfstream", "0");
1253 #endif
1254  // TODO(roth): This is a hack to ensure that the outlier_detection LB policy
1255  // is always registered at gRPC init time. When the LB policy registry is
1256  // moved to the new CoreConfiguration system, change this to use
1257  // CoreConfiguration::BuildSpecialConfiguration() instead.
1258  gpr_setenv("GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION", "true");
1259  grpc_init();
1260  gpr_unsetenv("GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION");
1261  const auto result = RUN_ALL_TESTS();
1262  grpc_shutdown();
1263  return result;
1264 }
_gevent_test_main.result
result
Definition: _gevent_test_main.py:96
testing
Definition: aws_request_signer_test.cc:25
ipv6_only_
bool ipv6_only_
Definition: client_lb_end2end_test.cc:217
env_var
Definition: win/process.c:40
generate.env
env
Definition: generate.py:37
absl::StrCat
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: abseil-cpp/absl/strings/str_cat.cc:98
metadata
Definition: cq_verifier.cc:48
grpc
Definition: grpcpp/alarm.h:33
route
XdsRouteConfigResource::Route route
Definition: xds_resolver.cc:337
backends_
std::vector< std::unique_ptr< BackendServiceImpl > > backends_
Definition: client_channel_stress_test.cc:333
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
DEBUG_LOCATION
#define DEBUG_LOCATION
Definition: debug_location.h:41
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
no_op_http_filter.h
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
GPR_ASSERT
#define GPR_ASSERT(x)
Definition: include/grpc/impl/codegen/log.h:94
cluster
absl::string_view cluster
Definition: xds_resolver.cc:331
grpc_timeout_milliseconds_to_deadline
gpr_timespec grpc_timeout_milliseconds_to_deadline(int64_t time_ms)
Definition: test/core/util/test_config.cc:89
gpr_sleep_until
GPRAPI void gpr_sleep_until(gpr_timespec until)
grpc_test_slowdown_factor
int64_t grpc_test_slowdown_factor()
Definition: test/core/util/test_config.cc:76
xds_end2end_test_lib.h
grpc::testing::INSTANTIATE_TEST_SUITE_P
INSTANTIATE_TEST_SUITE_P(HistogramTestCases, HistogramTest, ::testing::Range< int >(0, GRPC_STATS_HISTOGRAM_COUNT))
backup_poller.h
RUN_ALL_TESTS
int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:2471
GPR_GLOBAL_CONFIG_SET
#define GPR_GLOBAL_CONFIG_SET(name, value)
Definition: global_config_generic.h:26
tests.unit._exit_scenarios.port
port
Definition: _exit_scenarios.py:179
testing::InitGoogleTest
GTEST_API_ void InitGoogleTest(int *argc, char **argv)
Definition: bloaty/third_party/googletest/googletest/src/gtest.cc:6106
testing::Values
internal::ValueArray< T... > Values(T... v)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest-param-test.h:335
index
int index
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/protobuf.h:1184
grpc::testing::TestEnvironment
Definition: test/core/util/test_config.h:54
main
int main(int argc, char **argv)
Definition: xds_outlier_detection_end2end_test.cc:1244
grpc::testing::EXPECT_EQ
EXPECT_EQ(options.token_exchange_service_uri, "https://foo/exchange")
grpc.StatusCode.CANCELLED
tuple CANCELLED
Definition: src/python/grpcio/grpc/__init__.py:261
grpc::testing::TEST_P
TEST_P(HistogramTest, IncHistogram)
Definition: stats_test.cc:87
run_xds_tests.backend_service
def backend_service
Definition: run_xds_tests.py:3255
grpc_init
GRPCAPI void grpc_init(void)
Definition: init.cc:146
gpr_unsetenv
void gpr_unsetenv(const char *name)
grpc_shutdown
GRPCAPI void grpc_shutdown(void)
Definition: init.cc:209
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
grpc::testing::XdsTestType::Name
static std::string Name(const ::testing::TestParamInfo< XdsTestType > &info)
Definition: xds_end2end_test_lib.h:143
gpr_setenv
void gpr_setenv(const char *name, const char *value)


grpc
Author(s):
autogenerated on Fri May 16 2025 03:00:59