round_robin: avoid spurious updates when switching subchannel lists (#31939)
* round_robin: avoid spurious updates when switching subchannel lists * clang-format * clang-tidypull/31946/head
parent
896bfe373f
commit
1482bf9d18
10 changed files with 233 additions and 27 deletions
@ -0,0 +1,118 @@ |
|||||||
|
//
|
||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
#include "absl/status/status.h" |
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
#include "gtest/gtest.h" |
||||||
|
|
||||||
|
#include <grpc/grpc.h> |
||||||
|
|
||||||
|
#include "src/core/lib/channel/channel_args.h" |
||||||
|
#include "src/core/lib/gprpp/orphanable.h" |
||||||
|
#include "src/core/lib/gprpp/ref_counted_ptr.h" |
||||||
|
#include "src/core/lib/load_balancing/lb_policy.h" |
||||||
|
#include "test/core/client_channel/lb_policy/lb_policy_test_lib.h" |
||||||
|
#include "test/core/util/test_config.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace testing { |
||||||
|
namespace { |
||||||
|
|
||||||
|
class RoundRobinTest : public LoadBalancingPolicyTest { |
||||||
|
protected: |
||||||
|
RoundRobinTest() : lb_policy_(MakeLbPolicy("round_robin")) {} |
||||||
|
|
||||||
|
void ExpectStartup(absl::Span<const absl::string_view> addresses) { |
||||||
|
EXPECT_EQ(ApplyUpdate(BuildUpdate(addresses), lb_policy_.get()), |
||||||
|
absl::OkStatus()); |
||||||
|
// Expect the initial CONNECTNG update with a picker that queues.
|
||||||
|
ExpectConnectingUpdate(); |
||||||
|
// RR should have created a subchannel for each address.
|
||||||
|
for (size_t i = 0; i < addresses.size(); ++i) { |
||||||
|
auto* subchannel = FindSubchannel(addresses[i]); |
||||||
|
ASSERT_NE(subchannel, nullptr) << "Address: " << addresses[i]; |
||||||
|
// RR should ask each subchannel to connect.
|
||||||
|
EXPECT_TRUE(subchannel->ConnectionRequested()); |
||||||
|
// The subchannel will connect successfully.
|
||||||
|
subchannel->SetConnectivityState(GRPC_CHANNEL_CONNECTING); |
||||||
|
subchannel->SetConnectivityState(GRPC_CHANNEL_READY); |
||||||
|
// As each subchannel becomes READY, we should get a new picker that
|
||||||
|
// includes the behavior. Note that there may be any number of
|
||||||
|
// duplicate updates for the previous state in the queue before the
|
||||||
|
// update that we actually want to see.
|
||||||
|
if (i == 0) { |
||||||
|
// When the first subchannel becomes READY, accept any number of
|
||||||
|
// CONNECTING updates with a picker that queues followed by a READY
|
||||||
|
// update with a picker that repeatedly returns only the first address.
|
||||||
|
auto picker = WaitForConnected(); |
||||||
|
ExpectRoundRobinPicks(picker.get(), {addresses[0]}); |
||||||
|
} else { |
||||||
|
// When each subsequent subchannel becomes READY, we accept any number
|
||||||
|
// of READY updates where the picker returns only the previously
|
||||||
|
// connected subchannel(s) followed by a READY update where the picker
|
||||||
|
// returns the previously connected subchannel(s) *and* the newly
|
||||||
|
// connected subchannel.
|
||||||
|
WaitForRoundRobinListChange( |
||||||
|
absl::MakeSpan(addresses).subspan(0, i), |
||||||
|
absl::MakeSpan(addresses).subspan(0, i + 1)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
OrphanablePtr<LoadBalancingPolicy> lb_policy_; |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(RoundRobinTest, Basic) { |
||||||
|
const std::array<absl::string_view, 3> kAddresses = { |
||||||
|
"ipv4:127.0.0.1:441", "ipv4:127.0.0.1:442", "ipv4:127.0.0.1:443"}; |
||||||
|
ExpectStartup(kAddresses); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(RoundRobinTest, AddressUpdates) { |
||||||
|
const std::array<absl::string_view, 3> kAddresses = { |
||||||
|
"ipv4:127.0.0.1:441", "ipv4:127.0.0.1:442", "ipv4:127.0.0.1:443"}; |
||||||
|
ExpectStartup(kAddresses); |
||||||
|
// Send update to remove address 2.
|
||||||
|
EXPECT_EQ(ApplyUpdate(BuildUpdate(absl::MakeSpan(kAddresses).first(2)), |
||||||
|
lb_policy_.get()), |
||||||
|
absl::OkStatus()); |
||||||
|
WaitForRoundRobinListChange(kAddresses, absl::MakeSpan(kAddresses).first(2)); |
||||||
|
// Send update to remove address 0 and re-add address 2.
|
||||||
|
EXPECT_EQ(ApplyUpdate(BuildUpdate(absl::MakeSpan(kAddresses).last(2)), |
||||||
|
lb_policy_.get()), |
||||||
|
absl::OkStatus()); |
||||||
|
WaitForRoundRobinListChange(absl::MakeSpan(kAddresses).first(2), |
||||||
|
absl::MakeSpan(kAddresses).last(2)); |
||||||
|
} |
||||||
|
|
||||||
|
// TODO(roth): Add test cases:
|
||||||
|
// - empty address list
|
||||||
|
// - subchannels failing connection attempts
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
grpc::testing::TestEnvironment env(&argc, argv); |
||||||
|
grpc_init(); |
||||||
|
int ret = RUN_ALL_TESTS(); |
||||||
|
grpc_shutdown(); |
||||||
|
return ret; |
||||||
|
} |
Loading…
Reference in new issue