@@ -275,12 +275,23 @@ void SubsetLoadBalancer::update(uint32_t priority, const HostVector& hosts_added
275275
276276bool 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
460471void 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