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

Enable load balancing policy extensions #17400

Merged
merged 10 commits into from
Aug 14, 2021
13 changes: 7 additions & 6 deletions api/envoy/config/cluster/v3/cluster.proto
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ message Cluster {
// this option or not.
CLUSTER_PROVIDED = 6;

// [#not-implemented-hide:] Use the new :ref:`load_balancing_policy
// Use the new :ref:`load_balancing_policy
// <envoy_v3_api_field_config.cluster.v3.Cluster.load_balancing_policy>` field to determine the LB policy.
// [#next-major-version: In the v3 API, we should consider deprecating the lb_policy field
// and instead using the new load_balancing_policy field as the one and only mechanism for
Expand Down Expand Up @@ -720,8 +720,7 @@ message Cluster {

// The :ref:`load balancer type <arch_overview_load_balancing_types>` to use
// when picking a host in the cluster.
// [#comment:TODO: Remove enum constraint :ref:`LOAD_BALANCING_POLICY_CONFIG<envoy_v3_api_enum_value_config.cluster.v3.Cluster.LbPolicy.LOAD_BALANCING_POLICY_CONFIG>` when implemented.]
LbPolicy lb_policy = 6 [(validate.rules).enum = {defined_only: true not_in: 7}];
LbPolicy lb_policy = 6 [(validate.rules).enum = {defined_only: true}];

// Setting this is required for specifying members of
// :ref:`STATIC<envoy_v3_api_enum_value_config.cluster.v3.Cluster.DiscoveryType.STATIC>`,
Expand Down Expand Up @@ -1008,7 +1007,7 @@ message Cluster {
// servers of this cluster.
repeated Filter filters = 40;

// [#not-implemented-hide:] New mechanism for LB policy configuration. Used only if the
// New mechanism for LB policy configuration. Used only if the
// :ref:`lb_policy<envoy_v3_api_field_config.cluster.v3.Cluster.lb_policy>` field has the value
// :ref:`LOAD_BALANCING_POLICY_CONFIG<envoy_v3_api_enum_value_config.cluster.v3.Cluster.LbPolicy.LOAD_BALANCING_POLICY_CONFIG>`.
LoadBalancingPolicy load_balancing_policy = 41;
Expand Down Expand Up @@ -1073,7 +1072,7 @@ message Cluster {
bool connection_pool_per_downstream_connection = 51;
}

// [#not-implemented-hide:] Extensible load balancing policy configuration.
// Extensible load balancing policy configuration.
pianiststickman marked this conversation as resolved.
Show resolved Hide resolved
//
// Every LB policy defined via this mechanism will be identified via a unique name using reverse
// DNS notation. If the policy needs configuration parameters, it must define a message for its
Expand All @@ -1095,7 +1094,9 @@ message Cluster {
message LoadBalancingPolicy {
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.LoadBalancingPolicy";

// Deprecated and replaced by TypedExtensionConfig.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of deprecating this whole message, let's deprecate the name and typed_config fields and add the new TypedExtensionConfig field inside of this message. That way, if we need to add a new field on a per-policy basis in the future, we'll have somewhere to put it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems to be an anti-pattern, what field do we expect? In general a per-policy field should be added inside the message packed into Any.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's a field only for one individual policy, that would make sense. But if we discover the need to add another field that we want to be set for all policies, then this wrapper would provide a place to put that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we put that into CommonLbConfig then, and include the message in the policy's message? Or is the idea that policies would need to set this field whether they use the common LB config or not?

The verbosity of load_balancing_policy.policy.typed_extension_config.typed_config.foo seems a bit excessive. Is it something we could change if/when we actually have such a common field?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all policies will include CommonLbConfig in their config, and the idea here is to have a way to add the field for any policy -- e.g., if we wanted to add something to express the control flow of how the client picks which policy to use.

We could make this change later, but it would be a breaking change at that point. The idea here is to future-proof a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack, done.

message Policy {
option deprecated = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this referred anywhere? If no just delete?

Copy link
Contributor Author

@pianiststickman pianiststickman Aug 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was having trouble getting the presubmits to pass when I just deleted, but I didn't realize I needed to deprecate the unused v2 message as well. Done (for the parts of Policy that are removed).

option (udpa.annotations.versioning).previous_message_type =
"envoy.api.v2.LoadBalancingPolicy.Policy";

Expand All @@ -1112,7 +1113,7 @@ message LoadBalancingPolicy {
// Each client will iterate over the list in order and stop at the first policy that it
// supports. This provides a mechanism for starting to use new LB policies that are not yet
// supported by all clients.
repeated Policy policies = 1;
repeated core.v3.TypedExtensionConfig policies = 1;
pianiststickman marked this conversation as resolved.
Show resolved Hide resolved
}

// An extensible structure containing the address Envoy should bind to when
Expand Down
25 changes: 5 additions & 20 deletions api/envoy/config/cluster/v4alpha/cluster.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions envoy/upstream/load_balancer.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,26 @@ class ThreadAwareLoadBalancer {

using ThreadAwareLoadBalancerPtr = std::unique_ptr<ThreadAwareLoadBalancer>;

/**
* Factory for (thread-aware) load balancers. To support a load balancing policy of
* LOAD_BALANCING_POLICY_CONFIG, at least one load balancer factory corresponding to a policy in
* load_balancing_policy must be registered with Envoy. Envoy will use the first policy for which
* it has a registered factory.
*/
class TypedLoadBalancerFactory : public Config::UntypedFactory {
public:
~TypedLoadBalancerFactory() override = default;

/**
* @return ThreadAwareLoadBalancerPtr a new thread-aware load balancer.
*/
virtual ThreadAwareLoadBalancerPtr
create(const PrioritySet& priority_set, ClusterStats& stats, Stats::Scope& stats_scope,
Runtime::Loader& runtime, Random::RandomGenerator& random,
const ::envoy::config::core::v3::TypedExtensionConfig& lb_policy) PURE;

std::string category() const override { return "envoy.load_balancers"; }
};

} // namespace Upstream
} // namespace Envoy
3 changes: 2 additions & 1 deletion envoy/upstream/load_balancer_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ enum class LoadBalancerType {
RingHash,
OriginalDst,
Maglev,
ClusterProvided
ClusterProvided,
LoadBalancingPolicyConfig
};

/**
Expand Down
6 changes: 6 additions & 0 deletions envoy/upstream/upstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,12 @@ class ClusterInfo {
return std::dynamic_pointer_cast<const Derived>(extensionProtocolOptions(name));
}

/**
* @return const envoy::config::core::v3::TypedExtensionConfig& the load balancing policy to use
* for this cluster.
*/
virtual const envoy::config::core::v3::TypedExtensionConfig& loadBalancingPolicy() const PURE;

/**
* @return const envoy::config::cluster::v3::Cluster::CommonLbConfig& the common configuration for
* all load balancers for this cluster.
Expand Down
13 changes: 7 additions & 6 deletions generated_api_shadow/envoy/config/cluster/v3/cluster.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions generated_api_shadow/envoy/config/cluster/v4alpha/cluster.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions source/common/upstream/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,14 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "load_balancer_factory_base_lib",
hdrs = ["load_balancer_factory_base.h"],
deps = [
":load_balancer_lib",
],
)

envoy_cc_library(
name = "load_stats_reporter_lib",
srcs = ["load_stats_reporter.cc"],
Expand Down
10 changes: 10 additions & 0 deletions source/common/upstream/cluster_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,15 @@ ClusterManagerImpl::loadCluster(const envoy::config::cluster::v3::Cluster& clust
}
} else if (cluster_reference.info()->lbType() == LoadBalancerType::ClusterProvided) {
cluster_entry_it->second->thread_aware_lb_ = std::move(new_cluster_pair.second);
} else if (cluster_reference.info()->lbType() == LoadBalancerType::LoadBalancingPolicyConfig) {
const auto& policy = cluster_reference.info()->loadBalancingPolicy();
TypedLoadBalancerFactory* typed_lb_factory =
Registry::FactoryRegistry<TypedLoadBalancerFactory>::getFactory(policy.name());
RELEASE_ASSERT(typed_lb_factory != nullptr, "should have factory for selected LB policy");
pianiststickman marked this conversation as resolved.
Show resolved Hide resolved

cluster_entry_it->second->thread_aware_lb_ =
typed_lb_factory->create(cluster_reference.prioritySet(), cluster_reference.info()->stats(),
cluster_reference.info()->statsScope(), runtime_, random_, policy);
}

updateClusterCounts();
Expand Down Expand Up @@ -1377,6 +1386,7 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::ClusterEntry(
break;
}
case LoadBalancerType::ClusterProvided:
case LoadBalancerType::LoadBalancingPolicyConfig:
case LoadBalancerType::RingHash:
case LoadBalancerType::Maglev:
case LoadBalancerType::OriginalDst: {
Expand Down
29 changes: 29 additions & 0 deletions source/common/upstream/load_balancer_factory_base.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include "envoy/upstream/load_balancer.h"

namespace Envoy {
namespace Upstream {

/**
* Base class for cluster provided load balancers and load balancers specified by load balancing
* policy config. This class should be extended directly if the load balancing policy specifies a
* thread-aware load balancer.
*
* TODO: provide a ThreadLocalLoadBalancer construct to abstract away thread-awareness from load
* balancing extensions that don't require it.
*/
class TypedLoadBalancerFactoryBase : public TypedLoadBalancerFactory {
public:
// Upstream::TypedLoadBalancerFactory
std::string name() const override { return name_; }

protected:
TypedLoadBalancerFactoryBase(const std::string& name) : name_(name) {}

private:
const std::string name_;
};

} // namespace Upstream
} // namespace Envoy
4 changes: 2 additions & 2 deletions source/common/upstream/subset_lb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -781,8 +781,8 @@ SubsetLoadBalancer::PrioritySubsetImpl::PrioritySubsetImpl(const SubsetLoadBalan

case LoadBalancerType::OriginalDst:
case LoadBalancerType::ClusterProvided:
// LoadBalancerType::OriginalDst is blocked in the factory. LoadBalancerType::ClusterProvided
// is impossible because the subset LB returns a null load balancer from its factory.
case LoadBalancerType::LoadBalancingPolicyConfig:
// These load balancer types can only be created when there is no subset configuration.
NOT_REACHED_GCOVR_EXCL_LINE;
}

Expand Down
Loading