Skip to content

Commit e31bc1e

Browse files
Enable load balancing policy extensions (#17400)
Enables `LOAD_BALANCING_POLICY_CONFIG` enum value in `LbPolicy` and supports typed load balancers specified in `load_balancing_policy`. Continues work done by Charlie Getzen <charliegetzenlc@gmail.com> in #15827. Custom load balancers specified by `load_balancing_policy` are created as implementations of `ThreadAwareLoadBalancer`. Thread-local load balancers can be implemented as thread-aware load balancers that contain no logic at the thread-aware level, i.e. the purpose of the thread-aware LB is solely to contain the factory used to instantiate the thread-local LBs. (In the future it might be appropriate to provide a construct that abstracts away thread-aware aspects of `ThreadAwareLoadBalancer` for LBs that don't need to be thread-aware.) A cluster that uses `LOAD_BALANCING_POLICY_CONFIG` may not also set a subset LB configuration. If the load balancer type makes use of subsetting, it should include a subset configuration in its own configuration message. Future work on load balancing extensions should include moving the subset LB to use load balancing extensions. Similarly, a cluster that uses `LOAD_BALANCING_POLICY_CONFIG` may not set the `CommonLbConfig`, and it is not passed into load balancer creation (mostly owing to its dubious applicability as a top level configuration message to hierarchical load balancing policy). If the load balancer type makes use of the `CommonLbConfig`, it should include a `CommonLbConfig` in the configuration message for the load balancing policy. Considerations for migration of existing load balancers: - pieces of the `ThreadAwareLoadBalancerBase` implementation are specific to the built-in hashing load balancers and should be moved into a base class specifically for hashing load balancers. As it stands, custom load balancing policies are required to implement a `createLoadBalancer()` method even if the architecture of the LB policy does not require a hashing load balancer. I think we would also benefit from disentangling `ThreadAwareLoadBalancerBase` from `LoadBalancerBase`, as the former never actually does any host picking. - as we convert existing thread-local load balancers to thread-aware load balancers, new local LBs will be re-created upon membership changes. We should provide a mechanism allowing load balancers to control whether this rebuild should occur, e.g. a callback that calls `create()` for thread-aware LBs by default, which can be overridden to do nothing for thread-local LBs. Risk Level: low Testing: brought up a cluster with a custom load balancer specified by `load_balancing_policy`; new unit tests included Docs Changes: n/a Release Notes: Enable load balancing policy extensions Platform Specific Features: n/a Fixes #5598 Signed-off-by: Eugene Chan <eugenechan@google.com>
1 parent 5acb1d0 commit e31bc1e

File tree

17 files changed

+360
-48
lines changed

17 files changed

+360
-48
lines changed

api/envoy/config/cluster/v3/cluster.proto

+7-11
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ message Cluster {
110110
// this option or not.
111111
CLUSTER_PROVIDED = 6;
112112

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

721721
// The :ref:`load balancer type <arch_overview_load_balancing_types>` to use
722722
// when picking a host in the cluster.
723-
// [#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.]
724-
LbPolicy lb_policy = 6 [(validate.rules).enum = {defined_only: true not_in: 7}];
723+
LbPolicy lb_policy = 6 [(validate.rules).enum = {defined_only: true}];
725724

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

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

1076-
// [#not-implemented-hide:] Extensible load balancing policy configuration.
1075+
// Extensible load balancing policy configuration.
10771076
//
10781077
// Every LB policy defined via this mechanism will be identified via a unique name using reverse
10791078
// DNS notation. If the policy needs configuration parameters, it must define a message for its
@@ -1099,14 +1098,11 @@ message LoadBalancingPolicy {
10991098
option (udpa.annotations.versioning).previous_message_type =
11001099
"envoy.api.v2.LoadBalancingPolicy.Policy";
11011100

1102-
reserved 2;
1103-
1104-
reserved "config";
1101+
reserved 2, 1, 3;
11051102

1106-
// Required. The name of the LB policy.
1107-
string name = 1;
1103+
reserved "config", "name", "typed_config";
11081104

1109-
google.protobuf.Any typed_config = 3;
1105+
core.v3.TypedExtensionConfig typed_extension_config = 4;
11101106
}
11111107

11121108
// Each client will iterate over the list in order and stop at the first policy that it

api/envoy/config/cluster/v4alpha/cluster.proto

+7-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

envoy/upstream/load_balancer.h

+21
Original file line numberDiff line numberDiff line change
@@ -173,5 +173,26 @@ class ThreadAwareLoadBalancer {
173173

174174
using ThreadAwareLoadBalancerPtr = std::unique_ptr<ThreadAwareLoadBalancer>;
175175

176+
/**
177+
* Factory for (thread-aware) load balancers. To support a load balancing policy of
178+
* LOAD_BALANCING_POLICY_CONFIG, at least one load balancer factory corresponding to a policy in
179+
* load_balancing_policy must be registered with Envoy. Envoy will use the first policy for which
180+
* it has a registered factory.
181+
*/
182+
class TypedLoadBalancerFactory : public Config::UntypedFactory {
183+
public:
184+
~TypedLoadBalancerFactory() override = default;
185+
186+
/**
187+
* @return ThreadAwareLoadBalancerPtr a new thread-aware load balancer.
188+
*/
189+
virtual ThreadAwareLoadBalancerPtr
190+
create(const PrioritySet& priority_set, ClusterStats& stats, Stats::Scope& stats_scope,
191+
Runtime::Loader& runtime, Random::RandomGenerator& random,
192+
const ::envoy::config::cluster::v3::LoadBalancingPolicy_Policy& lb_policy) PURE;
193+
194+
std::string category() const override { return "envoy.load_balancers"; }
195+
};
196+
176197
} // namespace Upstream
177198
} // namespace Envoy

envoy/upstream/load_balancer_type.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ enum class LoadBalancerType {
2222
RingHash,
2323
OriginalDst,
2424
Maglev,
25-
ClusterProvided
25+
ClusterProvided,
26+
LoadBalancingPolicyConfig
2627
};
2728

2829
/**

envoy/upstream/upstream.h

+15
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,8 @@ using ProtocolOptionsConfigConstSharedPtr = std::shared_ptr<const ProtocolOption
692692
*/
693693
class ClusterTypedMetadataFactory : public Envoy::Config::TypedMetadataFactory {};
694694

695+
class TypedLoadBalancerFactory;
696+
695697
/**
696698
* Information about a given upstream cluster.
697699
*/
@@ -786,6 +788,19 @@ class ClusterInfo {
786788
return std::dynamic_pointer_cast<const Derived>(extensionProtocolOptions(name));
787789
}
788790

791+
/**
792+
* @return const envoy::config::cluster::v3::LoadBalancingPolicy_Policy& the load balancing policy
793+
* to use for this cluster.
794+
*/
795+
virtual const envoy::config::cluster::v3::LoadBalancingPolicy_Policy&
796+
loadBalancingPolicy() const PURE;
797+
798+
/**
799+
* @return the load balancer factory for this cluster if the load balancing type is
800+
* LOAD_BALANCING_POLICY_CONFIG.
801+
*/
802+
virtual TypedLoadBalancerFactory* loadBalancerFactory() const PURE;
803+
789804
/**
790805
* @return const envoy::config::cluster::v3::Cluster::CommonLbConfig& the common configuration for
791806
* all load balancers for this cluster.

generated_api_shadow/envoy/config/cluster/v3/cluster.proto

+8-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

generated_api_shadow/envoy/config/cluster/v4alpha/cluster.proto

+7-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source/common/upstream/BUILD

+8
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,14 @@ envoy_cc_library(
219219
],
220220
)
221221

222+
envoy_cc_library(
223+
name = "load_balancer_factory_base_lib",
224+
hdrs = ["load_balancer_factory_base.h"],
225+
deps = [
226+
":load_balancer_lib",
227+
],
228+
)
229+
222230
envoy_cc_library(
223231
name = "load_stats_reporter_lib",
224232
srcs = ["load_stats_reporter.cc"],

source/common/upstream/cluster_manager_impl.cc

+8
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,13 @@ ClusterManagerImpl::loadCluster(const envoy::config::cluster::v3::Cluster& clust
831831
}
832832
} else if (cluster_reference.info()->lbType() == LoadBalancerType::ClusterProvided) {
833833
cluster_entry_it->second->thread_aware_lb_ = std::move(new_cluster_pair.second);
834+
} else if (cluster_reference.info()->lbType() == LoadBalancerType::LoadBalancingPolicyConfig) {
835+
const auto& policy = cluster_reference.info()->loadBalancingPolicy();
836+
TypedLoadBalancerFactory* typed_lb_factory = cluster_reference.info()->loadBalancerFactory();
837+
RELEASE_ASSERT(typed_lb_factory != nullptr, "ClusterInfo should contain a valid factory");
838+
cluster_entry_it->second->thread_aware_lb_ =
839+
typed_lb_factory->create(cluster_reference.prioritySet(), cluster_reference.info()->stats(),
840+
cluster_reference.info()->statsScope(), runtime_, random_, policy);
834841
}
835842

836843
updateClusterCounts();
@@ -1363,6 +1370,7 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::ClusterEntry(
13631370
break;
13641371
}
13651372
case LoadBalancerType::ClusterProvided:
1373+
case LoadBalancerType::LoadBalancingPolicyConfig:
13661374
case LoadBalancerType::RingHash:
13671375
case LoadBalancerType::Maglev:
13681376
case LoadBalancerType::OriginalDst: {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
#include "envoy/upstream/load_balancer.h"
4+
5+
namespace Envoy {
6+
namespace Upstream {
7+
8+
/**
9+
* Base class for cluster provided load balancers and load balancers specified by load balancing
10+
* policy config. This class should be extended directly if the load balancing policy specifies a
11+
* thread-aware load balancer.
12+
*
13+
* TODO: provide a ThreadLocalLoadBalancer construct to abstract away thread-awareness from load
14+
* balancing extensions that don't require it.
15+
*/
16+
class TypedLoadBalancerFactoryBase : public TypedLoadBalancerFactory {
17+
public:
18+
// Upstream::TypedLoadBalancerFactory
19+
std::string name() const override { return name_; }
20+
21+
protected:
22+
TypedLoadBalancerFactoryBase(const std::string& name) : name_(name) {}
23+
24+
private:
25+
const std::string name_;
26+
};
27+
28+
} // namespace Upstream
29+
} // namespace Envoy

source/common/upstream/subset_lb.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -781,8 +781,8 @@ SubsetLoadBalancer::PrioritySubsetImpl::PrioritySubsetImpl(const SubsetLoadBalan
781781

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

0 commit comments

Comments
 (0)