1
#pragma once
2

            
3
#include "envoy/common/pure.h"
4
#include "envoy/common/random_generator.h"
5
#include "envoy/config/cluster/v3/cluster.pb.h"
6
#include "envoy/extensions/load_balancing_policies/maglev/v3/maglev.pb.h"
7
#include "envoy/extensions/load_balancing_policies/maglev/v3/maglev.pb.validate.h"
8
#include "envoy/stats/scope.h"
9
#include "envoy/stats/stats_macros.h"
10
#include "envoy/upstream/load_balancer.h"
11

            
12
#include "source/common/common/bit_array.h"
13
#include "source/extensions/load_balancing_policies/common/thread_aware_lb_impl.h"
14

            
15
namespace Envoy {
16
namespace Upstream {
17

            
18
using MaglevLbProto = envoy::extensions::load_balancing_policies::maglev::v3::Maglev;
19
using ClusterProto = envoy::config::cluster::v3::Cluster;
20

            
21
using CommonLbConfigProto = envoy::config::cluster::v3::Cluster::CommonLbConfig;
22
using LegacyMaglevLbProto = envoy::config::cluster::v3::Cluster::MaglevLbConfig;
23

            
24
/**
25
 * Load balancer config that used to wrap typed maglev config.
26
 */
27
class TypedMaglevLbConfig : public Upstream::TypedHashLbConfigBase {
28
public:
29
  TypedMaglevLbConfig(const CommonLbConfigProto& common_lb_config,
30
                      const LegacyMaglevLbProto& lb_config);
31
  TypedMaglevLbConfig(const MaglevLbProto& config, Regex::Engine& regex_engine,
32
                      absl::Status& creation_status);
33

            
34
  MaglevLbProto lb_config_;
35
};
36

            
37
/**
38
 * All Maglev load balancer stats. @see stats_macros.h
39
 */
40
#define ALL_MAGLEV_LOAD_BALANCER_STATS(GAUGE)                                                      \
41
468
  GAUGE(max_entries_per_host, Accumulate)                                                          \
42
468
  GAUGE(min_entries_per_host, Accumulate)
43

            
44
/**
45
 * Struct definition for all Maglev load balancer stats. @see stats_macros.h
46
 */
47
struct MaglevLoadBalancerStats {
48
  ALL_MAGLEV_LOAD_BALANCER_STATS(GENERATE_GAUGE_STRUCT)
49
};
50

            
51
class MaglevTable;
52
using MaglevTableSharedPtr = std::shared_ptr<MaglevTable>;
53

            
54
/**
55
 * This is an implementation of Maglev consistent hashing as described in:
56
 * https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/44824.pdf
57
 * section 3.4. Specifically, the algorithm shown in pseudocode listing 1 is implemented with a
58
 * fixed table size of 65537. This is the recommended table size in section 5.3.
59
 */
60
class MaglevTable : public ThreadAwareLoadBalancerBase::HashingLoadBalancer,
61
                    protected Logger::Loggable<Logger::Id::upstream> {
62
public:
63
  MaglevTable(uint64_t table_size, MaglevLoadBalancerStats& stats);
64
495
  ~MaglevTable() override = default;
65

            
66
  // Recommended table size in section 5.3 of the paper.
67
  static constexpr uint64_t DefaultTableSize = 65537;
68
  static constexpr uint64_t MaxNumberOfHostsForCompactMaglev = (static_cast<uint64_t>(1) << 32) - 1;
69

            
70
  /**
71
   * Log each entry of the maglev table (useful for debugging).
72
   */
73
  virtual void logMaglevTable(bool use_hostname_for_hashing) const PURE;
74

            
75
protected:
76
  struct TableBuildEntry {
77
    TableBuildEntry(const HostConstSharedPtr& host, uint64_t offset, uint64_t skip, double weight)
78
2614
        : host_(host), offset_(offset), skip_(skip), weight_(weight) {}
79

            
80
    HostConstSharedPtr host_;
81
    const uint64_t offset_;
82
    const uint64_t skip_;
83
    const double weight_;
84
    double target_weight_{};
85
    uint64_t next_{};
86
    uint64_t count_{};
87
  };
88

            
89
  uint64_t permutation(const TableBuildEntry& entry);
90

            
91
  /**
92
   * Template method for constructing the Maglev table.
93
   */
94
  void constructMaglevTableInternal(const NormalizedHostWeightVector& normalized_host_weights,
95
                                    double max_normalized_weight, bool use_hostname_for_hashing);
96

            
97
  const uint64_t table_size_;
98
  MaglevLoadBalancerStats& stats_;
99

            
100
private:
101
  /**
102
   * Implementation specific construction of data structures to represent the
103
   * Maglev Table.
104
   */
105
  virtual void constructImplementationInternals(std::vector<TableBuildEntry>& table_build_entries,
106
                                                double max_normalized_weight) PURE;
107
};
108

            
109
/**
110
 * This is an implementation of Maglev consistent hashing that directly holds
111
 * pointers.
112
 */
113
class OriginalMaglevTable : public MaglevTable {
114
public:
115
  OriginalMaglevTable(const NormalizedHostWeightVector& normalized_host_weights,
116
                      double max_normalized_weight, uint64_t table_size,
117
                      bool use_hostname_for_hashing, MaglevLoadBalancerStats& stats)
118
16
      : MaglevTable(table_size, stats) {
119
16
    constructMaglevTableInternal(normalized_host_weights, max_normalized_weight,
120
16
                                 use_hostname_for_hashing);
121
16
  }
122
16
  ~OriginalMaglevTable() override = default;
123

            
124
  // ThreadAwareLoadBalancerBase::HashingLoadBalancer
125
  HostSelectionResponse chooseHost(uint64_t hash, uint32_t attempt) const override;
126

            
127
  void logMaglevTable(bool use_hostname_for_hashing) const override;
128

            
129
private:
130
  void constructImplementationInternals(std::vector<TableBuildEntry>& table_build_entries,
131
                                        double max_normalized_weight) override;
132

            
133
  std::vector<HostConstSharedPtr> table_;
134
};
135

            
136
/**
137
 * This maglev implementation leverages the number of hosts to more efficiently
138
 * populate the maglev table.
139
 * TODO(kbaichoo): Re-evaluate whether we should have the abstraction on the
140
 * table representation.
141
 */
142
class CompactMaglevTable : public MaglevTable {
143
public:
144
  CompactMaglevTable(const NormalizedHostWeightVector& normalized_host_weights,
145
                     double max_normalized_weight, uint64_t table_size,
146
                     bool use_hostname_for_hashing, MaglevLoadBalancerStats& stats);
147
479
  ~CompactMaglevTable() override = default;
148

            
149
  // ThreadAwareLoadBalancerBase::HashingLoadBalancer
150
  HostSelectionResponse chooseHost(uint64_t hash, uint32_t attempt) const override;
151

            
152
  void logMaglevTable(bool use_hostname_for_hashing) const override;
153

            
154
private:
155
  void constructImplementationInternals(std::vector<TableBuildEntry>& table_build_entries,
156
                                        double max_normalized_weight) override;
157

            
158
  // Leverage a BitArray to more compactly fit represent the MaglevTable.
159
  // The BitArray will index into the host_table_ which will provide the given
160
  // host to load balance to.
161
  BitArray table_;
162
  std::vector<HostConstSharedPtr> host_table_;
163
};
164

            
165
/**
166
 * Thread aware load balancer implementation for Maglev.
167
 */
168
class MaglevLoadBalancer : public ThreadAwareLoadBalancerBase {
169
public:
170
  MaglevLoadBalancer(const PrioritySet& priority_set, ClusterLbStats& stats, Stats::Scope& scope,
171
                     Runtime::Loader& runtime, Random::RandomGenerator& random,
172
                     uint32_t healthy_panic_threshold, const MaglevLbProto& config,
173
                     HashPolicySharedPtr hash_policy);
174

            
175
76
  const MaglevLoadBalancerStats& stats() const { return stats_; }
176
4
  uint64_t tableSize() const { return table_size_; }
177

            
178
  static MaglevLoadBalancerStats generateStats(Stats::Scope& scope);
179

            
180
private:
181
  // ThreadAwareLoadBalancerBase
182
  HashingLoadBalancerSharedPtr
183
  createLoadBalancer(const NormalizedHostWeightVector& normalized_host_weights,
184
                     double /* min_normalized_weight */, double max_normalized_weight) override;
185

            
186
  Stats::ScopeSharedPtr scope_;
187
  MaglevLoadBalancerStats stats_;
188
  const uint64_t table_size_;
189
  const bool use_hostname_for_hashing_;
190
  const uint32_t hash_balance_factor_;
191
};
192

            
193
} // namespace Upstream
194
} // namespace Envoy