[LB policies] avoid looping over all endpoints/addresses (#35445)

This should slightly increase per-channel memory but will eliminate some O(n^2) loops with large numbers of endpoints or addresses.

Closes #35445

COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/35445 from markdroth:lb_policies_remove_unnecessary_loop 94465f44ec
PiperOrigin-RevId: 596007480
pull/35331/head
Mark D. Roth 1 year ago committed by Copybara-Service
parent f82f8966f0
commit 5923e6cd1e
  1. 10
      src/core/ext/filters/client_channel/lb_policy/endpoint_list.cc
  2. 5
      src/core/ext/filters/client_channel/lb_policy/endpoint_list.h
  3. 18
      src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc

@ -69,6 +69,9 @@ class EndpointList::Endpoint::Helper
grpc_connectivity_state state, const absl::Status& status,
RefCountedPtr<LoadBalancingPolicy::SubchannelPicker> picker) override {
auto old_state = std::exchange(endpoint_->connectivity_state_, state);
if (!old_state.has_value()) {
++endpoint_->endpoint_list_->num_endpoints_seen_initial_state_;
}
endpoint_->picker_ = std::move(picker);
endpoint_->OnStateUpdate(old_state, state, status);
}
@ -181,11 +184,4 @@ void EndpointList::ResetBackoffLocked() {
}
}
bool EndpointList::AllEndpointsSeenInitialState() const {
for (const auto& endpoint : endpoints_) {
if (!endpoint->connectivity_state().has_value()) return false;
}
return true;
}
} // namespace grpc_core

@ -199,7 +199,9 @@ class EndpointList : public InternallyRefCounted<EndpointList> {
// Returns true if all endpoints have seen their initial connectivity
// state notification.
bool AllEndpointsSeenInitialState() const;
bool AllEndpointsSeenInitialState() const {
return num_endpoints_seen_initial_state_ == size();
}
private:
// Returns the parent policy's helper. Needed because the accessor
@ -210,6 +212,7 @@ class EndpointList : public InternallyRefCounted<EndpointList> {
RefCountedPtr<LoadBalancingPolicy> policy_;
const char* tracer_;
std::vector<OrphanablePtr<Endpoint>> endpoints_;
size_t num_endpoints_seen_initial_state_ = 0;
};
} // namespace grpc_core

@ -224,7 +224,9 @@ class PickFirst : public LoadBalancingPolicy {
private:
// Returns true if all subchannels have seen their initial
// connectivity state notifications.
bool AllSubchannelsSeenInitialState();
bool AllSubchannelsSeenInitialState() const {
return num_subchannels_seen_initial_notification_ == size();
}
// Looks through subchannels_ starting from attempting_index_ to
// find the first one not currently in TRANSIENT_FAILURE, then
@ -255,6 +257,8 @@ class PickFirst : public LoadBalancingPolicy {
// TODO(roth): Remove this when we remove the Happy Eyeballs experiment.
bool in_transient_failure_ = false;
size_t num_subchannels_seen_initial_notification_ = 0;
// The index into subchannels_ to which we are currently attempting
// to connect during the initial Happy Eyeballs pass. Once the
// initial pass is over, this will be equal to size().
@ -754,6 +758,11 @@ void PickFirst::SubchannelList::SubchannelData::OnConnectivityStateChange(
seen_transient_failure_ = true;
subchannel_list_->last_failure_ = connectivity_status_;
}
// If this is the initial connectivity state update for this subchannel,
// increment the counter in the subchannel list.
if (!old_state.has_value()) {
++subchannel_list_->num_subchannels_seen_initial_notification_;
}
// If we haven't yet seen the initial connectivity state notification
// for all subchannels, do nothing.
if (!subchannel_list_->AllSubchannelsSeenInitialState()) return;
@ -1122,13 +1131,6 @@ void PickFirst::SubchannelList::ResetBackoffLocked() {
}
}
bool PickFirst::SubchannelList::AllSubchannelsSeenInitialState() {
for (auto& sd : subchannels_) {
if (!sd.connectivity_state().has_value()) return false;
}
return true;
}
void PickFirst::SubchannelList::StartConnectingNextSubchannel() {
// Find the next subchannel not in state TRANSIENT_FAILURE.
// We skip subchannels in state TRANSIENT_FAILURE to avoid a

Loading…
Cancel
Save