1
#pragma once
2

            
3
#include "source/extensions/load_balancing_policies/common/load_balancer_impl.h"
4

            
5
namespace Envoy {
6
namespace Upstream {
7

            
8
/**
9
 * Weighted Least Request load balancer.
10
 *
11
 * In a normal setup when all hosts have the same weight it randomly picks up N healthy hosts
12
 * (where N is specified in the LB configuration) and compares number of active requests. Technique
13
 * is based on http://www.eecs.harvard.edu/~michaelm/postscripts/mythesis.pdf and is known as P2C
14
 * (power of two choices).
15
 *
16
 * When hosts have different weights, an RR EDF schedule is used. Host weight is scaled
17
 * by the number of active requests at pick/insert time. Thus, hosts will never fully drain as
18
 * they would in normal P2C, though they will get picked less and less often. In the future, we
19
 * can consider two alternate algorithms:
20
 * 1) Expand out all hosts by weight (using more memory) and do standard P2C.
21
 * 2) Use a weighted Maglev table, and perform P2C on two random hosts selected from the table.
22
 *    The benefit of the Maglev table is at the expense of resolution, memory usage is capped.
23
 *    Additionally, the Maglev table can be shared amongst all threads.
24
 */
25
class LeastRequestLoadBalancer : public EdfLoadBalancerBase {
26
public:
27
  LeastRequestLoadBalancer(
28
      const PrioritySet& priority_set, const PrioritySet* local_priority_set, ClusterLbStats& stats,
29
      Runtime::Loader& runtime, Random::RandomGenerator& random, uint32_t healthy_panic_threshold,
30
      const envoy::extensions::load_balancing_policies::least_request::v3::LeastRequest&
31
          least_request_config,
32
      TimeSource& time_source)
33
74
      : EdfLoadBalancerBase(
34
74
            priority_set, local_priority_set, stats, runtime, random, healthy_panic_threshold,
35
74
            LoadBalancerConfigHelper::localityLbConfigFromProto(least_request_config),
36
74
            LoadBalancerConfigHelper::slowStartConfigFromProto(least_request_config), time_source),
37
74
        choice_count_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(least_request_config, choice_count, 2)),
38
        active_request_bias_runtime_(
39
74
            least_request_config.has_active_request_bias()
40
74
                ? absl::optional<Runtime::Double>(
41
8
                      {least_request_config.active_request_bias(), runtime})
42
74
                : absl::nullopt),
43
74
        selection_method_(least_request_config.selection_method()) {
44
74
    initialize();
45
74
  }
46

            
47
protected:
48
205
  void refresh(uint32_t priority) override {
49
205
    active_request_bias_ = active_request_bias_runtime_ != absl::nullopt
50
205
                               ? active_request_bias_runtime_.value().value()
51
205
                               : 1.0;
52

            
53
205
    if (active_request_bias_ < 0.0 || std::isnan(active_request_bias_)) {
54
2
      ENVOY_LOG_MISC(warn,
55
2
                     "upstream: invalid active request bias supplied (runtime key {}), using 1.0",
56
2
                     active_request_bias_runtime_->runtimeKey());
57
2
      active_request_bias_ = 1.0;
58
2
    }
59

            
60
205
    EdfLoadBalancerBase::refresh(priority);
61
205
  }
62

            
63
private:
64
652
  void refreshHostSource(const HostsSource&) override {}
65
  double hostWeight(const Host& host) const override;
66
  HostConstSharedPtr unweightedHostPeek(const HostVector& hosts_to_use,
67
                                        const HostsSource& source) override;
68
  HostConstSharedPtr unweightedHostPick(const HostVector& hosts_to_use,
69
                                        const HostsSource& source) override;
70
  HostSharedPtr unweightedHostPickFullScan(const HostVector& hosts_to_use);
71
  HostSharedPtr unweightedHostPickNChoices(const HostVector& hosts_to_use);
72

            
73
  const uint32_t choice_count_;
74

            
75
  // The exponent used to calculate host weights can be configured via runtime. We cache it for
76
  // performance reasons and refresh it in `LeastRequestLoadBalancer::refresh(uint32_t priority)`
77
  // whenever a `HostSet` is updated.
78
  double active_request_bias_{};
79

            
80
  const absl::optional<Runtime::Double> active_request_bias_runtime_;
81
  const envoy::extensions::load_balancing_policies::least_request::v3::LeastRequest::SelectionMethod
82
      selection_method_{};
83
};
84

            
85
} // namespace Upstream
86
} // namespace Envoy