1
#pragma once
2

            
3
#include <bitset>
4
#include <cmath>
5
#include <cstdint>
6
#include <memory>
7
#include <queue>
8
#include <set>
9
#include <vector>
10

            
11
#include "envoy/common/callback.h"
12
#include "envoy/common/random_generator.h"
13
#include "envoy/config/cluster/v3/cluster.pb.h"
14
#include "envoy/extensions/load_balancing_policies/least_request/v3/least_request.pb.h"
15
#include "envoy/extensions/load_balancing_policies/random/v3/random.pb.h"
16
#include "envoy/extensions/load_balancing_policies/round_robin/v3/round_robin.pb.h"
17
#include "envoy/runtime/runtime.h"
18
#include "envoy/stream_info/stream_info.h"
19
#include "envoy/upstream/load_balancer.h"
20
#include "envoy/upstream/upstream.h"
21

            
22
#include "source/common/protobuf/utility.h"
23
#include "source/common/runtime/runtime_features.h"
24
#include "source/common/runtime/runtime_protos.h"
25
#include "source/common/upstream/edf_scheduler.h"
26
#include "source/common/upstream/load_balancer_context_base.h"
27
#include "source/extensions/load_balancing_policies/common/locality_wrr.h"
28

            
29
namespace Envoy {
30
namespace Upstream {
31

            
32
157
inline bool tooManyPreconnects(size_t num_preconnect_picks, uint32_t healthy_hosts) {
33
  // Currently we only allow the number of preconnected connections to equal the
34
  // number of healthy hosts.
35
157
  return num_preconnect_picks >= healthy_hosts;
36
157
}
37

            
38
// Distributes load between priorities based on the per priority availability and the normalized
39
// total availability. Load is assigned to each priority according to how available each priority is
40
// adjusted for the normalized total availability.
41
//
42
// @param per_priority_load vector of loads that should be populated.
43
// @param per_priority_availability the percentage availability of each priority, used to determine
44
// how much load each priority can handle.
45
// @param total_load the amount of load that may be distributed. Will be updated with the amount of
46
// load remaining after distribution.
47
// @param normalized_total_availability the total availability, up to a max of 100. Used to
48
// scale the load when the total availability is less than 100%.
49
// @return the first available priority and the remaining load
50
std::pair<int32_t, size_t> distributeLoad(PriorityLoad& per_priority_load,
51
                                          const PriorityAvailability& per_priority_availability,
52
                                          size_t total_load, size_t normalized_total_availability);
53

            
54
class LoadBalancerConfigHelper {
55
public:
56
  template <class Proto>
57
  static absl::optional<envoy::extensions::load_balancing_policies::common::v3::SlowStartConfig>
58
33271
  slowStartConfigFromProto(const Proto& proto_config) {
59
33271
    if (!proto_config.has_slow_start_config()) {
60
33252
      return {};
61
33252
    }
62
19
    return proto_config.slow_start_config();
63
33271
  }
64

            
65
  template <class Proto>
66
  static absl::optional<envoy::extensions::load_balancing_policies::common::v3::LocalityLbConfig>
67
33444
  localityLbConfigFromProto(const Proto& proto_config) {
68
33444
    if (!proto_config.has_locality_lb_config()) {
69
33408
      return {};
70
33408
    }
71
36
    return proto_config.locality_lb_config();
72
33444
  }
73

            
74
  template <class TargetProto>
75
  static void
76
  convertHashLbConfigTo(const envoy::config::cluster::v3::Cluster::CommonLbConfig& source,
77
501
                        TargetProto& target) {
78
501
    if (source.has_consistent_hashing_lb_config()) {
79
3
      target.mutable_consistent_hashing_lb_config()->set_use_hostname_for_hashing(
80
3
          source.consistent_hashing_lb_config().use_hostname_for_hashing());
81

            
82
3
      if (source.consistent_hashing_lb_config().has_hash_balance_factor()) {
83
3
        *target.mutable_consistent_hashing_lb_config()->mutable_hash_balance_factor() =
84
3
            source.consistent_hashing_lb_config().hash_balance_factor();
85
3
      }
86
3
    }
87
501
  }
88

            
89
  template <class TargetProto>
90
  static void
91
  convertLocalityLbConfigTo(const envoy::config::cluster::v3::Cluster::CommonLbConfig& source,
92
12338
                            TargetProto& target) {
93
12338
    if (source.has_locality_weighted_lb_config()) {
94
12
      target.mutable_locality_lb_config()->mutable_locality_weighted_lb_config();
95
12331
    } else if (source.has_zone_aware_lb_config()) {
96
3
      auto& zone_aware_lb_config =
97
3
          *target.mutable_locality_lb_config()->mutable_zone_aware_lb_config();
98
3
      const auto& legacy = source.zone_aware_lb_config();
99

            
100
3
      zone_aware_lb_config.set_fail_traffic_on_panic(legacy.fail_traffic_on_panic());
101

            
102
3
      if (legacy.has_routing_enabled()) {
103
3
        *zone_aware_lb_config.mutable_routing_enabled() = legacy.routing_enabled();
104
3
      }
105
3
      if (legacy.has_min_cluster_size()) {
106
3
        *zone_aware_lb_config.mutable_min_cluster_size() = legacy.min_cluster_size();
107
3
      }
108
3
    }
109
12338
  }
110

            
111
  template <class SourceProto, class TargetProto>
112
12260
  static void convertSlowStartConfigTo(const SourceProto& source, TargetProto& target) {
113
12260
    if (!source.has_slow_start_config()) {
114
12256
      return;
115
12256
    }
116

            
117
4
    auto& slow_start_config = *target.mutable_slow_start_config();
118
4
    const auto& legacy = source.slow_start_config();
119

            
120
4
    if (legacy.has_slow_start_window()) {
121
4
      *slow_start_config.mutable_slow_start_window() = legacy.slow_start_window();
122
4
    }
123
4
    if (legacy.has_aggression()) {
124
4
      *slow_start_config.mutable_aggression() = legacy.aggression();
125
4
    }
126
4
    if (legacy.has_min_weight_percent()) {
127
2
      *slow_start_config.mutable_min_weight_percent() = legacy.min_weight_percent();
128
2
    }
129
4
  }
130
};
131

            
132
/**
133
 * Base class for all LB implementations.
134
 */
135
class LoadBalancerBase : public LoadBalancer, protected Logger::Loggable<Logger::Id::upstream> {
136
public:
137
  enum class HostAvailability { Healthy, Degraded };
138

            
139
  // A utility function to chose a priority level based on a precomputed hash and
140
  // two PriorityLoad vectors, one for healthy load and one for degraded.
141
  //
142
  // Returns the priority, a number between 0 and per_priority_load.size()-1 as well as which host
143
  // availability level was chosen.
144
  static std::pair<uint32_t, HostAvailability>
145
  choosePriority(uint64_t hash, const HealthyLoad& healthy_per_priority_load,
146
                 const DegradedLoad& degraded_per_priority_load);
147

            
148
  // Pool selection not implemented.
149
  absl::optional<Upstream::SelectedPoolAndConnection>
150
  selectExistingConnection(Upstream::LoadBalancerContext* /*context*/,
151
                           const Upstream::Host& /*host*/,
152
1
                           std::vector<uint8_t>& /*hash_key*/) override {
153
1
    return absl::nullopt;
154
1
  }
155
  // Lifetime tracking not implemented.
156
2
  OptRef<Envoy::Http::ConnectionPool::ConnectionLifetimeCallbacks> lifetimeCallbacks() override {
157
2
    return {};
158
2
  }
159

            
160
protected:
161
  /**
162
   * For the given host_set @return if we should be in a panic mode or not. For example, if the
163
   * majority of hosts are unhealthy we'll be likely in a panic mode. In this case we'll route
164
   * requests to hosts regardless of whether they are healthy or not.
165
   */
166
  bool isHostSetInPanic(const HostSet& host_set) const;
167

            
168
  /**
169
   * Method is called when all host sets are in panic mode.
170
   * In such state the load is distributed based on the number of hosts
171
   * in given priority regardless of their health.
172
   */
173
  void recalculateLoadInTotalPanic();
174

            
175
  LoadBalancerBase(const PrioritySet& priority_set, ClusterLbStats& stats, Runtime::Loader& runtime,
176
                   Random::RandomGenerator& random, uint32_t healthy_panic_threshold);
177

            
178
  // Choose host set randomly, based on the healthy_per_priority_load_ and
179
  // degraded_per_priority_load_. per_priority_load_ is consulted first, spilling over to
180
  // degraded_per_priority_load_ if necessary. When a host set is selected based on
181
  // degraded_per_priority_load_, only degraded hosts should be selected from that host set.
182
  //
183
  // @return host set to use and which availability to target.
184
  std::pair<HostSet&, HostAvailability> chooseHostSet(LoadBalancerContext* context,
185
                                                      uint64_t hash) const;
186

            
187
179
  uint32_t percentageLoad(uint32_t priority) const {
188
179
    return per_priority_load_.healthy_priority_load_.get()[priority];
189
179
  }
190
88
  uint32_t percentageDegradedLoad(uint32_t priority) const {
191
88
    return per_priority_load_.degraded_priority_load_.get()[priority];
192
88
  }
193
94
  bool isInPanic(uint32_t priority) const { return per_priority_panic_[priority]; }
194
  uint64_t random(bool peeking);
195

            
196
  ClusterLbStats& stats_;
197
  Runtime::Loader& runtime_;
198
  std::deque<uint64_t> stashed_random_;
199
  Random::RandomGenerator& random_;
200
  const uint32_t default_healthy_panic_percent_;
201
  // The priority-ordered set of hosts to use for load balancing.
202
  const PrioritySet& priority_set_;
203

            
204
public:
205
  // Called when a host set at the given priority level is updated. This updates
206
  // per_priority_health for that priority level, and may update per_priority_load for all
207
  // priority levels.
208
  void static recalculatePerPriorityState(uint32_t priority, const PrioritySet& priority_set,
209
                                          HealthyAndDegradedLoad& priority_load,
210
                                          HealthyAvailability& per_priority_health,
211
                                          DegradedAvailability& per_priority_degraded,
212
                                          uint32_t& total_healthy_hosts);
213
  void recalculatePerPriorityPanic();
214

            
215
protected:
216
  // Method calculates normalized total availability.
217
  //
218
  // The availability of a priority is ratio of available (healthy/degraded) hosts over the total
219
  // number of hosts multiplied by 100 and the overprovisioning factor. The total availability is
220
  // the sum of the availability of each priority, up to a maximum of 100.
221
  //
222
  // For example, using the default overprovisioning factor of 1.4, a if priority A has 4 hosts,
223
  // of which 1 is degraded and 1 is healthy, it will have availability of 2/4 * 100 * 1.4 = 70.
224
  //
225
  // Assuming two priorities with availability 60 and 70, the total availability would be 100.
226
  static uint32_t
227
  calculateNormalizedTotalAvailability(HealthyAvailability& per_priority_health,
228
137649
                                       DegradedAvailability& per_priority_degraded) {
229
137649
    const auto health =
230
137649
        std::accumulate(per_priority_health.get().begin(), per_priority_health.get().end(), 0);
231
137649
    const auto degraded =
232
137649
        std::accumulate(per_priority_degraded.get().begin(), per_priority_degraded.get().end(), 0);
233

            
234
137649
    return std::min<uint32_t>(health + degraded, 100);
235
137649
  }
236

            
237
  // The percentage load (0-100) for each priority level when targeting healthy hosts and
238
  // the percentage load (0-100) for each priority level when targeting degraded hosts.
239
  HealthyAndDegradedLoad per_priority_load_;
240
  // The health percentage (0-100) for each priority level.
241
  HealthyAvailability per_priority_health_;
242
  // The degraded percentage (0-100) for each priority level.
243
  DegradedAvailability per_priority_degraded_;
244
  // Levels which are in panic
245
  std::vector<bool> per_priority_panic_;
246
  // The total count of healthy hosts across all priority levels.
247
  uint32_t total_healthy_hosts_;
248

            
249
  // Processes any dirty priorities accumulated by PriorityUpdateCb.
250
  // Idempotent: safe to call multiple times (no-op if dirty set is empty).
251
  void processDirtyPriorities();
252

            
253
private:
254
  Common::CallbackHandlePtr priority_update_cb_;
255
  Common::CallbackHandlePtr member_update_cb_;
256
  absl::flat_hash_set<uint32_t> dirty_priorities_;
257
};
258

            
259
/**
260
 * Base class for zone aware load balancers
261
 */
262
class ZoneAwareLoadBalancerBase : public LoadBalancerBase {
263
public:
264
  using LocalityLbConfig = envoy::extensions::load_balancing_policies::common::v3::LocalityLbConfig;
265

            
266
  HostSelectionResponse chooseHost(LoadBalancerContext* context) override;
267

            
268
protected:
269
  // Both priority_set and local_priority_set if non-null must have at least one host set.
270
  ZoneAwareLoadBalancerBase(const PrioritySet& priority_set, const PrioritySet* local_priority_set,
271
                            ClusterLbStats& stats, Runtime::Loader& runtime,
272
                            Random::RandomGenerator& random, uint32_t healthy_panic_threshold,
273
                            const absl::optional<LocalityLbConfig> locality_config);
274

            
275
  // When deciding which hosts to use on an LB decision, we need to know how to index into the
276
  // priority_set. This priority_set cursor is used by ZoneAwareLoadBalancerBase subclasses, e.g.
277
  // RoundRobinLoadBalancer, to index into auxiliary data structures specific to the LB for
278
  // a given host set selection.
279
  struct HostsSource {
280
    enum class SourceType : uint8_t {
281
      // All hosts in the host set.
282
      AllHosts,
283
      // All healthy hosts in the host set.
284
      HealthyHosts,
285
      // All degraded hosts in the host set.
286
      DegradedHosts,
287
      // Healthy hosts for locality @ locality_index.
288
      LocalityHealthyHosts,
289
      // Degraded hosts for locality @ locality_index.
290
      LocalityDegradedHosts,
291
    };
292

            
293
3267448
    HostsSource() = default;
294

            
295
    // TODO(kbaichoo): plumb the priority parameter as uint8_t all the way from
296
    // the config.
297
    HostsSource(uint32_t priority, SourceType source_type)
298
201430
        : priority_(static_cast<uint8_t>(priority)), source_type_(source_type) {
299
201430
      ASSERT(priority <= 128, "Priority out of bounds.");
300
201430
      ASSERT(source_type == SourceType::AllHosts || source_type == SourceType::HealthyHosts ||
301
201430
             source_type == SourceType::DegradedHosts);
302
201430
    }
303

            
304
    // TODO(kbaichoo): plumb the priority parameter as uint8_t all the way from
305
    // the config.
306
    HostsSource(uint32_t priority, SourceType source_type, uint32_t locality_index)
307
66502
        : priority_(static_cast<uint8_t>(priority)), source_type_(source_type),
308
66502
          locality_index_(locality_index) {
309
66502
      ASSERT(priority <= 128, "Priority out of bounds.");
310
66502
      ASSERT(source_type == SourceType::LocalityHealthyHosts ||
311
66502
             source_type == SourceType::LocalityDegradedHosts);
312
66502
    }
313

            
314
    // Priority in PrioritySet.
315
    uint8_t priority_{};
316

            
317
    // How to index into HostSet for a given priority.
318
    SourceType source_type_{};
319

            
320
    // Locality index into HostsPerLocality for SourceType::LocalityHealthyHosts.
321
    uint32_t locality_index_{};
322

            
323
10265190
    bool operator==(const HostsSource& other) const {
324
10265193
      return priority_ == other.priority_ && source_type_ == other.source_type_ &&
325
10265190
             locality_index_ == other.locality_index_;
326
10265190
    }
327
  };
328

            
329
  struct HostsSourceHash {
330
3835414
    size_t operator()(const HostsSource& hs) const {
331
      // This is only used for absl::flat_hash_map keys, so we don't need a deterministic hash.
332
3835414
      size_t hash = std::hash<uint32_t>()(hs.priority_);
333
3835414
      hash = 37 * hash + std::hash<size_t>()(static_cast<std::size_t>(hs.source_type_));
334
3835414
      hash = 37 * hash + std::hash<uint32_t>()(hs.locality_index_);
335
3835414
      return hash;
336
3835414
    }
337
  };
338

            
339
  /**
340
   * By implementing this method instead of chooseHost, host selection will
341
   * be subject to host filters specified by LoadBalancerContext.
342
   *
343
   * Host selection will be retried up to the number specified by
344
   * hostSelectionRetryCount on LoadBalancerContext, and if no hosts are found
345
   * within the allowed attempts, the host that was selected during the last
346
   * attempt will be returned.
347
   *
348
   * If host selection is idempotent (i.e. retrying will not change the outcome),
349
   * sub classes should override chooseHost to avoid the unnecessary overhead of
350
   * retrying host selection.
351
   */
352
  virtual HostConstSharedPtr chooseHostOnce(LoadBalancerContext* context) PURE;
353

            
354
  /**
355
   * Pick the host source to use, doing zone aware routing when the hosts are sufficiently healthy.
356
   * If no host is chosen (due to fail_traffic_on_panic being set), return absl::nullopt.
357
   */
358
  absl::optional<HostsSource> hostSourceToUse(LoadBalancerContext* context, uint64_t hash) const;
359

            
360
  /**
361
   * Index into priority_set via hosts source descriptor.
362
   */
363
  const HostVector& hostSourceToHosts(HostsSource hosts_source) const;
364

            
365
private:
366
  enum class LocalityRoutingState {
367
    // Locality based routing is off.
368
    NoLocalityRouting,
369
    // All queries can be routed to the local locality.
370
    LocalityDirect,
371
    // The local locality can not handle the anticipated load. Residual load will be spread across
372
    // various other localities.
373
    LocalityResidual
374
  };
375

            
376
  struct LocalityPercentages {
377
    // The percentage of local hosts in a specific locality.
378
    // Percentage is stored as integer number and scaled by 10000 multiplier for better precision.
379
    // If upstream_percentage is 0, local_percentage may not be representative
380
    // of the actual percentage and will be set to 0.
381
    uint64_t local_percentage;
382
    // The percentage of upstream hosts in a specific locality.
383
    // Percentage is stored as integer number and scaled by 10000 multiplier for better precision.
384
    uint64_t upstream_percentage;
385
  };
386

            
387
  /**
388
   * Increase per_priority_state_ to at least priority_set.hostSetsPerPriority().size()
389
   */
390
  void resizePerPriorityState();
391

            
392
  /**
393
   * @return decision on quick exit from locality aware routing based on cluster configuration.
394
   * This gets recalculated on update callback.
395
   */
396
  bool earlyExitNonLocalityRouting();
397

            
398
  /**
399
   * Try to select upstream hosts from the same locality.
400
   * @param host_set the last host set returned by chooseHostSet()
401
   */
402
  uint32_t tryChooseLocalLocalityHosts(const HostSet& host_set) const;
403

            
404
  /**
405
   * @return combined per-locality information about percentages of local/upstream hosts in each
406
   * upstream locality. See LocalityPercentages for more details. The ordering of localities
407
   * matches the ordering of upstream localities in the input upstream_hosts_per_locality.
408
   */
409
  absl::FixedArray<LocalityPercentages>
410
  calculateLocalityPercentages(const HostsPerLocality& local_hosts_per_locality,
411
                               const HostsPerLocality& upstream_hosts_per_locality);
412

            
413
  /**
414
   * Regenerate locality aware routing structures for fast decisions on upstream locality selection.
415
   */
416
  void regenerateLocalityRoutingStructures();
417

            
418
438
  HostSet& localHostSet() const { return *local_priority_set_->hostSetsPerPriority()[0]; }
419

            
420
  static absl::optional<HostsSource::SourceType>
421
1227
  localitySourceType(HostAvailability host_availability) {
422
1227
    switch (host_availability) {
423
1226
    case HostAvailability::Healthy:
424
1226
      return absl::make_optional<HostsSource::SourceType>(
425
1226
          HostsSource::SourceType::LocalityHealthyHosts);
426
    case HostAvailability::Degraded:
427
      return absl::make_optional<HostsSource::SourceType>(
428
          HostsSource::SourceType::LocalityDegradedHosts);
429
1227
    }
430
1
    IS_ENVOY_BUG("unexpected locality source type enum");
431
1
    return absl::nullopt;
432
1227
  }
433

            
434
3266156
  static absl::optional<HostsSource::SourceType> sourceType(HostAvailability host_availability) {
435
3266156
    switch (host_availability) {
436
3265913
    case HostAvailability::Healthy:
437
3265913
      return absl::make_optional<HostsSource::SourceType>(HostsSource::SourceType::HealthyHosts);
438
242
    case HostAvailability::Degraded:
439
242
      return absl::make_optional<HostsSource::SourceType>(HostsSource::SourceType::DegradedHosts);
440
3266156
    }
441

            
442
1
    IS_ENVOY_BUG("unexpected source type enum");
443
1
    return absl::nullopt;
444
3266156
  }
445

            
446
1143
  absl::optional<uint32_t> chooseHealthyLocality(HostSet& host_set) const {
447
1143
    ASSERT(per_priority_state_[host_set.priority()]->locality_wrr_);
448
1143
    return per_priority_state_[host_set.priority()]->locality_wrr_->chooseHealthyLocality();
449
1143
  };
450

            
451
4
  absl::optional<uint32_t> chooseDegradedLocality(HostSet& host_set) const {
452
4
    ASSERT(per_priority_state_[host_set.priority()]->locality_wrr_);
453
4
    return per_priority_state_[host_set.priority()]->locality_wrr_->chooseDegradedLocality();
454
4
  };
455

            
456
  // The set of local Envoy instances which are load balancing across priority_set_.
457
  const PrioritySet* local_priority_set_;
458

            
459
  struct PerPriorityState {
460
    // The percent of requests which can be routed to the local locality.
461
    uint64_t local_percent_to_route_{};
462
    // Tracks the current state of locality based routing.
463
    LocalityRoutingState locality_routing_state_{LocalityRoutingState::NoLocalityRouting};
464
    // When locality_routing_state_ == LocalityResidual this tracks the capacity
465
    // for each of the non-local localities to determine what traffic should be
466
    // routed where.
467
    std::vector<uint64_t> residual_capacity_;
468

            
469
    // Locality Weighted Round Robin config.
470
    std::unique_ptr<LocalityWrr> locality_wrr_;
471
  };
472
  using PerPriorityStatePtr = std::unique_ptr<PerPriorityState>;
473

            
474
  void rebuildLocalityWrrForPriority(uint32_t priority);
475

            
476
  // Routing state broken out for each priority level in priority_set_.
477
  std::vector<PerPriorityStatePtr> per_priority_state_;
478
  Common::CallbackHandlePtr priority_update_cb_;
479
  Common::CallbackHandlePtr member_update_cb_;
480
  Common::CallbackHandlePtr local_priority_set_member_update_cb_handle_;
481
  absl::flat_hash_set<uint32_t> dirty_priorities_;
482

            
483
  // Config for zone aware routing.
484
  const uint64_t min_cluster_size_;
485
  const absl::optional<uint32_t> force_local_zone_min_size_;
486
  // Keep small members (bools and enums) at the end of class, to reduce alignment overhead.
487
  const uint32_t routing_enabled_;
488
  const LocalityLbConfig::ZoneAwareLbConfig::LocalityBasis locality_basis_;
489
  const bool fail_traffic_on_panic_ : 1;
490

            
491
  // If locality weight aware routing is enabled.
492
  const bool locality_weighted_balancing_ : 1;
493

            
494
  friend class TestZoneAwareLoadBalancer;
495
};
496

            
497
/**
498
 * Base implementation of LoadBalancer that performs weighted RR selection across the hosts in the
499
 * cluster. This scheduler respects host weighting and utilizes an EdfScheduler to achieve O(log
500
 * n) pick and insertion time complexity, O(n) memory use. The key insight is that if we schedule
501
 * with 1 / weight deadline, we will achieve the desired pick frequency for weighted RR in a given
502
 * interval. Naive implementations of weighted RR are either O(n) pick time or O(m * n) memory use,
503
 * where m is the weight range. We also explicitly check for the unweighted special case and use a
504
 * simple index to achieve O(1) scheduling in that case.
505
 * TODO(htuch): We use EDF at Google, but the EDF scheduler may be overkill if we don't want to
506
 * support large ranges of weights or arbitrary precision floating weights, we could construct an
507
 * explicit schedule, since m will be a small constant factor in O(m * n). This
508
 * could also be done on a thread aware LB, avoiding creating multiple EDF
509
 * instances.
510
 *
511
 * This base class also supports unweighted selection which derived classes can use to customize
512
 * behavior. Derived classes can also override how host weight is determined when in weighted mode.
513
 */
514
class EdfLoadBalancerBase : public ZoneAwareLoadBalancerBase {
515
public:
516
  using SlowStartConfig = envoy::extensions::load_balancing_policies::common::v3::SlowStartConfig;
517

            
518
  EdfLoadBalancerBase(const PrioritySet& priority_set, const PrioritySet* local_priority_set,
519
                      ClusterLbStats& stats, Runtime::Loader& runtime,
520
                      Random::RandomGenerator& random, uint32_t healthy_panic_threshold,
521
                      const absl::optional<LocalityLbConfig> locality_config,
522
                      const absl::optional<SlowStartConfig> slow_start_config,
523
                      TimeSource& time_source);
524

            
525
  // Upstream::ZoneAwareLoadBalancerBase
526
  HostConstSharedPtr peekAnotherHost(LoadBalancerContext* context) override;
527
  HostConstSharedPtr chooseHostOnce(LoadBalancerContext* context) override;
528

            
529
protected:
530
  struct Scheduler {
531
    // EdfScheduler for weighted LB. The edf_ is only created when the original
532
    // host weights of 2 or more hosts differ. When not present, the
533
    // implementation of chooseHostOnce falls back to unweightedHostPick.
534
    std::unique_ptr<EdfScheduler<Host>> edf_;
535
  };
536

            
537
  void initialize();
538

            
539
  virtual void refresh(uint32_t priority);
540

            
541
  bool isSlowStartEnabled() const;
542
  bool noHostsAreInSlowStart() const;
543

            
544
  virtual void recalculateHostsInSlowStart(const HostVector& hosts_added);
545

            
546
  // Seed to allow us to desynchronize load balancers across a fleet. If we don't
547
  // do this, multiple Envoys that receive an update at the same time (or even
548
  // multiple load balancers on the same host) will send requests to
549
  // backends in roughly lock step, causing significant imbalance and potential
550
  // overload.
551
  const uint64_t seed_;
552

            
553
  double applySlowStartFactor(double host_weight, const Host& host) const;
554

            
555
private:
556
  friend class EdfLoadBalancerBasePeer;
557
  virtual void refreshHostSource(const HostsSource& source) PURE;
558
  virtual double hostWeight(const Host& host) const PURE;
559
  virtual HostConstSharedPtr unweightedHostPeek(const HostVector& hosts_to_use,
560
                                                const HostsSource& source) PURE;
561
  virtual HostConstSharedPtr unweightedHostPick(const HostVector& hosts_to_use,
562
                                                const HostsSource& source) PURE;
563

            
564
  // Scheduler for each valid HostsSource.
565
  absl::flat_hash_map<HostsSource, Scheduler, HostsSourceHash> scheduler_;
566
  Common::CallbackHandlePtr priority_update_cb_;
567
  Common::CallbackHandlePtr member_update_cb_;
568
  absl::flat_hash_set<uint32_t> dirty_priorities_;
569

            
570
protected:
571
  // Slow start related config
572
  const std::chrono::milliseconds slow_start_window_;
573
  const absl::optional<Runtime::Double> aggression_runtime_;
574
  TimeSource& time_source_;
575
  MonotonicTime latest_host_added_time_;
576
  const double slow_start_min_weight_percent_;
577
};
578

            
579
} // namespace Upstream
580
} // namespace Envoy