Skip to content

Commit 17efc83

Browse files
rgs1mattklein123
authored andcommitted
Avoid extra predicate() calls (#3869)
In `SubsetLoadBalancer::HostSubsetImpl::update()` we have repeated calls to `predicate()`. These calls are expensive mostly because of `Envoy::Config::Metadata::metadataValue()` calls. Furthermore, metadata is guarded by a lock since #3814 — so there is some contention too. This change improves things by doing two things: * avoid use of `predicate()` to derive `healthy_hosts_per_locality` * cache `predicate()` calls for the special case of only metadata changing (current hosts == hosts added) Signed-off-by: Raul Gutierrez Segales <[email protected]>
1 parent 2a68458 commit 17efc83

File tree

1 file changed

+40
-8
lines changed

1 file changed

+40
-8
lines changed

source/common/upstream/subset_lb.cc

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,12 +275,23 @@ void SubsetLoadBalancer::update(uint32_t priority, const HostVector& hosts_added
275275

276276
bool SubsetLoadBalancer::hostMatches(const SubsetMetadata& kvs, const Host& host) {
277277
const envoy::api::v2::core::Metadata& host_metadata = *host.metadata();
278+
const auto filter_it =
279+
host_metadata.filter_metadata().find(Config::MetadataFilters::get().ENVOY_LB);
280+
281+
if (filter_it == host_metadata.filter_metadata().end()) {
282+
return kvs.size() == 0;
283+
}
284+
285+
const ProtobufWkt::Struct& data_struct = filter_it->second;
286+
const auto& fields = data_struct.fields();
278287

279288
for (const auto& kv : kvs) {
280-
const ProtobufWkt::Value& host_value = Config::Metadata::metadataValue(
281-
host_metadata, Config::MetadataFilters::get().ENVOY_LB, kv.first);
289+
const auto entry_it = fields.find(kv.first);
290+
if (entry_it == fields.end()) {
291+
return false;
292+
}
282293

283-
if (!ValueUtil::equal(host_value, kv.second)) {
294+
if (!ValueUtil::equal(entry_it->second, kv.second)) {
284295
return false;
285296
}
286297
}
@@ -460,9 +471,12 @@ SubsetLoadBalancer::PrioritySubsetImpl::PrioritySubsetImpl(const SubsetLoadBalan
460471
void SubsetLoadBalancer::HostSubsetImpl::update(const HostVector& hosts_added,
461472
const HostVector& hosts_removed,
462473
std::function<bool(const Host&)> predicate) {
474+
std::unordered_set<HostSharedPtr> predicate_added;
475+
463476
HostVector filtered_added;
464477
for (const auto host : hosts_added) {
465478
if (predicate(*host)) {
479+
predicate_added.insert(host);
466480
filtered_added.emplace_back(host);
467481
}
468482
}
@@ -477,20 +491,38 @@ void SubsetLoadBalancer::HostSubsetImpl::update(const HostVector& hosts_added,
477491
HostVectorSharedPtr hosts(new HostVector());
478492
HostVectorSharedPtr healthy_hosts(new HostVector());
479493

494+
// It's possible that hosts_added == original_host_set_.hosts(), e.g.: when
495+
// calling refreshSubsets() if only metadata change. If so, we can avoid the
496+
// predicate() call.
480497
for (const auto host : original_host_set_.hosts()) {
481-
if (predicate(*host)) {
498+
bool host_seen = predicate_added.count(host) == 1;
499+
if (host_seen || predicate(*host)) {
482500
hosts->emplace_back(host);
483501
if (host->healthy()) {
484502
healthy_hosts->emplace_back(host);
485503
}
486504
}
487505
}
488506

489-
HostsPerLocalityConstSharedPtr hosts_per_locality =
490-
original_host_set_.hostsPerLocality().filter(predicate);
507+
// Calling predicate() is expensive since it involves metadata lookups; so we
508+
// avoid it in the 2nd call to filter() by using the result from the first call
509+
// to filter() as the starting point.
510+
//
511+
// Also, if we only have one locality we can avoid the first call to filter() by
512+
// just creating a new HostsPerLocality from the list of all hosts.
513+
//
514+
// TODO(rgs1): merge these two filter() calls in one loop.
515+
HostsPerLocalityConstSharedPtr hosts_per_locality;
516+
517+
if (original_host_set_.hostsPerLocality().get().size() == 1) {
518+
hosts_per_locality.reset(
519+
new HostsPerLocalityImpl(*hosts, original_host_set_.hostsPerLocality().hasLocalLocality()));
520+
} else {
521+
hosts_per_locality = original_host_set_.hostsPerLocality().filter(predicate);
522+
}
523+
491524
HostsPerLocalityConstSharedPtr healthy_hosts_per_locality =
492-
original_host_set_.hostsPerLocality().filter(
493-
[&predicate](const Host& host) { return predicate(host) && host.healthy(); });
525+
hosts_per_locality->filter([](const Host& host) { return host.healthy(); });
494526

495527
if (locality_weight_aware_) {
496528
HostSetImpl::updateHosts(hosts, healthy_hosts, hosts_per_locality, healthy_hosts_per_locality,

0 commit comments

Comments
 (0)