Skip to content
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

Fix healthchecks that do not persist when priority is changed #35748

Merged
merged 1 commit into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ minor_behavior_changes:

bug_fixes:
# *Changes expected to improve the state of the world and are unlikely to have negative effects*
- area: upstream
change: |
Use previously calculated healthcheck when endpoints move priority levels repeatedly.
- area: dispatcher
change: |
Update approximate now after polling instead of before polling. This is only used by QUIC.
Expand Down
16 changes: 13 additions & 3 deletions source/common/upstream/upstream_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,7 @@ void MainPrioritySetImpl::updateHosts(uint32_t priority, UpdateHostsParams&& upd
HostMapConstSharedPtr cross_priority_host_map) {
ASSERT(cross_priority_host_map == nullptr,
"External cross-priority host map is meaningless to MainPrioritySetImpl");
updateCrossPriorityHostMap(hosts_added, hosts_removed);
updateCrossPriorityHostMap(priority, hosts_added, hosts_removed);

PrioritySetImpl::updateHosts(priority, std::move(update_hosts_params), locality_weights,
hosts_added, hosts_removed, seed, weighted_priority_health,
Expand All @@ -972,7 +972,8 @@ HostMapConstSharedPtr MainPrioritySetImpl::crossPriorityHostMap() const {
return const_cross_priority_host_map_;
}

void MainPrioritySetImpl::updateCrossPriorityHostMap(const HostVector& hosts_added,
void MainPrioritySetImpl::updateCrossPriorityHostMap(uint32_t priority,
const HostVector& hosts_added,
const HostVector& hosts_removed) {
if (hosts_added.empty() && hosts_removed.empty()) {
// No new hosts have been added and no old hosts have been removed.
Expand All @@ -987,7 +988,16 @@ void MainPrioritySetImpl::updateCrossPriorityHostMap(const HostVector& hosts_add
}

for (const auto& host : hosts_removed) {
mutable_cross_priority_host_map_->erase(addressToString(host->address()));
const auto host_address = addressToString(host->address());
const auto existing_host = mutable_cross_priority_host_map_->find(host_address);
if (existing_host != mutable_cross_priority_host_map_->end()) {
// Only delete from the current priority to protect from situations where
// the add operation was already executed and has already moved the metadata of the host
// from a higher priority value to a lower priority value.
if (existing_host->second->priority() == priority) {
mutable_cross_priority_host_map_->erase(host_address);
}
}
}

for (const auto& host : hosts_added) {
Expand Down
3 changes: 2 additions & 1 deletion source/common/upstream/upstream_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,8 @@ class MainPrioritySetImpl : public PrioritySetImpl, public Logger::Loggable<Logg
HostMapConstSharedPtr crossPriorityHostMap() const override;

protected:
void updateCrossPriorityHostMap(const HostVector& hosts_added, const HostVector& hosts_removed);
void updateCrossPriorityHostMap(uint32_t priority, const HostVector& hosts_added,
const HostVector& hosts_removed);

mutable HostMapSharedPtr mutable_cross_priority_host_map_;
};
Expand Down
74 changes: 74 additions & 0 deletions test/extensions/clusters/eds/eds_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1405,6 +1405,80 @@ TEST_F(EdsTest, EndpointMovedToNewPriority) {
}
}

// Verifies that if a endpoint is moved to a new priority multiple times, the health
// check value is preserved.
TEST_F(EdsTest, EndpointMovedToNewPriorityRepeated) {
envoy::config::endpoint::v3::ClusterLoadAssignment cluster_load_assignment;
cluster_load_assignment.set_cluster_name("fare");
resetCluster();

auto health_checker = std::make_shared<MockHealthChecker>();
EXPECT_CALL(*health_checker, start());
EXPECT_CALL(*health_checker, addHostCheckCompleteCb(_)).Times(2);
cluster_->setHealthChecker(health_checker);

auto add_endpoint = [&cluster_load_assignment](int port, int priority) {
auto* endpoints = cluster_load_assignment.add_endpoints();
endpoints->set_priority(priority);

auto* socket_address = endpoints->add_lb_endpoints()
->mutable_endpoint()
->mutable_address()
->mutable_socket_address();
socket_address->set_address("1.2.3.4");
socket_address->set_port_value(port);
};

add_endpoint(80, 0);
add_endpoint(81, 0);

doOnConfigUpdateVerifyNoThrow(cluster_load_assignment);

{
auto& hosts = cluster_->prioritySet().hostSetsPerPriority()[0]->hosts();
EXPECT_EQ(hosts.size(), 2);

// Mark the hosts as healthy
for (auto& host : hosts) {
EXPECT_TRUE(host->healthFlagGet(Host::HealthFlag::FAILED_ACTIVE_HC));
host->healthFlagClear(Host::HealthFlag::FAILED_ACTIVE_HC);
host->healthFlagClear(Host::HealthFlag::PENDING_ACTIVE_HC);
}
}

std::vector<uint32_t> priority_levels = {1, 2, 3, 2, 1};
for (uint32_t priority : priority_levels) {
cluster_load_assignment.clear_endpoints();
add_endpoint(80, priority);
add_endpoint(81, priority);

doOnConfigUpdateVerifyNoThrow(cluster_load_assignment);

{
for (uint32_t i = 0; i < cluster_->prioritySet().hostSetsPerPriority().size(); i++) {
auto& hosts = cluster_->prioritySet().hostSetsPerPriority()[i]->hosts();
if (i == priority) {
// Priorities equal to this one should have the endpoints with port 80 and 81
EXPECT_EQ(hosts.size(), 2);
} else {
// Priorities not equal to this one should now be empty.
EXPECT_EQ(hosts.size(), 0);
}
}
}

{
auto& hosts = cluster_->prioritySet().hostSetsPerPriority()[priority]->hosts();

// The endpoints were healthy, so moving them around should preserve that.
EXPECT_FALSE(hosts[0]->healthFlagGet(Host::HealthFlag::FAILED_ACTIVE_HC));
EXPECT_FALSE(hosts[0]->healthFlagGet(Host::HealthFlag::PENDING_DYNAMIC_REMOVAL));
EXPECT_FALSE(hosts[1]->healthFlagGet(Host::HealthFlag::FAILED_ACTIVE_HC));
EXPECT_FALSE(hosts[1]->healthFlagGet(Host::HealthFlag::PENDING_DYNAMIC_REMOVAL));
}
}
}

// Verifies that if an endpoint is moved between priorities, the health check value
// of the host is preserved
TEST_F(EdsTest, EndpointMoved) {
Expand Down