1
#pragma once
2

            
3
#include <cstdint>
4
#include <memory>
5

            
6
#include "envoy/common/pure.h"
7
#include "envoy/config/cluster/v3/cluster.pb.h"
8
#include "envoy/network/transport_socket.h"
9
#include "envoy/router/router.h"
10
#include "envoy/stream_info/stream_info.h"
11
#include "envoy/upstream/types.h"
12
#include "envoy/upstream/upstream.h"
13

            
14
#include "xds/data/orca/v3/orca_load_report.pb.h"
15

            
16
namespace Envoy {
17
namespace Server {
18
namespace Configuration {
19
class ServerFactoryContext;
20
} // namespace Configuration
21
} // namespace Server
22
namespace Http {
23
namespace ConnectionPool {
24
class ConnectionLifetimeCallbacks;
25
} // namespace ConnectionPool
26
} // namespace Http
27
namespace Upstream {
28

            
29
using ClusterProto = envoy::config::cluster::v3::Cluster;
30

            
31
/*
32
 * A handle to allow cancelation of asynchronous host selection.
33
 * If chooseHost returns a HostSelectionResponse with an AsyncHostSelectionHandle
34
 * handle, and the endpoint does not wish to receive onAsyncHostSelction call,
35
 * it must call cancel() on the provided handle.
36
 *
37
 * Please note that the AsyncHostSelectionHandle may be deleted after the
38
 * cancel() call. It is up to the implemention of the asynchronous load balancer
39
 * to ensure the cancelation state persists until the load balancer checks it.
40
 */
41
class AsyncHostSelectionHandle {
42
public:
43
  AsyncHostSelectionHandle& operator=(const AsyncHostSelectionHandle&) = delete;
44
13981
  virtual ~AsyncHostSelectionHandle() = default;
45
  virtual void cancel() PURE;
46
};
47

            
48
/*
49
 * The response to a LoadBalancer::chooseHost call.
50
 *
51
 * chooseHost either returns a host directly or, in the case of asynchronous
52
 * load balancing, returns an AsyncHostSelectionHandle handle.
53
 *
54
 * If it returns a AsyncHostSelectionHandle handle, the load balancer guarantees an
55
 * eventual call to LoadBalancerContext::onAsyncHostSelction unless
56
 * AsyncHostSelectionHandle::cancel is called.
57
 */
58
struct HostSelectionResponse {
59
  HostSelectionResponse(HostConstSharedPtr host,
60
                        std::unique_ptr<AsyncHostSelectionHandle> cancelable = nullptr)
61
3666641
      : host(host), cancelable(std::move(cancelable)) {}
62
  HostSelectionResponse(HostConstSharedPtr host, std::string details)
63
22
      : host(host), details(details) {}
64
  HostConstSharedPtr host;
65
  // Optional details if host selection fails (empty string implies no details).
66
  std::string details;
67
  std::unique_ptr<AsyncHostSelectionHandle> cancelable;
68
};
69

            
70
/**
71
 * Context information passed to a load balancer to use when choosing a host. Not all load
72
 * balancers make use of all context information.
73
 */
74
class LoadBalancerContext {
75
public:
76
279726
  virtual ~LoadBalancerContext() = default;
77

            
78
  /**
79
   * Compute and return an optional hash key to use during load balancing. This
80
   * method may modify internal state so it should only be called once per
81
   * routing attempt.
82
   * @return absl::optional<uint64_t> the optional hash key to use.
83
   */
84
  virtual absl::optional<uint64_t> computeHashKey() PURE;
85

            
86
  /**
87
   * @return Router::MetadataMatchCriteria* metadata for use in selecting a subset of hosts
88
   *         during load balancing.
89
   */
90
  virtual const Router::MetadataMatchCriteria* metadataMatchCriteria() PURE;
91

            
92
  /**
93
   * @return const Network::Connection* the incoming connection or nullptr to use during load
94
   * balancing.
95
   */
96
  virtual const Network::Connection* downstreamConnection() const PURE;
97

            
98
  /**
99
   * @return const StreamInfo* the incoming request stream info or nullptr to use during load
100
   * balancing.
101
   */
102
  virtual StreamInfo::StreamInfo* requestStreamInfo() const PURE;
103

            
104
  /**
105
   * @return const Http::HeaderMap* the incoming headers or nullptr to use during load
106
   * balancing.
107
   */
108
  virtual const Http::RequestHeaderMap* downstreamHeaders() const PURE;
109

            
110
  /**
111
   * Called to retrieve a reference to the priority load data that should be used when selecting a
112
   * priority. Implementations may return the provided original reference to make no changes, or
113
   * return a reference to alternative PriorityLoad held internally.
114
   *
115
   * @param priority_state current priority state of the cluster being being load balanced.
116
   * @param original_priority_load the cached priority load for the cluster being load balanced.
117
   * @param priority_mapping_func see @Upstream::RetryPriority::PriorityMappingFunc.
118
   * @return a reference to the priority load data that should be used to select a priority.
119
   *
120
   */
121
  virtual const HealthyAndDegradedLoad& determinePriorityLoad(
122
      const PrioritySet& priority_set, const HealthyAndDegradedLoad& original_priority_load,
123
      const Upstream::RetryPriority::PriorityMappingFunc& priority_mapping_func) PURE;
124

            
125
  /**
126
   * Called to determine whether we should reperform host selection. The load balancer
127
   * will retry host selection until either this function returns true or hostSelectionRetryCount is
128
   * reached.
129
   */
130
  virtual bool shouldSelectAnotherHost(const Host& host) PURE;
131

            
132
  /**
133
   * Called to determine how many times host selection should be retried until the filter is
134
   * ignored.
135
   */
136
  virtual uint32_t hostSelectionRetryCount() const PURE;
137

            
138
  /**
139
   * Returns the set of socket options which should be applied on upstream connections
140
   */
141
  virtual Network::Socket::OptionsSharedPtr upstreamSocketOptions() const PURE;
142

            
143
  /**
144
   * Returns the transport socket options which should be applied on upstream connections
145
   */
146
  virtual Network::TransportSocketOptionsConstSharedPtr upstreamTransportSocketOptions() const PURE;
147

            
148
  /**
149
   * Upstream override host. The first element is the target host address and the second element is
150
   * a boolean indicating whether the host should be selected strictly or not.
151
   * If the host should be selected strictly and no valid host is found, the load balancer should
152
   * return  nullptr.
153
   * If the host should not be selected strictly, the load balancer will select another host is the
154
   * target host is not valid.
155
   */
156
  using OverrideHost = std::pair<absl::string_view, bool>;
157
  /**
158
   * Returns the host the load balancer should select directly. If the expected host exists and
159
   * the host can be selected directly, the load balancer can bypass the load balancing algorithm
160
   * and return the corresponding host directly.
161
   */
162
  virtual absl::optional<OverrideHost> overrideHostToSelect() const PURE;
163

            
164
  /* Called by the load balancer when asynchronous host selection completes
165
   * @param host supplies the upstream host selected
166
   * @param details gives optional details about the resolution success/failure.
167
   */
168
  virtual void onAsyncHostSelection(HostConstSharedPtr&& host, std::string&& details) PURE;
169

            
170
  /**
171
   * Called by the load balancer to set the headers modifier that will be used to modify the
172
   * response headers before sending them downstream.
173
   * NOTE: this should be called only once per request, no matter how many times the retrying
174
   * happens.
175
   * @param modifier supplies the function that will modify the response headers.
176
   */
177
  virtual void setHeadersModifier(std::function<void(Http::ResponseHeaderMap&)> modifier) PURE;
178
};
179

            
180
/**
181
 * Identifies a specific connection within a pool.
182
 */
183
struct SelectedPoolAndConnection {
184
  Envoy::ConnectionPool::Instance& pool_;
185
  const Network::Connection& connection_;
186
};
187

            
188
/**
189
 * Abstract load balancing interface.
190
 */
191
class LoadBalancer {
192
public:
193
79747
  virtual ~LoadBalancer() = default;
194

            
195
  /*
196
   * This is a convenience wrapper function for code which does not yet support
197
   * asynchronous host selection. It cancels any asynchronous lookup and treats
198
   * it as host selection failure.
199
   */
200
  static HostConstSharedPtr
201
222157
  onlyAllowSynchronousHostSelection(HostSelectionResponse host_selection) {
202
222157
    if (host_selection.cancelable) {
203
      // Async host selection not handled yet. Treat this as host selection
204
      // failure.
205
      host_selection.cancelable->cancel();
206
    }
207
222157
    return std::move(host_selection.host);
208
222157
  }
209

            
210
  /**
211
   * Ask the load balancer for the next host to use depending on the underlying LB algorithm.
212
   * @param context supplies the load balancer context. Not all load balancers make use of all
213
   *        context information. Load balancers should be written to assume that context information
214
   *        is missing and use sensible defaults.
215
   * @return a HostSelectionResponse either containing a host, or AsyncHostSelectionHandle handle.
216
   *
217
   * Please note that asynchronous host selection is not yet fully supported in
218
   * Envoy. It works for HTTP load balancing (with the notable exclusion of the
219
   * subset load balancer) but not for TCP proxy or other load balancers.
220
   * Updates to functionality should be reflected in load_balancing_policies.rst
221
   */
222
  virtual HostSelectionResponse chooseHost(LoadBalancerContext* context) PURE;
223

            
224
  /**
225
   * Returns a best effort prediction of the next host to be picked, or nullptr if not predictable.
226
   * Advances with subsequent calls, so while the first call will return the next host to be picked,
227
   * a subsequent call will return the second host to be picked.
228
   * @param context supplies the context which is used in host selection.
229
   */
230
  virtual HostConstSharedPtr peekAnotherHost(LoadBalancerContext* context) PURE;
231

            
232
  /**
233
   * Returns connection lifetime callbacks that may be used to inform the load balancer of
234
   * connection events. Load balancers which do not intend to track connection lifetime events
235
   * will return nullopt.
236
   * @return optional lifetime callbacks for this load balancer.
237
   */
238
  virtual OptRef<Envoy::Http::ConnectionPool::ConnectionLifetimeCallbacks> lifetimeCallbacks() PURE;
239

            
240
  /**
241
   * Returns a specific pool and existing connection to be used for the specified host.
242
   *
243
   * @return selected pool and connection to be used, or nullopt if no selection is made,
244
   *         for example if no matching connection is found.
245
   */
246
  virtual absl::optional<SelectedPoolAndConnection>
247
  selectExistingConnection(LoadBalancerContext* context, const Host& host,
248
                           std::vector<uint8_t>& hash_key) PURE;
249
};
250

            
251
using LoadBalancerPtr = std::unique_ptr<LoadBalancer>;
252

            
253
/**
254
 * Necessary parameters for creating a worker local load balancer.
255
 */
256
struct LoadBalancerParams {
257
  // The worker local priority set of the target cluster.
258
  const PrioritySet& priority_set;
259
  // The worker local priority set of the local cluster.
260
  const PrioritySet* local_priority_set{};
261
};
262

            
263
/**
264
 * Factory for load balancers.
265
 */
266
class LoadBalancerFactory {
267
public:
268
18305
  virtual ~LoadBalancerFactory() = default;
269

            
270
  /**
271
   * @return LoadBalancerPtr a new worker local load balancer.
272
   */
273
  virtual LoadBalancerPtr create(LoadBalancerParams params) PURE;
274

            
275
  /**
276
   * @return bool whether the load balancer should be recreated when the host set changes.
277
   */
278
959
  virtual bool recreateOnHostChange() const { return true; }
279
};
280

            
281
using LoadBalancerFactorySharedPtr = std::shared_ptr<LoadBalancerFactory>;
282

            
283
/**
284
 * A thread aware load balancer is a load balancer that is global to all workers on behalf of a
285
 * cluster. These load balancers are harder to write so not every load balancer has to be one.
286
 * If a load balancer is a thread aware load balancer, the following semantics are used:
287
 * 1) A single instance is created on the main thread.
288
 * 2) The shared factory is passed to all workers.
289
 * 3) Every time there is a host set update on the main thread, all workers will create a new
290
 *    worker local load balancer via the factory.
291
 *
292
 * The above semantics mean that any global state in the factory must be protected by appropriate
293
 * locks. Additionally, the factory *must not* refer back to the owning thread aware load
294
 * balancer. If a cluster is removed via CDS, the thread aware load balancer can be destroyed
295
 * before cluster destruction reaches each worker. See the ring hash load balancer for one
296
 * example of how this pattern is used in practice. The common expected pattern is that the
297
 * factory will be consuming shared immutable state from the main thread
298
 *
299
 * TODO(mattklein123): The reason that locking is used in the above threading model vs. pure TLS
300
 * has to do with the lack of a TLS function that does the following:
301
 * 1) Create a per-worker data structure on the main thread. E.g., allocate 4 objects for 4
302
 *    workers.
303
 * 2) Then fan those objects out to each worker.
304
 * With the existence of a function like that, the callback locking from the worker to the main
305
 * thread could be removed. We can look at this in a follow up. The reality though is that the
306
 * locking is currently only used to protect some small bits of data on host set update and will
307
 * never be contended.
308
 */
309
class ThreadAwareLoadBalancer {
310
public:
311
18422
  virtual ~ThreadAwareLoadBalancer() = default;
312

            
313
  /**
314
   * @return LoadBalancerFactorySharedPtr the shared factory to use for creating new worker local
315
   * load balancers.
316
   */
317
  virtual LoadBalancerFactorySharedPtr factory() PURE;
318

            
319
  /**
320
   * When a thread aware load balancer is constructed, it should return nullptr for any created
321
   * load balancer chooseHost() calls. Once initialize is called, the load balancer should
322
   * instantiate any needed structured and prepare for further updates. The cluster manager
323
   * will do this at the appropriate time.
324
   */
325
  virtual absl::Status initialize() PURE;
326
};
327

            
328
using ThreadAwareLoadBalancerPtr = std::unique_ptr<ThreadAwareLoadBalancer>;
329

            
330
/*
331
 * Parsed load balancer configuration that will be used to create load balancer.
332
 */
333
class LoadBalancerConfig {
334
public:
335
246995
  virtual ~LoadBalancerConfig() = default;
336

            
337
  /**
338
   * Optional method to allow a load balancer to validate endpoints before they're applied. If an
339
   * error is returned from this method, the endpoints are rejected. If this method does not return
340
   * an error, the load balancer must be able to use these endpoints in an update from the priority
341
   * set.
342
   */
343
16939
  virtual absl::Status validateEndpoints(const PriorityState&) const { return absl::OkStatus(); }
344
};
345
using LoadBalancerConfigPtr = std::unique_ptr<LoadBalancerConfig>;
346

            
347
/**
348
 * Factory config for load balancers. To support a load balancing policy of
349
 * LOAD_BALANCING_POLICY_CONFIG, at least one load balancer factory corresponding to a policy in
350
 * load_balancing_policy must be registered with Envoy. Envoy will use the first policy for which
351
 * it has a registered factory.
352
 */
353
class TypedLoadBalancerFactory : public Config::TypedFactory {
354
public:
355
80
  ~TypedLoadBalancerFactory() override = default;
356

            
357
  /**
358
   * @return ThreadAwareLoadBalancerPtr a new thread-aware load balancer.
359
   *
360
   * @param lb_config supplies the parsed config of the load balancer.
361
   * @param cluster_info supplies the cluster info.
362
   * @param priority_set supplies the priority set on the main thread.
363
   * @param runtime supplies the runtime loader.
364
   * @param random supplies the random generator.
365
   * @param time_source supplies the time source.
366
   */
367
  virtual ThreadAwareLoadBalancerPtr
368
  create(OptRef<const LoadBalancerConfig> lb_config, const ClusterInfo& cluster_info,
369
         const PrioritySet& priority_set, Runtime::Loader& runtime, Random::RandomGenerator& random,
370
         TimeSource& time_source) PURE;
371

            
372
  /**
373
   * This method is used to validate and create load balancer config from typed proto config.
374
   *
375
   * @return LoadBalancerConfigPtr a new load balancer config or error.
376
   *
377
   * @param factory_context supplies the load balancer factory context.
378
   * @param config supplies the typed proto config of the load balancer. A dynamic_cast could
379
   *        be performed on the config to the expected proto type.
380
   */
381
  virtual absl::StatusOr<LoadBalancerConfigPtr>
382
  loadConfig(Server::Configuration::ServerFactoryContext& factory_context,
383
             const Protobuf::Message& config) PURE;
384

            
385
  /**
386
   * This method is used to validate and create load balancer config from legacy proto config.
387
   * This method is only used for backwards compatibility with the legacy cluster config.
388
   *
389
   * @return LoadBalancerConfigPtr a new load balancer config or error.
390
   *
391
   * @param factory_context supplies the load balancer factory context.
392
   * @param cluster supplies the legacy proto config of the cluster.
393
   */
394
  virtual absl::StatusOr<LoadBalancerConfigPtr>
395
  loadLegacy(Server::Configuration::ServerFactoryContext& factory_context,
396
             const ClusterProto& cluster) {
397
    UNREFERENCED_PARAMETER(cluster);
398
    UNREFERENCED_PARAMETER(factory_context);
399
    return nullptr;
400
  }
401

            
402
5137
  std::string category() const override { return "envoy.load_balancing_policies"; }
403
};
404

            
405
} // namespace Upstream
406
} // namespace Envoy