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
using RoundRobinLbProto = envoy::extensions::load_balancing_policies::round_robin::v3::RoundRobin;
9
using CommonLbConfigProto = envoy::config::cluster::v3::Cluster::CommonLbConfig;
10
using LegacyRoundRobinLbProto = envoy::config::cluster::v3::Cluster::RoundRobinLbConfig;
11

            
12
/**
13
 * Load balancer config that used to wrap the proto config.
14
 */
15
class TypedRoundRobinLbConfig : public Upstream::LoadBalancerConfig {
16
public:
17
  TypedRoundRobinLbConfig(const RoundRobinLbProto& lb_config);
18
  TypedRoundRobinLbConfig(const CommonLbConfigProto& common_lb_config,
19
                          const LegacyRoundRobinLbProto& lb_config);
20

            
21
  RoundRobinLbProto lb_config_;
22
};
23

            
24
/**
25
 * A round robin load balancer. When in weighted mode, EDF scheduling is used. When in not
26
 * weighted mode, simple RR index selection is used.
27
 */
28
class RoundRobinLoadBalancer : public EdfLoadBalancerBase {
29
public:
30
  RoundRobinLoadBalancer(
31
      const PrioritySet& priority_set, const PrioritySet* local_priority_set, ClusterLbStats& stats,
32
      Runtime::Loader& runtime, Random::RandomGenerator& random, uint32_t healthy_panic_threshold,
33
      const envoy::extensions::load_balancing_policies::round_robin::v3::RoundRobin&
34
          round_robin_config,
35
      TimeSource& time_source)
36
33197
      : EdfLoadBalancerBase(
37
33197
            priority_set, local_priority_set, stats, runtime, random, healthy_panic_threshold,
38
33197
            LoadBalancerConfigHelper::localityLbConfigFromProto(round_robin_config),
39
33197
            LoadBalancerConfigHelper::slowStartConfigFromProto(round_robin_config), time_source) {
40
33197
    initialize();
41
33197
  }
42

            
43
private:
44
267282
  void refreshHostSource(const HostsSource& source) override {
45
    // insert() is used here on purpose so that we don't overwrite the index if the host source
46
    // already exists. Note that host sources will never be removed, but given how uncommon this
47
    // is it probably doesn't matter.
48
267282
    rr_indexes_.insert({source, seed_});
49
    // If the list of hosts changes, the order of picks change. Discard the
50
    // index.
51
267282
    peekahead_index_ = 0;
52
267282
  }
53
1410
  double hostWeight(const Host& host) const override {
54
1410
    if (!noHostsAreInSlowStart()) {
55
578
      return applySlowStartFactor(host.weight(), host);
56
578
    }
57
832
    return host.weight();
58
1410
  }
59

            
60
  HostConstSharedPtr unweightedHostPeek(const HostVector& hosts_to_use,
61
127
                                        const HostsSource& source) override {
62
127
    auto i = rr_indexes_.find(source);
63
127
    if (i == rr_indexes_.end()) {
64
      return nullptr;
65
    }
66
127
    return hosts_to_use[(i->second + (peekahead_index_)++) % hosts_to_use.size()];
67
127
  }
68

            
69
  HostConstSharedPtr unweightedHostPick(const HostVector& hosts_to_use,
70
49337
                                        const HostsSource& source) override {
71
49337
    if (peekahead_index_ > 0) {
72
88
      --peekahead_index_;
73
88
    }
74
    // To avoid storing the RR index in the base class, we end up using a second map here with
75
    // host source as the key. This means that each LB decision will require two map lookups in
76
    // the unweighted case. We might consider trying to optimize this in the future.
77
49337
    ASSERT(rr_indexes_.find(source) != rr_indexes_.end());
78
49337
    return hosts_to_use[rr_indexes_[source]++ % hosts_to_use.size()];
79
49337
  }
80

            
81
  uint64_t peekahead_index_{};
82
  absl::flat_hash_map<HostsSource, uint64_t, HostsSourceHash> rr_indexes_;
83
};
84

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