1 //
2 // Copyright 2018 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <grpc/support/port_platform.h>
18
19 #include "absl/strings/string_view.h"
20
21 #include <grpc/grpc.h>
22
23 #include "src/core/ext/filters/client_channel/lb_policy.h"
24 #include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h"
25 #include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h"
26 #include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_args.h"
27 #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
28 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
29 #include "src/core/ext/xds/xds_client.h"
30 #include "src/core/ext/xds/xds_client_stats.h"
31 #include "src/core/lib/channel/channel_args.h"
32 #include "src/core/lib/gpr/env.h"
33 #include "src/core/lib/gpr/string.h"
34 #include "src/core/lib/gprpp/orphanable.h"
35 #include "src/core/lib/gprpp/ref_counted_ptr.h"
36 #include "src/core/lib/gprpp/sync.h"
37 #include "src/core/lib/iomgr/work_serializer.h"
38
39 namespace grpc_core {
40
41 TraceFlag grpc_xds_cluster_impl_lb_trace(false, "xds_cluster_impl_lb");
42
43 namespace {
44
45 //
46 // global circuit breaker atomic map
47 //
48
49 class CircuitBreakerCallCounterMap {
50 public:
51 using Key =
52 std::pair<std::string /*cluster*/, std::string /*eds_service_name*/>;
53
54 class CallCounter : public RefCounted<CallCounter> {
55 public:
CallCounter(Key key)56 explicit CallCounter(Key key) : key_(std::move(key)) {}
57 ~CallCounter() override;
58
Increment()59 uint32_t Increment() { return concurrent_requests_.FetchAdd(1); }
Decrement()60 void Decrement() { concurrent_requests_.FetchSub(1); }
61
62 private:
63 Key key_;
64 Atomic<uint32_t> concurrent_requests_{0};
65 };
66
67 RefCountedPtr<CallCounter> GetOrCreate(const std::string& cluster,
68 const std::string& eds_service_name);
69
70 private:
71 Mutex mu_;
72 std::map<Key, CallCounter*> map_;
73 };
74
75 CircuitBreakerCallCounterMap* g_call_counter_map = nullptr;
76
77 RefCountedPtr<CircuitBreakerCallCounterMap::CallCounter>
GetOrCreate(const std::string & cluster,const std::string & eds_service_name)78 CircuitBreakerCallCounterMap::GetOrCreate(const std::string& cluster,
79 const std::string& eds_service_name) {
80 Key key(cluster, eds_service_name);
81 RefCountedPtr<CallCounter> result;
82 MutexLock lock(&mu_);
83 auto it = map_.find(key);
84 if (it == map_.end()) {
85 it = map_.insert({key, nullptr}).first;
86 } else {
87 result = it->second->RefIfNonZero();
88 }
89 if (result == nullptr) {
90 result = MakeRefCounted<CallCounter>(std::move(key));
91 it->second = result.get();
92 }
93 return result;
94 }
95
~CallCounter()96 CircuitBreakerCallCounterMap::CallCounter::~CallCounter() {
97 MutexLock lock(&g_call_counter_map->mu_);
98 auto it = g_call_counter_map->map_.find(key_);
99 if (it != g_call_counter_map->map_.end() && it->second == this) {
100 g_call_counter_map->map_.erase(it);
101 }
102 }
103
104 //
105 // LB policy
106 //
107
108 constexpr char kXdsClusterImpl[] = "xds_cluster_impl_experimental";
109
110 // TODO (donnadionne): Check to see if circuit breaking is enabled, this will be
111 // removed once circuit breaking feature is fully integrated and enabled by
112 // default.
XdsCircuitBreakingEnabled()113 bool XdsCircuitBreakingEnabled() {
114 char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_CIRCUIT_BREAKING");
115 bool parsed_value;
116 bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
117 gpr_free(value);
118 return parse_succeeded && parsed_value;
119 }
120
121 // Config for xDS Cluster Impl LB policy.
122 class XdsClusterImplLbConfig : public LoadBalancingPolicy::Config {
123 public:
XdsClusterImplLbConfig(RefCountedPtr<LoadBalancingPolicy::Config> child_policy,std::string cluster_name,std::string eds_service_name,absl::optional<std::string> lrs_load_reporting_server_name,uint32_t max_concurrent_requests,RefCountedPtr<XdsApi::EdsUpdate::DropConfig> drop_config)124 XdsClusterImplLbConfig(
125 RefCountedPtr<LoadBalancingPolicy::Config> child_policy,
126 std::string cluster_name, std::string eds_service_name,
127 absl::optional<std::string> lrs_load_reporting_server_name,
128 uint32_t max_concurrent_requests,
129 RefCountedPtr<XdsApi::EdsUpdate::DropConfig> drop_config)
130 : child_policy_(std::move(child_policy)),
131 cluster_name_(std::move(cluster_name)),
132 eds_service_name_(std::move(eds_service_name)),
133 lrs_load_reporting_server_name_(
134 std::move(lrs_load_reporting_server_name)),
135 max_concurrent_requests_(max_concurrent_requests),
136 drop_config_(std::move(drop_config)) {}
137
name() const138 const char* name() const override { return kXdsClusterImpl; }
139
child_policy() const140 RefCountedPtr<LoadBalancingPolicy::Config> child_policy() const {
141 return child_policy_;
142 }
cluster_name() const143 const std::string& cluster_name() const { return cluster_name_; }
eds_service_name() const144 const std::string& eds_service_name() const { return eds_service_name_; }
lrs_load_reporting_server_name() const145 const absl::optional<std::string>& lrs_load_reporting_server_name() const {
146 return lrs_load_reporting_server_name_;
147 };
max_concurrent_requests() const148 const uint32_t max_concurrent_requests() const {
149 return max_concurrent_requests_;
150 }
drop_config() const151 RefCountedPtr<XdsApi::EdsUpdate::DropConfig> drop_config() const {
152 return drop_config_;
153 }
154
155 private:
156 RefCountedPtr<LoadBalancingPolicy::Config> child_policy_;
157 std::string cluster_name_;
158 std::string eds_service_name_;
159 absl::optional<std::string> lrs_load_reporting_server_name_;
160 uint32_t max_concurrent_requests_;
161 RefCountedPtr<XdsApi::EdsUpdate::DropConfig> drop_config_;
162 };
163
164 // xDS Cluster Impl LB policy.
165 class XdsClusterImplLb : public LoadBalancingPolicy {
166 public:
167 XdsClusterImplLb(RefCountedPtr<XdsClient> xds_client, Args args);
168
name() const169 const char* name() const override { return kXdsClusterImpl; }
170
171 void UpdateLocked(UpdateArgs args) override;
172 void ExitIdleLocked() override;
173 void ResetBackoffLocked() override;
174
175 private:
176 class StatsSubchannelWrapper : public DelegatingSubchannel {
177 public:
StatsSubchannelWrapper(RefCountedPtr<SubchannelInterface> wrapped_subchannel,RefCountedPtr<XdsClusterLocalityStats> locality_stats)178 StatsSubchannelWrapper(
179 RefCountedPtr<SubchannelInterface> wrapped_subchannel,
180 RefCountedPtr<XdsClusterLocalityStats> locality_stats)
181 : DelegatingSubchannel(std::move(wrapped_subchannel)),
182 locality_stats_(std::move(locality_stats)) {}
183
locality_stats() const184 XdsClusterLocalityStats* locality_stats() const {
185 return locality_stats_.get();
186 }
187
188 private:
189 RefCountedPtr<XdsClusterLocalityStats> locality_stats_;
190 };
191
192 // A simple wrapper for ref-counting a picker from the child policy.
193 class RefCountedPicker : public RefCounted<RefCountedPicker> {
194 public:
RefCountedPicker(std::unique_ptr<SubchannelPicker> picker)195 explicit RefCountedPicker(std::unique_ptr<SubchannelPicker> picker)
196 : picker_(std::move(picker)) {}
Pick(PickArgs args)197 PickResult Pick(PickArgs args) { return picker_->Pick(args); }
198
199 private:
200 std::unique_ptr<SubchannelPicker> picker_;
201 };
202
203 // A picker that wraps the picker from the child to perform drops.
204 class Picker : public SubchannelPicker {
205 public:
206 Picker(XdsClusterImplLb* xds_cluster_impl_lb,
207 RefCountedPtr<RefCountedPicker> picker);
208
209 PickResult Pick(PickArgs args) override;
210
211 private:
212 RefCountedPtr<CircuitBreakerCallCounterMap::CallCounter> call_counter_;
213 bool xds_circuit_breaking_enabled_;
214 uint32_t max_concurrent_requests_;
215 RefCountedPtr<XdsApi::EdsUpdate::DropConfig> drop_config_;
216 RefCountedPtr<XdsClusterDropStats> drop_stats_;
217 RefCountedPtr<RefCountedPicker> picker_;
218 };
219
220 class Helper : public ChannelControlHelper {
221 public:
Helper(RefCountedPtr<XdsClusterImplLb> xds_cluster_impl_policy)222 explicit Helper(RefCountedPtr<XdsClusterImplLb> xds_cluster_impl_policy)
223 : xds_cluster_impl_policy_(std::move(xds_cluster_impl_policy)) {}
224
~Helper()225 ~Helper() override {
226 xds_cluster_impl_policy_.reset(DEBUG_LOCATION, "Helper");
227 }
228
229 RefCountedPtr<SubchannelInterface> CreateSubchannel(
230 ServerAddress address, const grpc_channel_args& args) override;
231 void UpdateState(grpc_connectivity_state state, const absl::Status& status,
232 std::unique_ptr<SubchannelPicker> picker) override;
233 void RequestReresolution() override;
234 void AddTraceEvent(TraceSeverity severity,
235 absl::string_view message) override;
236
237 private:
238 RefCountedPtr<XdsClusterImplLb> xds_cluster_impl_policy_;
239 };
240
241 ~XdsClusterImplLb() override;
242
243 void ShutdownLocked() override;
244
245 OrphanablePtr<LoadBalancingPolicy> CreateChildPolicyLocked(
246 const grpc_channel_args* args);
247 void UpdateChildPolicyLocked(ServerAddressList addresses,
248 const grpc_channel_args* args);
249
250 void MaybeUpdatePickerLocked();
251
252 // Current config from the resolver.
253 RefCountedPtr<XdsClusterImplLbConfig> config_;
254
255 // Current concurrent number of requests.
256 RefCountedPtr<CircuitBreakerCallCounterMap::CallCounter> call_counter_;
257
258 // Internal state.
259 bool shutting_down_ = false;
260
261 // The xds client.
262 RefCountedPtr<XdsClient> xds_client_;
263
264 // The stats for client-side load reporting.
265 RefCountedPtr<XdsClusterDropStats> drop_stats_;
266
267 OrphanablePtr<LoadBalancingPolicy> child_policy_;
268
269 // Latest state and picker reported by the child policy.
270 grpc_connectivity_state state_ = GRPC_CHANNEL_IDLE;
271 absl::Status status_;
272 RefCountedPtr<RefCountedPicker> picker_;
273 };
274
275 //
276 // XdsClusterImplLb::Picker
277 //
278
Picker(XdsClusterImplLb * xds_cluster_impl_lb,RefCountedPtr<RefCountedPicker> picker)279 XdsClusterImplLb::Picker::Picker(XdsClusterImplLb* xds_cluster_impl_lb,
280 RefCountedPtr<RefCountedPicker> picker)
281 : call_counter_(xds_cluster_impl_lb->call_counter_),
282 xds_circuit_breaking_enabled_(XdsCircuitBreakingEnabled()),
283 max_concurrent_requests_(
284 xds_cluster_impl_lb->config_->max_concurrent_requests()),
285 drop_config_(xds_cluster_impl_lb->config_->drop_config()),
286 drop_stats_(xds_cluster_impl_lb->drop_stats_),
287 picker_(std::move(picker)) {
288 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
289 gpr_log(GPR_INFO, "[xds_cluster_impl_lb %p] constructed new picker %p",
290 xds_cluster_impl_lb, this);
291 }
292 }
293
Pick(LoadBalancingPolicy::PickArgs args)294 LoadBalancingPolicy::PickResult XdsClusterImplLb::Picker::Pick(
295 LoadBalancingPolicy::PickArgs args) {
296 // Handle EDS drops.
297 const std::string* drop_category;
298 if (drop_config_->ShouldDrop(&drop_category)) {
299 if (drop_stats_ != nullptr) drop_stats_->AddCallDropped(*drop_category);
300 PickResult result;
301 result.type = PickResult::PICK_COMPLETE;
302 return result;
303 }
304 // Handle circuit breaking.
305 uint32_t current = call_counter_->Increment();
306 if (xds_circuit_breaking_enabled_) {
307 // Check and see if we exceeded the max concurrent requests count.
308 if (current >= max_concurrent_requests_) {
309 call_counter_->Decrement();
310 if (drop_stats_ != nullptr) drop_stats_->AddUncategorizedDrops();
311 PickResult result;
312 result.type = PickResult::PICK_COMPLETE;
313 return result;
314 }
315 }
316 // If we're not dropping the call, we should always have a child picker.
317 if (picker_ == nullptr) { // Should never happen.
318 PickResult result;
319 result.type = PickResult::PICK_FAILED;
320 result.error = grpc_error_set_int(
321 GRPC_ERROR_CREATE_FROM_STATIC_STRING(
322 "xds_cluster_impl picker not given any child picker"),
323 GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_INTERNAL);
324 call_counter_->Decrement();
325 return result;
326 }
327 // Not dropping, so delegate to child picker.
328 PickResult result = picker_->Pick(args);
329 if (result.type == result.PICK_COMPLETE && result.subchannel != nullptr) {
330 XdsClusterLocalityStats* locality_stats = nullptr;
331 if (drop_stats_ != nullptr) { // If load reporting is enabled.
332 auto* subchannel_wrapper =
333 static_cast<StatsSubchannelWrapper*>(result.subchannel.get());
334 // Handle load reporting.
335 locality_stats = subchannel_wrapper->locality_stats()->Ref().release();
336 // Record a call started.
337 locality_stats->AddCallStarted();
338 // Unwrap subchannel to pass back up the stack.
339 result.subchannel = subchannel_wrapper->wrapped_subchannel();
340 }
341 // Intercept the recv_trailing_metadata op to record call completion.
342 auto* call_counter = call_counter_->Ref(DEBUG_LOCATION, "call").release();
343 auto original_recv_trailing_metadata_ready =
344 result.recv_trailing_metadata_ready;
345 result.recv_trailing_metadata_ready =
346 // Note: This callback does not run in either the control plane
347 // work serializer or in the data plane mutex.
348 [locality_stats, original_recv_trailing_metadata_ready, call_counter](
349 grpc_error* error, MetadataInterface* metadata,
350 CallState* call_state) {
351 // Record call completion for load reporting.
352 if (locality_stats != nullptr) {
353 const bool call_failed = error != GRPC_ERROR_NONE;
354 locality_stats->AddCallFinished(call_failed);
355 locality_stats->Unref(DEBUG_LOCATION, "LocalityStats+call");
356 }
357 // Decrement number of calls in flight.
358 call_counter->Decrement();
359 call_counter->Unref(DEBUG_LOCATION, "call");
360 // Invoke the original recv_trailing_metadata_ready callback, if any.
361 if (original_recv_trailing_metadata_ready != nullptr) {
362 original_recv_trailing_metadata_ready(error, metadata, call_state);
363 }
364 };
365 } else {
366 // TODO(roth): We should ideally also record call failures here in the case
367 // where a pick fails. This is challenging, because we don't know which
368 // picks are for wait_for_ready RPCs or how many times we'll return a
369 // failure for the same wait_for_ready RPC.
370 call_counter_->Decrement();
371 }
372 return result;
373 }
374
375 //
376 // XdsClusterImplLb
377 //
378
XdsClusterImplLb(RefCountedPtr<XdsClient> xds_client,Args args)379 XdsClusterImplLb::XdsClusterImplLb(RefCountedPtr<XdsClient> xds_client,
380 Args args)
381 : LoadBalancingPolicy(std::move(args)), xds_client_(std::move(xds_client)) {
382 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
383 gpr_log(GPR_INFO, "[xds_cluster_impl_lb %p] created -- using xds client %p",
384 this, xds_client_.get());
385 }
386 }
387
~XdsClusterImplLb()388 XdsClusterImplLb::~XdsClusterImplLb() {
389 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
390 gpr_log(GPR_INFO,
391 "[xds_cluster_impl_lb %p] destroying xds_cluster_impl LB policy",
392 this);
393 }
394 }
395
ShutdownLocked()396 void XdsClusterImplLb::ShutdownLocked() {
397 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
398 gpr_log(GPR_INFO, "[xds_cluster_impl_lb %p] shutting down", this);
399 }
400 shutting_down_ = true;
401 // Remove the child policy's interested_parties pollset_set from the
402 // xDS policy.
403 if (child_policy_ != nullptr) {
404 grpc_pollset_set_del_pollset_set(child_policy_->interested_parties(),
405 interested_parties());
406 child_policy_.reset();
407 }
408 // Drop our ref to the child's picker, in case it's holding a ref to
409 // the child.
410 picker_.reset();
411 drop_stats_.reset();
412 xds_client_.reset();
413 }
414
ExitIdleLocked()415 void XdsClusterImplLb::ExitIdleLocked() {
416 if (child_policy_ != nullptr) child_policy_->ExitIdleLocked();
417 }
418
ResetBackoffLocked()419 void XdsClusterImplLb::ResetBackoffLocked() {
420 // The XdsClient will have its backoff reset by the xds resolver, so we
421 // don't need to do it here.
422 if (child_policy_ != nullptr) child_policy_->ResetBackoffLocked();
423 }
424
UpdateLocked(UpdateArgs args)425 void XdsClusterImplLb::UpdateLocked(UpdateArgs args) {
426 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
427 gpr_log(GPR_INFO, "[xds_cluster_impl_lb %p] Received update", this);
428 }
429 // Update config.
430 const bool is_initial_update = config_ == nullptr;
431 auto old_config = std::move(config_);
432 config_ = std::move(args.config);
433 // On initial update, create drop stats.
434 if (is_initial_update) {
435 if (config_->lrs_load_reporting_server_name().has_value()) {
436 drop_stats_ = xds_client_->AddClusterDropStats(
437 config_->lrs_load_reporting_server_name().value(),
438 config_->cluster_name(), config_->eds_service_name());
439 }
440 call_counter_ = g_call_counter_map->GetOrCreate(
441 config_->cluster_name(), config_->eds_service_name());
442 } else {
443 // Cluster name, EDS service name, and LRS server name should never
444 // change, because the EDS policy above us should be swapped out if
445 // that happens.
446 GPR_ASSERT(config_->cluster_name() == old_config->cluster_name());
447 GPR_ASSERT(config_->eds_service_name() == old_config->eds_service_name());
448 GPR_ASSERT(config_->lrs_load_reporting_server_name() ==
449 old_config->lrs_load_reporting_server_name());
450 }
451 // Update picker if max_concurrent_requests has changed.
452 if (is_initial_update || config_->max_concurrent_requests() !=
453 old_config->max_concurrent_requests()) {
454 MaybeUpdatePickerLocked();
455 }
456 // Update child policy.
457 UpdateChildPolicyLocked(std::move(args.addresses), args.args);
458 }
459
MaybeUpdatePickerLocked()460 void XdsClusterImplLb::MaybeUpdatePickerLocked() {
461 // If we're dropping all calls, report READY, regardless of what (or
462 // whether) the child has reported.
463 if (config_->drop_config() != nullptr && config_->drop_config()->drop_all()) {
464 auto drop_picker = absl::make_unique<Picker>(this, picker_);
465 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
466 gpr_log(GPR_INFO,
467 "[xds_cluster_impl_lb %p] updating connectivity (drop all): "
468 "state=READY "
469 "picker=%p",
470 this, drop_picker.get());
471 }
472 channel_control_helper()->UpdateState(GRPC_CHANNEL_READY, absl::Status(),
473 std::move(drop_picker));
474 return;
475 }
476 // Otherwise, update only if we have a child picker.
477 if (picker_ != nullptr) {
478 auto drop_picker = absl::make_unique<Picker>(this, picker_);
479 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
480 gpr_log(GPR_INFO,
481 "[xds_cluster_impl_lb %p] updating connectivity: state=%s "
482 "status=(%s) "
483 "picker=%p",
484 this, ConnectivityStateName(state_), status_.ToString().c_str(),
485 drop_picker.get());
486 }
487 channel_control_helper()->UpdateState(state_, status_,
488 std::move(drop_picker));
489 }
490 }
491
CreateChildPolicyLocked(const grpc_channel_args * args)492 OrphanablePtr<LoadBalancingPolicy> XdsClusterImplLb::CreateChildPolicyLocked(
493 const grpc_channel_args* args) {
494 LoadBalancingPolicy::Args lb_policy_args;
495 lb_policy_args.work_serializer = work_serializer();
496 lb_policy_args.args = args;
497 lb_policy_args.channel_control_helper =
498 absl::make_unique<Helper>(Ref(DEBUG_LOCATION, "Helper"));
499 OrphanablePtr<LoadBalancingPolicy> lb_policy =
500 MakeOrphanable<ChildPolicyHandler>(std::move(lb_policy_args),
501 &grpc_xds_cluster_impl_lb_trace);
502 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
503 gpr_log(GPR_INFO,
504 "[xds_cluster_impl_lb %p] Created new child policy handler %p",
505 this, lb_policy.get());
506 }
507 // Add our interested_parties pollset_set to that of the newly created
508 // child policy. This will make the child policy progress upon activity on
509 // this policy, which in turn is tied to the application's call.
510 grpc_pollset_set_add_pollset_set(lb_policy->interested_parties(),
511 interested_parties());
512 return lb_policy;
513 }
514
UpdateChildPolicyLocked(ServerAddressList addresses,const grpc_channel_args * args)515 void XdsClusterImplLb::UpdateChildPolicyLocked(ServerAddressList addresses,
516 const grpc_channel_args* args) {
517 // Create policy if needed.
518 if (child_policy_ == nullptr) {
519 child_policy_ = CreateChildPolicyLocked(args);
520 }
521 // Construct update args.
522 UpdateArgs update_args;
523 update_args.addresses = std::move(addresses);
524 update_args.config = config_->child_policy();
525 grpc_arg cluster_arg = grpc_channel_arg_string_create(
526 const_cast<char*>(GRPC_ARG_XDS_CLUSTER_NAME),
527 const_cast<char*>(config_->cluster_name().c_str()));
528 update_args.args = grpc_channel_args_copy_and_add(args, &cluster_arg, 1);
529 // Update the policy.
530 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
531 gpr_log(GPR_INFO,
532 "[xds_cluster_impl_lb %p] Updating child policy handler %p", this,
533 child_policy_.get());
534 }
535 child_policy_->UpdateLocked(std::move(update_args));
536 }
537
538 //
539 // XdsClusterImplLb::Helper
540 //
541
CreateSubchannel(ServerAddress address,const grpc_channel_args & args)542 RefCountedPtr<SubchannelInterface> XdsClusterImplLb::Helper::CreateSubchannel(
543 ServerAddress address, const grpc_channel_args& args) {
544 if (xds_cluster_impl_policy_->shutting_down_) return nullptr;
545 // If load reporting is enabled, wrap the subchannel such that it
546 // includes the locality stats object, which will be used by the EdsPicker.
547 if (xds_cluster_impl_policy_->config_->lrs_load_reporting_server_name()
548 .has_value()) {
549 RefCountedPtr<XdsLocalityName> locality_name;
550 auto* attribute = address.GetAttribute(kXdsLocalityNameAttributeKey);
551 if (attribute != nullptr) {
552 const auto* locality_attr =
553 static_cast<const XdsLocalityAttribute*>(attribute);
554 locality_name = locality_attr->locality_name();
555 }
556 RefCountedPtr<XdsClusterLocalityStats> locality_stats =
557 xds_cluster_impl_policy_->xds_client_->AddClusterLocalityStats(
558 *xds_cluster_impl_policy_->config_
559 ->lrs_load_reporting_server_name(),
560 xds_cluster_impl_policy_->config_->cluster_name(),
561 xds_cluster_impl_policy_->config_->eds_service_name(),
562 std::move(locality_name));
563 return MakeRefCounted<StatsSubchannelWrapper>(
564 xds_cluster_impl_policy_->channel_control_helper()->CreateSubchannel(
565 std::move(address), args),
566 std::move(locality_stats));
567 }
568 // Load reporting not enabled, so don't wrap the subchannel.
569 return xds_cluster_impl_policy_->channel_control_helper()->CreateSubchannel(
570 std::move(address), args);
571 }
572
UpdateState(grpc_connectivity_state state,const absl::Status & status,std::unique_ptr<SubchannelPicker> picker)573 void XdsClusterImplLb::Helper::UpdateState(
574 grpc_connectivity_state state, const absl::Status& status,
575 std::unique_ptr<SubchannelPicker> picker) {
576 if (xds_cluster_impl_policy_->shutting_down_) return;
577 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_cluster_impl_lb_trace)) {
578 gpr_log(GPR_INFO,
579 "[xds_cluster_impl_lb %p] child connectivity state update: "
580 "state=%s (%s) "
581 "picker=%p",
582 xds_cluster_impl_policy_.get(), ConnectivityStateName(state),
583 status.ToString().c_str(), picker.get());
584 }
585 // Save the state and picker.
586 xds_cluster_impl_policy_->state_ = state;
587 xds_cluster_impl_policy_->status_ = status;
588 xds_cluster_impl_policy_->picker_ =
589 MakeRefCounted<RefCountedPicker>(std::move(picker));
590 // Wrap the picker and return it to the channel.
591 xds_cluster_impl_policy_->MaybeUpdatePickerLocked();
592 }
593
RequestReresolution()594 void XdsClusterImplLb::Helper::RequestReresolution() {
595 if (xds_cluster_impl_policy_->shutting_down_) return;
596 xds_cluster_impl_policy_->channel_control_helper()->RequestReresolution();
597 }
598
AddTraceEvent(TraceSeverity severity,absl::string_view message)599 void XdsClusterImplLb::Helper::AddTraceEvent(TraceSeverity severity,
600 absl::string_view message) {
601 if (xds_cluster_impl_policy_->shutting_down_) return;
602 xds_cluster_impl_policy_->channel_control_helper()->AddTraceEvent(severity,
603 message);
604 }
605
606 //
607 // factory
608 //
609
610 class XdsClusterImplLbFactory : public LoadBalancingPolicyFactory {
611 public:
CreateLoadBalancingPolicy(LoadBalancingPolicy::Args args) const612 OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
613 LoadBalancingPolicy::Args args) const override {
614 grpc_error* error = GRPC_ERROR_NONE;
615 RefCountedPtr<XdsClient> xds_client = XdsClient::GetOrCreate(&error);
616 if (error != GRPC_ERROR_NONE) {
617 gpr_log(
618 GPR_ERROR,
619 "cannot get XdsClient to instantiate xds_cluster_impl LB policy: %s",
620 grpc_error_string(error));
621 GRPC_ERROR_UNREF(error);
622 return nullptr;
623 }
624 return MakeOrphanable<XdsClusterImplLb>(std::move(xds_client),
625 std::move(args));
626 }
627
name() const628 const char* name() const override { return kXdsClusterImpl; }
629
ParseLoadBalancingConfig(const Json & json,grpc_error ** error) const630 RefCountedPtr<LoadBalancingPolicy::Config> ParseLoadBalancingConfig(
631 const Json& json, grpc_error** error) const override {
632 GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
633 if (json.type() == Json::Type::JSON_NULL) {
634 // This policy was configured in the deprecated loadBalancingPolicy
635 // field or in the client API.
636 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
637 "field:loadBalancingPolicy error:xds_cluster_impl policy requires "
638 "configuration. Please use loadBalancingConfig field of service "
639 "config instead.");
640 return nullptr;
641 }
642 std::vector<grpc_error*> error_list;
643 // Child policy.
644 RefCountedPtr<LoadBalancingPolicy::Config> child_policy;
645 auto it = json.object_value().find("childPolicy");
646 if (it == json.object_value().end()) {
647 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
648 "field:childPolicy error:required field missing"));
649 } else {
650 grpc_error* parse_error = GRPC_ERROR_NONE;
651 child_policy = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
652 it->second, &parse_error);
653 if (child_policy == nullptr) {
654 GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
655 std::vector<grpc_error*> child_errors;
656 child_errors.push_back(parse_error);
657 error_list.push_back(
658 GRPC_ERROR_CREATE_FROM_VECTOR("field:childPolicy", &child_errors));
659 }
660 }
661 // Cluster name.
662 std::string cluster_name;
663 it = json.object_value().find("clusterName");
664 if (it == json.object_value().end()) {
665 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
666 "field:clusterName error:required field missing"));
667 } else if (it->second.type() != Json::Type::STRING) {
668 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
669 "field:clusterName error:type should be string"));
670 } else {
671 cluster_name = it->second.string_value();
672 }
673 // EDS service name.
674 std::string eds_service_name;
675 it = json.object_value().find("edsServiceName");
676 if (it != json.object_value().end()) {
677 if (it->second.type() != Json::Type::STRING) {
678 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
679 "field:edsServiceName error:type should be string"));
680 } else {
681 eds_service_name = it->second.string_value();
682 }
683 }
684 // LRS load reporting server name.
685 absl::optional<std::string> lrs_load_reporting_server_name;
686 it = json.object_value().find("lrsLoadReportingServerName");
687 if (it != json.object_value().end()) {
688 if (it->second.type() != Json::Type::STRING) {
689 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
690 "field:lrsLoadReportingServerName error:type should be string"));
691 } else {
692 lrs_load_reporting_server_name = it->second.string_value();
693 }
694 }
695 // Max concurrent requests.
696 uint32_t max_concurrent_requests = 1024;
697 it = json.object_value().find("maxConcurrentRequests");
698 if (it != json.object_value().end()) {
699 if (it->second.type() != Json::Type::NUMBER) {
700 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
701 "field:max_concurrent_requests error:must be of type number"));
702 } else {
703 max_concurrent_requests =
704 gpr_parse_nonnegative_int(it->second.string_value().c_str());
705 }
706 }
707 // Drop config.
708 auto drop_config = MakeRefCounted<XdsApi::EdsUpdate::DropConfig>();
709 it = json.object_value().find("dropCategories");
710 if (it == json.object_value().end()) {
711 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
712 "field:dropCategories error:required field missing"));
713 } else {
714 std::vector<grpc_error*> child_errors =
715 ParseDropCategories(it->second, drop_config.get());
716 if (!child_errors.empty()) {
717 error_list.push_back(GRPC_ERROR_CREATE_FROM_VECTOR(
718 "field:dropCategories", &child_errors));
719 }
720 }
721 if (!error_list.empty()) {
722 *error = GRPC_ERROR_CREATE_FROM_VECTOR(
723 "xds_cluster_impl_experimental LB policy config", &error_list);
724 return nullptr;
725 }
726 return MakeRefCounted<XdsClusterImplLbConfig>(
727 std::move(child_policy), std::move(cluster_name),
728 std::move(eds_service_name), std::move(lrs_load_reporting_server_name),
729 max_concurrent_requests, std::move(drop_config));
730 }
731
732 private:
ParseDropCategories(const Json & json,XdsApi::EdsUpdate::DropConfig * drop_config)733 static std::vector<grpc_error*> ParseDropCategories(
734 const Json& json, XdsApi::EdsUpdate::DropConfig* drop_config) {
735 std::vector<grpc_error*> error_list;
736 if (json.type() != Json::Type::ARRAY) {
737 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
738 "dropCategories field is not an array"));
739 return error_list;
740 }
741 for (size_t i = 0; i < json.array_value().size(); ++i) {
742 const Json& entry = json.array_value()[i];
743 std::vector<grpc_error*> child_errors =
744 ParseDropCategory(entry, drop_config);
745 if (!child_errors.empty()) {
746 grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
747 absl::StrCat("errors parsing index ", i).c_str());
748 for (size_t i = 0; i < child_errors.size(); ++i) {
749 error = grpc_error_add_child(error, child_errors[i]);
750 }
751 error_list.push_back(error);
752 }
753 }
754 return error_list;
755 }
756
ParseDropCategory(const Json & json,XdsApi::EdsUpdate::DropConfig * drop_config)757 static std::vector<grpc_error*> ParseDropCategory(
758 const Json& json, XdsApi::EdsUpdate::DropConfig* drop_config) {
759 std::vector<grpc_error*> error_list;
760 if (json.type() != Json::Type::OBJECT) {
761 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
762 "dropCategories entry is not an object"));
763 return error_list;
764 }
765 std::string category;
766 auto it = json.object_value().find("category");
767 if (it == json.object_value().end()) {
768 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
769 "\"category\" field not present"));
770 } else if (it->second.type() != Json::Type::STRING) {
771 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
772 "\"category\" field is not a string"));
773 } else {
774 category = it->second.string_value();
775 }
776 uint32_t requests_per_million = 0;
777 it = json.object_value().find("requests_per_million");
778 if (it == json.object_value().end()) {
779 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
780 "\"requests_per_million\" field is not present"));
781 } else if (it->second.type() != Json::Type::NUMBER) {
782 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
783 "\"requests_per_million\" field is not a number"));
784 } else {
785 requests_per_million =
786 gpr_parse_nonnegative_int(it->second.string_value().c_str());
787 }
788 if (error_list.empty()) {
789 drop_config->AddCategory(std::move(category), requests_per_million);
790 }
791 return error_list;
792 }
793 };
794
795 } // namespace
796
797 } // namespace grpc_core
798
799 //
800 // Plugin registration
801 //
802
grpc_lb_policy_xds_cluster_impl_init()803 void grpc_lb_policy_xds_cluster_impl_init() {
804 grpc_core::g_call_counter_map = new grpc_core::CircuitBreakerCallCounterMap();
805 grpc_core::LoadBalancingPolicyRegistry::Builder::
806 RegisterLoadBalancingPolicyFactory(
807 absl::make_unique<grpc_core::XdsClusterImplLbFactory>());
808 }
809
grpc_lb_policy_xds_cluster_impl_shutdown()810 void grpc_lb_policy_xds_cluster_impl_shutdown() {
811 delete grpc_core::g_call_counter_map;
812 }
813