1 //
2 // Copyright 2022 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <grpc/grpc.h>
18
19 #include <array>
20 #include <memory>
21
22 #include "absl/status/status.h"
23 #include "absl/strings/string_view.h"
24 #include "absl/types/span.h"
25 #include "gtest/gtest.h"
26 #include "src/core/resolver/endpoint_addresses.h"
27 #include "src/core/util/orphanable.h"
28 #include "src/core/util/ref_counted_ptr.h"
29 #include "test/core/load_balancing/lb_policy_test_lib.h"
30 #include "test/core/test_util/test_config.h"
31
32 namespace grpc_core {
33 namespace testing {
34 namespace {
35
36 class RoundRobinTest : public LoadBalancingPolicyTest {
37 protected:
RoundRobinTest()38 RoundRobinTest() : LoadBalancingPolicyTest("round_robin") {}
39 };
40
TEST_F(RoundRobinTest,Basic)41 TEST_F(RoundRobinTest, Basic) {
42 const std::array<absl::string_view, 3> kAddresses = {
43 "ipv4:127.0.0.1:441", "ipv4:127.0.0.1:442", "ipv4:127.0.0.1:443"};
44 EXPECT_EQ(ApplyUpdate(BuildUpdate(kAddresses, nullptr), lb_policy()),
45 absl::OkStatus());
46 ExpectRoundRobinStartup(kAddresses);
47 }
48
TEST_F(RoundRobinTest,AddressUpdates)49 TEST_F(RoundRobinTest, AddressUpdates) {
50 const std::array<absl::string_view, 3> kAddresses = {
51 "ipv4:127.0.0.1:441", "ipv4:127.0.0.1:442", "ipv4:127.0.0.1:443"};
52 EXPECT_EQ(ApplyUpdate(BuildUpdate(kAddresses, nullptr), lb_policy()),
53 absl::OkStatus());
54 ExpectRoundRobinStartup(kAddresses);
55 // Send update to remove address 2.
56 EXPECT_EQ(
57 ApplyUpdate(BuildUpdate(absl::MakeSpan(kAddresses).first(2), nullptr),
58 lb_policy()),
59 absl::OkStatus());
60 WaitForRoundRobinListChange(kAddresses, absl::MakeSpan(kAddresses).first(2));
61 // Send update to remove address 0 and re-add address 2.
62 EXPECT_EQ(
63 ApplyUpdate(BuildUpdate(absl::MakeSpan(kAddresses).last(2), nullptr),
64 lb_policy()),
65 absl::OkStatus());
66 WaitForRoundRobinListChange(absl::MakeSpan(kAddresses).first(2),
67 absl::MakeSpan(kAddresses).last(2));
68 }
69
TEST_F(RoundRobinTest,MultipleAddressesPerEndpoint)70 TEST_F(RoundRobinTest, MultipleAddressesPerEndpoint) {
71 constexpr std::array<absl::string_view, 2> kEndpoint1Addresses = {
72 "ipv4:127.0.0.1:443", "ipv4:127.0.0.1:444"};
73 constexpr std::array<absl::string_view, 2> kEndpoint2Addresses = {
74 "ipv4:127.0.0.1:445", "ipv4:127.0.0.1:446"};
75 const std::array<EndpointAddresses, 2> kEndpoints = {
76 MakeEndpointAddresses(kEndpoint1Addresses),
77 MakeEndpointAddresses(kEndpoint2Addresses)};
78 EXPECT_EQ(ApplyUpdate(BuildUpdate(kEndpoints, nullptr), lb_policy_.get()),
79 absl::OkStatus());
80 // RR should have created a subchannel for each address.
81 auto* subchannel1_0 = FindSubchannel(kEndpoint1Addresses[0]);
82 ASSERT_NE(subchannel1_0, nullptr) << "Address: " << kEndpoint1Addresses[0];
83 auto* subchannel1_1 = FindSubchannel(kEndpoint1Addresses[1]);
84 ASSERT_NE(subchannel1_1, nullptr) << "Address: " << kEndpoint1Addresses[1];
85 auto* subchannel2_0 = FindSubchannel(kEndpoint2Addresses[0]);
86 ASSERT_NE(subchannel2_0, nullptr) << "Address: " << kEndpoint2Addresses[0];
87 auto* subchannel2_1 = FindSubchannel(kEndpoint2Addresses[1]);
88 ASSERT_NE(subchannel2_1, nullptr) << "Address: " << kEndpoint2Addresses[1];
89 // PF for each endpoint should try to connect to the first subchannel.
90 EXPECT_TRUE(subchannel1_0->ConnectionRequested());
91 EXPECT_FALSE(subchannel1_1->ConnectionRequested());
92 EXPECT_TRUE(subchannel2_0->ConnectionRequested());
93 EXPECT_FALSE(subchannel2_1->ConnectionRequested());
94 // In the first endpoint, the first subchannel reports CONNECTING.
95 // This causes RR to report CONNECTING.
96 subchannel1_0->SetConnectivityState(GRPC_CHANNEL_CONNECTING);
97 ExpectConnectingUpdate();
98 // In the second endpoint, the first subchannel reports CONNECTING.
99 subchannel2_0->SetConnectivityState(GRPC_CHANNEL_CONNECTING);
100 // In the first endpoint, the first subchannel fails to connect.
101 // This causes PF to start a connection attempt on the second subchannel.
102 subchannel1_0->SetConnectivityState(GRPC_CHANNEL_TRANSIENT_FAILURE,
103 absl::UnavailableError("ugh"));
104 EXPECT_TRUE(subchannel1_1->ConnectionRequested());
105 subchannel1_1->SetConnectivityState(GRPC_CHANNEL_CONNECTING);
106 // In the second endpoint, the first subchannel becomes connected.
107 // This causes RR to report READY with all RPCs going to a single address.
108 subchannel2_0->SetConnectivityState(GRPC_CHANNEL_READY);
109 auto picker = WaitForConnected();
110 ExpectRoundRobinPicks(picker.get(), {kEndpoint2Addresses[0]});
111 // In the first endpoint, the second subchannel becomes connected.
112 // This causes RR to add it to the rotation.
113 subchannel1_1->SetConnectivityState(GRPC_CHANNEL_READY);
114 WaitForRoundRobinListChange({kEndpoint2Addresses[0]},
115 {kEndpoint1Addresses[1], kEndpoint2Addresses[0]});
116 // No more connection attempts triggered.
117 EXPECT_FALSE(subchannel1_0->ConnectionRequested());
118 EXPECT_FALSE(subchannel1_1->ConnectionRequested());
119 EXPECT_FALSE(subchannel2_0->ConnectionRequested());
120 EXPECT_FALSE(subchannel2_1->ConnectionRequested());
121 // First endpoint first subchannel finishes backoff, but this doesn't
122 // affect anything -- in fact, PF isn't even watching this subchannel
123 // anymore, since it's connected to the other one. However, this
124 // ensures that the subchannel is in the right state when we try to
125 // reconnect below.
126 subchannel1_0->SetConnectivityState(GRPC_CHANNEL_IDLE);
127 EXPECT_FALSE(subchannel1_0->ConnectionRequested());
128 // Endpoint 1 switches to a different address.
129 ExpectEndpointAddressChange(kEndpoint1Addresses, 1, 0, [&]() {
130 // RR will remove the endpoint from the rotation when it becomes
131 // disconnected.
132 WaitForRoundRobinListChange(
133 {kEndpoint1Addresses[1], kEndpoint2Addresses[0]},
134 {kEndpoint2Addresses[0]});
135 });
136 // Then RR will re-add the endpoint with the new address.
137 WaitForRoundRobinListChange({kEndpoint2Addresses[0]},
138 {kEndpoint1Addresses[0], kEndpoint2Addresses[0]});
139 // No more connection attempts triggered.
140 EXPECT_FALSE(subchannel1_0->ConnectionRequested());
141 EXPECT_FALSE(subchannel1_1->ConnectionRequested());
142 EXPECT_FALSE(subchannel2_0->ConnectionRequested());
143 EXPECT_FALSE(subchannel2_1->ConnectionRequested());
144 }
145
146 // TODO(roth): Add test cases:
147 // - empty address list
148 // - subchannels failing connection attempts
149
150 } // namespace
151 } // namespace testing
152 } // namespace grpc_core
153
main(int argc,char ** argv)154 int main(int argc, char** argv) {
155 ::testing::InitGoogleTest(&argc, argv);
156 grpc::testing::TestEnvironment env(&argc, argv);
157 return RUN_ALL_TESTS();
158 }
159