1
#include "source/extensions/load_balancing_policies/dynamic_modules/load_balancer.h"
2

            
3
namespace Envoy {
4
namespace Extensions {
5
namespace LoadBalancingPolicies {
6
namespace DynamicModules {
7

            
8
DynamicModuleLoadBalancer::DynamicModuleLoadBalancer(DynamicModuleLbConfigSharedPtr config,
9
                                                     const Upstream::PrioritySet& priority_set,
10
                                                     const std::string& cluster_name)
11
30
    : config_(std::move(config)), priority_set_(priority_set), cluster_name_(cluster_name),
12
30
      in_module_lb_(nullptr) {
13
  // Create the in-module load balancer instance.
14
30
  in_module_lb_ = config_->on_lb_new_(config_->in_module_config_, this);
15
30
  if (in_module_lb_ == nullptr) {
16
2
    ENVOY_LOG(error, "failed to create in-module load balancer instance");
17
2
    return;
18
2
  }
19

            
20
  // Register for host membership updates.
21
28
  member_update_cb_ = priority_set_.addMemberUpdateCb(
22
28
      [this](const Upstream::HostVector& hosts_added, const Upstream::HostVector& hosts_removed) {
23
2
        hosts_added_ = &hosts_added;
24
2
        hosts_removed_ = &hosts_removed;
25
2
        config_->on_host_membership_update_(this, in_module_lb_, hosts_added.size(),
26
2
                                            hosts_removed.size());
27
2
        hosts_added_ = nullptr;
28
2
        hosts_removed_ = nullptr;
29
2
      });
30
28
}
31

            
32
30
DynamicModuleLoadBalancer::~DynamicModuleLoadBalancer() {
33
30
  if (in_module_lb_ != nullptr && config_->on_lb_destroy_ != nullptr) {
34
28
    config_->on_lb_destroy_(in_module_lb_);
35
28
    in_module_lb_ = nullptr;
36
28
  }
37
30
}
38

            
39
Upstream::HostSelectionResponse
40
13
DynamicModuleLoadBalancer::chooseHost(Upstream::LoadBalancerContext* context) {
41
13
  if (in_module_lb_ == nullptr) {
42
2
    return {nullptr};
43
2
  }
44

            
45
  // Call the module's chooseHost function.
46
11
  uint32_t priority = 0;
47
11
  uint32_t host_index = 0;
48
11
  bool selected = config_->on_choose_host_(this, in_module_lb_, context, &priority, &host_index);
49

            
50
11
  if (!selected) {
51
2
    return {nullptr};
52
2
  }
53

            
54
9
  const auto& host_sets = priority_set_.hostSetsPerPriority();
55
9
  if (priority >= host_sets.size()) {
56
1
    ENVOY_LOG(warn, "dynamic module returned invalid priority {} (priorities: {})", priority,
57
1
              host_sets.size());
58
1
    return {nullptr};
59
1
  }
60

            
61
8
  const auto& healthy_hosts = host_sets[priority]->healthyHosts();
62
8
  if (host_index >= healthy_hosts.size()) {
63
1
    ENVOY_LOG(warn,
64
1
              "dynamic module returned invalid host index {} at priority {} (healthy hosts: {})",
65
1
              host_index, priority, healthy_hosts.size());
66
1
    return {nullptr};
67
1
  }
68

            
69
7
  return {healthy_hosts[host_index]};
70
8
}
71

            
72
Upstream::HostConstSharedPtr
73
1
DynamicModuleLoadBalancer::peekAnotherHost(Upstream::LoadBalancerContext*) {
74
  // Not implemented - return nullptr.
75
1
  return nullptr;
76
1
}
77

            
78
OptRef<Envoy::Http::ConnectionPool::ConnectionLifetimeCallbacks>
79
1
DynamicModuleLoadBalancer::lifetimeCallbacks() {
80
1
  return {};
81
1
}
82

            
83
absl::optional<Upstream::SelectedPoolAndConnection>
84
DynamicModuleLoadBalancer::selectExistingConnection(Upstream::LoadBalancerContext*,
85
1
                                                    const Upstream::Host&, std::vector<uint8_t>&) {
86
1
  return absl::nullopt;
87
1
}
88

            
89
7
bool DynamicModuleLoadBalancer::setHostData(uint32_t priority, size_t index, uintptr_t data) {
90
7
  const auto& host_sets = priority_set_.hostSetsPerPriority();
91
7
  if (priority >= host_sets.size()) {
92
1
    return false;
93
1
  }
94
6
  const auto& hosts = host_sets[priority]->hosts();
95
6
  if (index >= hosts.size()) {
96
1
    return false;
97
1
  }
98
5
  if (data == 0) {
99
1
    per_host_data_.erase({priority, index});
100
4
  } else {
101
4
    per_host_data_[{priority, index}] = data;
102
4
  }
103
5
  return true;
104
6
}
105

            
106
bool DynamicModuleLoadBalancer::getHostData(uint32_t priority, size_t index,
107
8
                                            uintptr_t* data) const {
108
8
  const auto& host_sets = priority_set_.hostSetsPerPriority();
109
8
  if (priority >= host_sets.size()) {
110
1
    return false;
111
1
  }
112
7
  const auto& hosts = host_sets[priority]->hosts();
113
7
  if (index >= hosts.size()) {
114
1
    return false;
115
1
  }
116
6
  auto it = per_host_data_.find({priority, index});
117
6
  if (it != per_host_data_.end()) {
118
4
    *data = it->second;
119
4
  } else {
120
2
    *data = 0;
121
2
  }
122
6
  return true;
123
7
}
124

            
125
} // namespace DynamicModules
126
} // namespace LoadBalancingPolicies
127
} // namespace Extensions
128
} // namespace Envoy