-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
upstream: Introducing close_connections_on_host_set_change property #7675
Changes from all commits
ba2f255
92ad8d5
888640c
77cf3c6
853343b
b336b6b
36d0ee4
d351954
09b185a
42cfdb2
213ed5a
ad3ba43
92019f1
6900e8a
88abfa4
5c4129c
ba07c8e
22f49f4
f4a1a4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -199,7 +199,7 @@ class MockedUpdatedClusterManagerImpl : public TestClusterManagerImpl { | |
local_cluster_update_.post(priority, hosts_added, hosts_removed); | ||
} | ||
|
||
void postThreadLocalHostRemoval(const Cluster&, const HostVector& hosts_removed) override { | ||
void postThreadLocalDrainConnections(const Cluster&, const HostVector& hosts_removed) override { | ||
local_hosts_removed_.post(hosts_removed); | ||
} | ||
|
||
|
@@ -3321,6 +3321,195 @@ TEST_F(TcpKeepaliveTest, TcpKeepaliveWithAllOptions) { | |
expectSetsockoptSoKeepalive(7, 4, 1); | ||
} | ||
|
||
TEST_F(ClusterManagerImplTest, ConnPoolsDrainedOnHostSetChange) { | ||
const std::string yaml = R"EOF( | ||
static_resources: | ||
clusters: | ||
- name: cluster_1 | ||
connect_timeout: 0.250s | ||
lb_policy: ROUND_ROBIN | ||
type: STATIC | ||
common_lb_config: | ||
close_connections_on_host_set_change: true | ||
)EOF"; | ||
|
||
ReadyWatcher initialized; | ||
EXPECT_CALL(initialized, ready()); | ||
|
||
create(parseBootstrapFromV2Yaml(yaml)); | ||
|
||
// Set up for an initialize callback. | ||
cluster_manager_->setInitializedCb([&]() -> void { initialized.ready(); }); | ||
|
||
std::unique_ptr<MockClusterUpdateCallbacks> callbacks(new NiceMock<MockClusterUpdateCallbacks>()); | ||
ClusterUpdateCallbacksHandlePtr cb = | ||
cluster_manager_->addThreadLocalClusterUpdateCallbacks(*callbacks); | ||
|
||
EXPECT_FALSE(cluster_manager_->get("cluster_1")->info()->addedViaApi()); | ||
|
||
// Verify that we get no hosts when the HostSet is empty. | ||
EXPECT_EQ(nullptr, cluster_manager_->httpConnPoolForCluster( | ||
"cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); | ||
EXPECT_EQ(nullptr, cluster_manager_->tcpConnPoolForCluster("cluster_1", ResourcePriority::Default, | ||
nullptr, nullptr)); | ||
EXPECT_EQ(nullptr, | ||
cluster_manager_->tcpConnForCluster("cluster_1", nullptr, nullptr).connection_); | ||
|
||
Cluster& cluster = cluster_manager_->activeClusters().begin()->second; | ||
|
||
// Set up the HostSet. | ||
HostSharedPtr host1 = makeTestHost(cluster.info(), "tcp://127.0.0.1:80"); | ||
HostSharedPtr host2 = makeTestHost(cluster.info(), "tcp://127.0.0.1:81"); | ||
|
||
HostVector hosts{host1, host2}; | ||
auto hosts_ptr = std::make_shared<HostVector>(hosts); | ||
|
||
// Sending non-mergeable updates. | ||
cluster.prioritySet().updateHosts( | ||
0, HostSetImpl::partitionHosts(hosts_ptr, HostsPerLocalityImpl::empty()), nullptr, hosts, {}, | ||
100); | ||
|
||
EXPECT_EQ(1, factory_.stats_.counter("cluster_manager.cluster_updated").value()); | ||
EXPECT_EQ(0, factory_.stats_.counter("cluster_manager.cluster_updated_via_merge").value()); | ||
EXPECT_EQ(0, factory_.stats_.counter("cluster_manager.update_merge_cancelled").value()); | ||
|
||
EXPECT_CALL(factory_, allocateConnPool_(_, _)) | ||
.Times(3) | ||
.WillRepeatedly(ReturnNew<Http::ConnectionPool::MockInstance>()); | ||
|
||
EXPECT_CALL(factory_, allocateTcpConnPool_(_)) | ||
.Times(3) | ||
.WillRepeatedly(ReturnNew<Tcp::ConnectionPool::MockInstance>()); | ||
|
||
// This should provide us a CP for each of the above hosts. | ||
Http::ConnectionPool::MockInstance* cp1 = | ||
dynamic_cast<Http::ConnectionPool::MockInstance*>(cluster_manager_->httpConnPoolForCluster( | ||
"cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); | ||
// Create persistent connection for host2. | ||
Http::ConnectionPool::MockInstance* cp2 = | ||
dynamic_cast<Http::ConnectionPool::MockInstance*>(cluster_manager_->httpConnPoolForCluster( | ||
"cluster_1", ResourcePriority::Default, Http::Protocol::Http2, nullptr)); | ||
|
||
Tcp::ConnectionPool::MockInstance* tcp1 = | ||
dynamic_cast<Tcp::ConnectionPool::MockInstance*>(cluster_manager_->tcpConnPoolForCluster( | ||
"cluster_1", ResourcePriority::Default, nullptr, nullptr)); | ||
|
||
Tcp::ConnectionPool::MockInstance* tcp2 = | ||
dynamic_cast<Tcp::ConnectionPool::MockInstance*>(cluster_manager_->tcpConnPoolForCluster( | ||
"cluster_1", ResourcePriority::Default, nullptr, nullptr)); | ||
|
||
EXPECT_NE(cp1, cp2); | ||
EXPECT_NE(tcp1, tcp2); | ||
|
||
EXPECT_CALL(*cp2, addDrainedCallback(_)) | ||
.WillOnce(Invoke([](Http::ConnectionPool::Instance::DrainedCb cb) { cb(); })); | ||
|
||
EXPECT_CALL(*cp1, addDrainedCallback(_)) | ||
.WillOnce(Invoke([](Http::ConnectionPool::Instance::DrainedCb cb) { cb(); })); | ||
|
||
EXPECT_CALL(*tcp1, addDrainedCallback(_)) | ||
.WillOnce(Invoke([](Tcp::ConnectionPool::Instance::DrainedCb cb) { cb(); })); | ||
|
||
EXPECT_CALL(*tcp2, addDrainedCallback(_)) | ||
.WillOnce(Invoke([](Tcp::ConnectionPool::Instance::DrainedCb cb) { cb(); })); | ||
|
||
HostVector hosts_removed; | ||
hosts_removed.push_back(host2); | ||
|
||
// This update should drain all connection pools (host1, host2). | ||
cluster.prioritySet().updateHosts( | ||
0, HostSetImpl::partitionHosts(hosts_ptr, HostsPerLocalityImpl::empty()), nullptr, {}, | ||
hosts_removed, 100); | ||
|
||
// Recreate connection pool for host1. | ||
cp1 = dynamic_cast<Http::ConnectionPool::MockInstance*>(cluster_manager_->httpConnPoolForCluster( | ||
"cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); | ||
|
||
tcp1 = dynamic_cast<Tcp::ConnectionPool::MockInstance*>(cluster_manager_->tcpConnPoolForCluster( | ||
"cluster_1", ResourcePriority::Default, nullptr, nullptr)); | ||
|
||
HostSharedPtr host3 = makeTestHost(cluster.info(), "tcp://127.0.0.1:82"); | ||
|
||
HostVector hosts_added; | ||
hosts_added.push_back(host3); | ||
|
||
EXPECT_CALL(*cp1, addDrainedCallback(_)) | ||
.WillOnce(Invoke([](Http::ConnectionPool::Instance::DrainedCb cb) { cb(); })); | ||
|
||
EXPECT_CALL(*tcp1, addDrainedCallback(_)) | ||
.WillOnce(Invoke([](Tcp::ConnectionPool::Instance::DrainedCb cb) { cb(); })); | ||
|
||
// Adding host3 should drain connection pool for host1. | ||
cluster.prioritySet().updateHosts( | ||
0, HostSetImpl::partitionHosts(hosts_ptr, HostsPerLocalityImpl::empty()), nullptr, | ||
hosts_added, {}, 100); | ||
} | ||
|
||
TEST_F(ClusterManagerImplTest, ConnPoolsNotDrainedOnHostSetChange) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any way to have some common code for this test and the previous test since I'm guessing they are mostly the same? Also I wonder if the not draining case is already well covered but I'm fine either way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mattklein123 don't think is worth the effort to introduce common code/method to share between two tests, since that code would be very specific to those 2 tests. Think is nice to still have 2 tests, to test when property is set vs not set. Could try to reduce the code size of second test (basically we want to ensure that connections are not drained on host addition). WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apologies, i have to do force push to fix DCO (DCO broke after committing suggestions). Reduced test size in last commit, waiting for opinions. |
||
const std::string yaml = R"EOF( | ||
static_resources: | ||
clusters: | ||
- name: cluster_1 | ||
connect_timeout: 0.250s | ||
lb_policy: ROUND_ROBIN | ||
type: STATIC | ||
)EOF"; | ||
|
||
ReadyWatcher initialized; | ||
EXPECT_CALL(initialized, ready()); | ||
create(parseBootstrapFromV2Yaml(yaml)); | ||
|
||
// Set up for an initialize callback. | ||
cluster_manager_->setInitializedCb([&]() -> void { initialized.ready(); }); | ||
|
||
std::unique_ptr<MockClusterUpdateCallbacks> callbacks(new NiceMock<MockClusterUpdateCallbacks>()); | ||
ClusterUpdateCallbacksHandlePtr cb = | ||
cluster_manager_->addThreadLocalClusterUpdateCallbacks(*callbacks); | ||
|
||
Cluster& cluster = cluster_manager_->activeClusters().begin()->second; | ||
|
||
// Set up the HostSet. | ||
HostSharedPtr host1 = makeTestHost(cluster.info(), "tcp://127.0.0.1:80"); | ||
|
||
HostVector hosts{host1}; | ||
auto hosts_ptr = std::make_shared<HostVector>(hosts); | ||
|
||
// Sending non-mergeable updates. | ||
cluster.prioritySet().updateHosts( | ||
0, HostSetImpl::partitionHosts(hosts_ptr, HostsPerLocalityImpl::empty()), nullptr, hosts, {}, | ||
100); | ||
|
||
EXPECT_CALL(factory_, allocateConnPool_(_, _)) | ||
.Times(1) | ||
.WillRepeatedly(ReturnNew<Http::ConnectionPool::MockInstance>()); | ||
|
||
EXPECT_CALL(factory_, allocateTcpConnPool_(_)) | ||
.Times(1) | ||
.WillRepeatedly(ReturnNew<Tcp::ConnectionPool::MockInstance>()); | ||
|
||
// This should provide us a CP for each of the above hosts. | ||
Http::ConnectionPool::MockInstance* cp1 = | ||
dynamic_cast<Http::ConnectionPool::MockInstance*>(cluster_manager_->httpConnPoolForCluster( | ||
"cluster_1", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); | ||
|
||
Tcp::ConnectionPool::MockInstance* tcp1 = | ||
dynamic_cast<Tcp::ConnectionPool::MockInstance*>(cluster_manager_->tcpConnPoolForCluster( | ||
"cluster_1", ResourcePriority::Default, nullptr, nullptr)); | ||
|
||
HostSharedPtr host2 = makeTestHost(cluster.info(), "tcp://127.0.0.1:82"); | ||
HostVector hosts_added; | ||
hosts_added.push_back(host2); | ||
|
||
// No connection pools should be drained. | ||
EXPECT_CALL(*cp1, drainConnections()).Times(0); | ||
EXPECT_CALL(*tcp1, drainConnections()).Times(0); | ||
|
||
// No connection pools should be drained. | ||
cluster.prioritySet().updateHosts( | ||
0, HostSetImpl::partitionHosts(hosts_ptr, HostsPerLocalityImpl::empty()), nullptr, | ||
hosts_added, {}, 100); | ||
} | ||
|
||
} // namespace | ||
} // namespace Upstream | ||
} // namespace Envoy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed this on my last pass: this callback only triggers when hosts are added or removed. For example, a change to host weights would not trigger this callback, instead only triggering callbacks added with
addPriorityUpdateCb
for the appropriate priority. Not sure if this is intended or not, just pointing it out.