Skip to content

Commit b5e47bf

Browse files
authored
upstream: avoid quadratic time complexity in server initialization (#15237)
Currently ClusterManagerInitHelper.secondary_init_clusters_ is a list. Upon every insert the list gets searched for an already added cluster with the same name. Since in normal circumstances all new clusters have unique names the worst case scenario is triggered and all elements of the list are checked sequentially upon every cluster added. Replace the list with a hash map. Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@intel.com>
1 parent 3f54cd8 commit b5e47bf

File tree

2 files changed

+15
-21
lines changed

2 files changed

+15
-21
lines changed

source/common/upstream/cluster_manager_impl.cc

+13-19
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include <chrono>
44
#include <cstdint>
55
#include <functional>
6-
#include <list>
76
#include <memory>
87
#include <string>
98
#include <vector>
@@ -66,20 +65,12 @@ void ClusterManagerInitHelper::addCluster(ClusterManagerCluster& cm_cluster) {
6665
Cluster& cluster = cm_cluster.cluster();
6766
if (cluster.initializePhase() == Cluster::InitializePhase::Primary) {
6867
// Remove the previous cluster before the cluster object is destroyed.
69-
primary_init_clusters_.remove_if(
70-
[name_to_remove = cluster.info()->name()](ClusterManagerCluster* cluster_iter) {
71-
return cluster_iter->cluster().info()->name() == name_to_remove;
72-
});
73-
primary_init_clusters_.push_back(&cm_cluster);
68+
primary_init_clusters_.insert_or_assign(cm_cluster.cluster().info()->name(), &cm_cluster);
7469
cluster.initialize(initialize_cb);
7570
} else {
7671
ASSERT(cluster.initializePhase() == Cluster::InitializePhase::Secondary);
7772
// Remove the previous cluster before the cluster object is destroyed.
78-
secondary_init_clusters_.remove_if(
79-
[name_to_remove = cluster.info()->name()](ClusterManagerCluster* cluster_iter) {
80-
return cluster_iter->cluster().info()->name() == name_to_remove;
81-
});
82-
secondary_init_clusters_.push_back(&cm_cluster);
73+
secondary_init_clusters_.insert_or_assign(cm_cluster.cluster().info()->name(), &cm_cluster);
8374
if (started_secondary_initialize_) {
8475
// This can happen if we get a second CDS update that adds new clusters after we have
8576
// already started secondary init. In this case, just immediately initialize.
@@ -104,17 +95,20 @@ void ClusterManagerInitHelper::removeCluster(ClusterManagerCluster& cluster) {
10495

10596
// There is a remote edge case where we can remove a cluster via CDS that has not yet been
10697
// initialized. When called via the remove cluster API this code catches that case.
107-
std::list<ClusterManagerCluster*>* cluster_list;
98+
absl::flat_hash_map<std::string, ClusterManagerCluster*>* cluster_map;
10899
if (cluster.cluster().initializePhase() == Cluster::InitializePhase::Primary) {
109-
cluster_list = &primary_init_clusters_;
100+
cluster_map = &primary_init_clusters_;
110101
} else {
111102
ASSERT(cluster.cluster().initializePhase() == Cluster::InitializePhase::Secondary);
112-
cluster_list = &secondary_init_clusters_;
103+
cluster_map = &secondary_init_clusters_;
113104
}
114105

115106
// It is possible that the cluster we are removing has already been initialized, and is not
116-
// present in the initializer list. If so, this is fine.
117-
cluster_list->remove(&cluster);
107+
// present in the initializer map. If so, this is fine.
108+
absl::string_view cluster_name = cluster.cluster().info()->name();
109+
if (cluster_map->at(cluster_name) == &cluster) {
110+
cluster_map->erase(cluster_name);
111+
}
118112
ENVOY_LOG(debug, "cm init: init complete: cluster={} primary={} secondary={}",
119113
cluster.cluster().info()->name(), primary_init_clusters_.size(),
120114
secondary_init_clusters_.size());
@@ -123,13 +117,13 @@ void ClusterManagerInitHelper::removeCluster(ClusterManagerCluster& cluster) {
123117

124118
void ClusterManagerInitHelper::initializeSecondaryClusters() {
125119
started_secondary_initialize_ = true;
126-
// Cluster::initialize() method can modify the list of secondary_init_clusters_ to remove
120+
// Cluster::initialize() method can modify the map of secondary_init_clusters_ to remove
127121
// the item currently being initialized, so we eschew range-based-for and do this complicated
128122
// dance to increment the iterator before calling initialize.
129123
for (auto iter = secondary_init_clusters_.begin(); iter != secondary_init_clusters_.end();) {
130-
ClusterManagerCluster* cluster = *iter;
124+
ClusterManagerCluster* cluster = iter->second;
125+
ENVOY_LOG(debug, "initializing secondary cluster {}", iter->first);
131126
++iter;
132-
ENVOY_LOG(debug, "initializing secondary cluster {}", cluster->cluster().info()->name());
133127
cluster->cluster().initialize([cluster, this] { onClusterInit(*cluster); });
134128
}
135129
}

source/common/upstream/cluster_manager_impl.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@ class ClusterManagerInitHelper : Logger::Loggable<Logger::Id::upstream> {
188188
CdsApi* cds_{};
189189
ClusterManager::PrimaryClustersReadyCallback primary_clusters_initialized_callback_;
190190
ClusterManager::InitializationCompleteCallback initialized_callback_;
191-
std::list<ClusterManagerCluster*> primary_init_clusters_;
192-
std::list<ClusterManagerCluster*> secondary_init_clusters_;
191+
absl::flat_hash_map<std::string, ClusterManagerCluster*> primary_init_clusters_;
192+
absl::flat_hash_map<std::string, ClusterManagerCluster*> secondary_init_clusters_;
193193
State state_{State::Loading};
194194
bool started_secondary_initialize_{};
195195
};

0 commit comments

Comments
 (0)