|  | @@ -32,6 +32,7 @@
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/client_channel.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/lb_policy.h"
 | 
	
		
			
				|  |  | +#include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/lb_policy_factory.h"
 | 
	
		
			
				|  |  |  #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
 | 
	
	
		
			
				|  | @@ -198,14 +199,8 @@ class XdsLb : public LoadBalancingPolicy {
 | 
	
		
			
				|  |  |      void RequestReresolution() override;
 | 
	
		
			
				|  |  |      void AddTraceEvent(TraceSeverity severity, StringView message) override;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    void set_child(LoadBalancingPolicy* child) { child_ = child; }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |     private:
 | 
	
		
			
				|  |  | -    bool CalledByPendingFallback() const;
 | 
	
		
			
				|  |  | -    bool CalledByCurrentFallback() const;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      RefCountedPtr<XdsLb> parent_;
 | 
	
		
			
				|  |  | -    LoadBalancingPolicy* child_ = nullptr;
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    // Each LocalityMap holds a ref to the XdsLb.
 | 
	
	
		
			
				|  | @@ -262,19 +257,14 @@ class XdsLb : public LoadBalancingPolicy {
 | 
	
		
			
				|  |  |          // client, which is a watch-based API.
 | 
	
		
			
				|  |  |          void RequestReresolution() override {}
 | 
	
		
			
				|  |  |          void AddTraceEvent(TraceSeverity severity, StringView message) override;
 | 
	
		
			
				|  |  | -        void set_child(LoadBalancingPolicy* child) { child_ = child; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |         private:
 | 
	
		
			
				|  |  | -        bool CalledByPendingChild() const;
 | 
	
		
			
				|  |  | -        bool CalledByCurrentChild() const;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          RefCountedPtr<Locality> locality_;
 | 
	
		
			
				|  |  | -        LoadBalancingPolicy* child_ = nullptr;
 | 
	
		
			
				|  |  |        };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |        // Methods for dealing with the child policy.
 | 
	
		
			
				|  |  |        OrphanablePtr<LoadBalancingPolicy> CreateChildPolicyLocked(
 | 
	
		
			
				|  |  | -          const char* name, const grpc_channel_args* args);
 | 
	
		
			
				|  |  | +          const grpc_channel_args* args);
 | 
	
		
			
				|  |  |        grpc_channel_args* CreateChildPolicyArgsLocked(
 | 
	
		
			
				|  |  |            const grpc_channel_args* args);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -291,7 +281,6 @@ class XdsLb : public LoadBalancingPolicy {
 | 
	
		
			
				|  |  |        RefCountedPtr<XdsLocalityName> name_;
 | 
	
		
			
				|  |  |        RefCountedPtr<XdsClusterLocalityStats> stats_;
 | 
	
		
			
				|  |  |        OrphanablePtr<LoadBalancingPolicy> child_policy_;
 | 
	
		
			
				|  |  | -      OrphanablePtr<LoadBalancingPolicy> pending_child_policy_;
 | 
	
		
			
				|  |  |        RefCountedPtr<RefCountedEndpointPicker> picker_wrapper_;
 | 
	
		
			
				|  |  |        RefCountedPtr<LoadReportingPicker> load_reporting_picker_;
 | 
	
		
			
				|  |  |        grpc_connectivity_state connectivity_state_ = GRPC_CHANNEL_IDLE;
 | 
	
	
		
			
				|  | @@ -403,7 +392,7 @@ class XdsLb : public LoadBalancingPolicy {
 | 
	
		
			
				|  |  |    static void OnFallbackTimerLocked(void* arg, grpc_error* error);
 | 
	
		
			
				|  |  |    void UpdateFallbackPolicyLocked();
 | 
	
		
			
				|  |  |    OrphanablePtr<LoadBalancingPolicy> CreateFallbackPolicyLocked(
 | 
	
		
			
				|  |  | -      const char* name, const grpc_channel_args* args);
 | 
	
		
			
				|  |  | +      const grpc_channel_args* args);
 | 
	
		
			
				|  |  |    void MaybeExitFallbackMode();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    // Server name from target URI.
 | 
	
	
		
			
				|  | @@ -445,7 +434,6 @@ class XdsLb : public LoadBalancingPolicy {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    // Non-null iff we are in fallback mode.
 | 
	
		
			
				|  |  |    OrphanablePtr<LoadBalancingPolicy> fallback_policy_;
 | 
	
		
			
				|  |  | -  OrphanablePtr<LoadBalancingPolicy> pending_fallback_policy_;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const grpc_millis locality_retention_interval_ms_;
 | 
	
		
			
				|  |  |    const grpc_millis locality_map_failover_timeout_ms_;
 | 
	
	
		
			
				|  | @@ -539,71 +527,26 @@ XdsLb::PickResult XdsLb::LocalityPicker::PickFromLocality(const uint32_t key,
 | 
	
		
			
				|  |  |  // XdsLb::FallbackHelper
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -bool XdsLb::FallbackHelper::CalledByPendingFallback() const {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(child_ != nullptr);
 | 
	
		
			
				|  |  | -  return child_ == parent_->pending_fallback_policy_.get();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -bool XdsLb::FallbackHelper::CalledByCurrentFallback() const {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(child_ != nullptr);
 | 
	
		
			
				|  |  | -  return child_ == parent_->fallback_policy_.get();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  RefCountedPtr<SubchannelInterface> XdsLb::FallbackHelper::CreateSubchannel(
 | 
	
		
			
				|  |  |      const grpc_channel_args& args) {
 | 
	
		
			
				|  |  | -  if (parent_->shutting_down_ ||
 | 
	
		
			
				|  |  | -      (!CalledByPendingFallback() && !CalledByCurrentFallback())) {
 | 
	
		
			
				|  |  | -    return nullptr;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  if (parent_->shutting_down_) return nullptr;
 | 
	
		
			
				|  |  |    return parent_->channel_control_helper()->CreateSubchannel(args);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsLb::FallbackHelper::UpdateState(
 | 
	
		
			
				|  |  |      grpc_connectivity_state state, std::unique_ptr<SubchannelPicker> picker) {
 | 
	
		
			
				|  |  |    if (parent_->shutting_down_) return;
 | 
	
		
			
				|  |  | -  // If this request is from the pending fallback policy, ignore it until
 | 
	
		
			
				|  |  | -  // it reports READY, at which point we swap it into place.
 | 
	
		
			
				|  |  | -  if (CalledByPendingFallback()) {
 | 
	
		
			
				|  |  | -    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
 | 
	
		
			
				|  |  | -      gpr_log(
 | 
	
		
			
				|  |  | -          GPR_INFO,
 | 
	
		
			
				|  |  | -          "[xdslb %p helper %p] pending fallback policy %p reports state=%s",
 | 
	
		
			
				|  |  | -          parent_.get(), this, parent_->pending_fallback_policy_.get(),
 | 
	
		
			
				|  |  | -          ConnectivityStateName(state));
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (state != GRPC_CHANNEL_READY) return;
 | 
	
		
			
				|  |  | -    grpc_pollset_set_del_pollset_set(
 | 
	
		
			
				|  |  | -        parent_->fallback_policy_->interested_parties(),
 | 
	
		
			
				|  |  | -        parent_->interested_parties());
 | 
	
		
			
				|  |  | -    parent_->fallback_policy_ = std::move(parent_->pending_fallback_policy_);
 | 
	
		
			
				|  |  | -  } else if (!CalledByCurrentFallback()) {
 | 
	
		
			
				|  |  | -    // This request is from an outdated fallback policy, so ignore it.
 | 
	
		
			
				|  |  | -    return;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |    parent_->channel_control_helper()->UpdateState(state, std::move(picker));
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsLb::FallbackHelper::RequestReresolution() {
 | 
	
		
			
				|  |  |    if (parent_->shutting_down_) return;
 | 
	
		
			
				|  |  | -  const LoadBalancingPolicy* latest_fallback_policy =
 | 
	
		
			
				|  |  | -      parent_->pending_fallback_policy_ != nullptr
 | 
	
		
			
				|  |  | -          ? parent_->pending_fallback_policy_.get()
 | 
	
		
			
				|  |  | -          : parent_->fallback_policy_.get();
 | 
	
		
			
				|  |  | -  if (child_ != latest_fallback_policy) return;
 | 
	
		
			
				|  |  | -  if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
 | 
	
		
			
				|  |  | -    gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | -            "[xdslb %p] Re-resolution requested from the fallback policy (%p).",
 | 
	
		
			
				|  |  | -            parent_.get(), child_);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |    parent_->channel_control_helper()->RequestReresolution();
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsLb::FallbackHelper::AddTraceEvent(TraceSeverity severity,
 | 
	
		
			
				|  |  |                                            StringView message) {
 | 
	
		
			
				|  |  | -  if (parent_->shutting_down_ ||
 | 
	
		
			
				|  |  | -      (!CalledByPendingFallback() && !CalledByCurrentFallback())) {
 | 
	
		
			
				|  |  | -    return;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  if (parent_->shutting_down_) return;
 | 
	
		
			
				|  |  |    parent_->channel_control_helper()->AddTraceEvent(severity, message);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -737,13 +680,8 @@ void XdsLb::ShutdownLocked() {
 | 
	
		
			
				|  |  |    if (fallback_policy_ != nullptr) {
 | 
	
		
			
				|  |  |      grpc_pollset_set_del_pollset_set(fallback_policy_->interested_parties(),
 | 
	
		
			
				|  |  |                                       interested_parties());
 | 
	
		
			
				|  |  | +    fallback_policy_.reset();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  if (pending_fallback_policy_ != nullptr) {
 | 
	
		
			
				|  |  | -    grpc_pollset_set_del_pollset_set(
 | 
	
		
			
				|  |  | -        pending_fallback_policy_->interested_parties(), interested_parties());
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  fallback_policy_.reset();
 | 
	
		
			
				|  |  | -  pending_fallback_policy_.reset();
 | 
	
		
			
				|  |  |    // Cancel the endpoint watch here instead of in our dtor if we are using the
 | 
	
		
			
				|  |  |    // XdsResolver, because the watcher holds a ref to us and we might not be
 | 
	
		
			
				|  |  |    // destroying the Xds client leading to a situation where the Xds lb policy is
 | 
	
	
		
			
				|  | @@ -771,9 +709,6 @@ void XdsLb::ResetBackoffLocked() {
 | 
	
		
			
				|  |  |    if (fallback_policy_ != nullptr) {
 | 
	
		
			
				|  |  |      fallback_policy_->ResetBackoffLocked();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  if (pending_fallback_policy_ != nullptr) {
 | 
	
		
			
				|  |  | -    pending_fallback_policy_->ResetBackoffLocked();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsLb::UpdateLocked(UpdateArgs args) {
 | 
	
	
		
			
				|  | @@ -896,127 +831,37 @@ void XdsLb::OnFallbackTimerLocked(void* arg, grpc_error* error) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsLb::UpdateFallbackPolicyLocked() {
 | 
	
		
			
				|  |  |    if (shutting_down_) return;
 | 
	
		
			
				|  |  | -  // Construct update args.
 | 
	
		
			
				|  |  | +  // Create policy if needed.
 | 
	
		
			
				|  |  | +  if (fallback_policy_ == nullptr) {
 | 
	
		
			
				|  |  | +    fallback_policy_ = CreateFallbackPolicyLocked(args_);
 | 
	
		
			
				|  |  | +    GPR_ASSERT(fallback_policy_ != nullptr);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // Perform update.
 | 
	
		
			
				|  |  |    UpdateArgs update_args;
 | 
	
		
			
				|  |  |    update_args.addresses = fallback_backend_addresses_;
 | 
	
		
			
				|  |  |    update_args.config = config_->fallback_policy();
 | 
	
		
			
				|  |  |    update_args.args = grpc_channel_args_copy(args_);
 | 
	
		
			
				|  |  | -  // If the child policy name changes, we need to create a new child
 | 
	
		
			
				|  |  | -  // policy.  When this happens, we leave child_policy_ as-is and store
 | 
	
		
			
				|  |  | -  // the new child policy in pending_child_policy_.  Once the new child
 | 
	
		
			
				|  |  | -  // policy transitions into state READY, we swap it into child_policy_,
 | 
	
		
			
				|  |  | -  // replacing the original child policy.  So pending_child_policy_ is
 | 
	
		
			
				|  |  | -  // non-null only between when we apply an update that changes the child
 | 
	
		
			
				|  |  | -  // policy name and when the new child reports state READY.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // Updates can arrive at any point during this transition.  We always
 | 
	
		
			
				|  |  | -  // apply updates relative to the most recently created child policy,
 | 
	
		
			
				|  |  | -  // even if the most recent one is still in pending_child_policy_.  This
 | 
	
		
			
				|  |  | -  // is true both when applying the updates to an existing child policy
 | 
	
		
			
				|  |  | -  // and when determining whether we need to create a new policy.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // As a result of this, there are several cases to consider here:
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // 1. We have no existing child policy (i.e., we have started up but
 | 
	
		
			
				|  |  | -  //    have not yet received a serverlist from the balancer or gone
 | 
	
		
			
				|  |  | -  //    into fallback mode; in this case, both child_policy_ and
 | 
	
		
			
				|  |  | -  //    pending_child_policy_ are null).  In this case, we create a
 | 
	
		
			
				|  |  | -  //    new child policy and store it in child_policy_.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // 2. We have an existing child policy and have no pending child policy
 | 
	
		
			
				|  |  | -  //    from a previous update (i.e., either there has not been a
 | 
	
		
			
				|  |  | -  //    previous update that changed the policy name, or we have already
 | 
	
		
			
				|  |  | -  //    finished swapping in the new policy; in this case, child_policy_
 | 
	
		
			
				|  |  | -  //    is non-null but pending_child_policy_ is null).  In this case:
 | 
	
		
			
				|  |  | -  //    a. If child_policy_->name() equals child_policy_name, then we
 | 
	
		
			
				|  |  | -  //       update the existing child policy.
 | 
	
		
			
				|  |  | -  //    b. If child_policy_->name() does not equal child_policy_name,
 | 
	
		
			
				|  |  | -  //       we create a new policy.  The policy will be stored in
 | 
	
		
			
				|  |  | -  //       pending_child_policy_ and will later be swapped into
 | 
	
		
			
				|  |  | -  //       child_policy_ by the helper when the new child transitions
 | 
	
		
			
				|  |  | -  //       into state READY.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // 3. We have an existing child policy and have a pending child policy
 | 
	
		
			
				|  |  | -  //    from a previous update (i.e., a previous update set
 | 
	
		
			
				|  |  | -  //    pending_child_policy_ as per case 2b above and that policy has
 | 
	
		
			
				|  |  | -  //    not yet transitioned into state READY and been swapped into
 | 
	
		
			
				|  |  | -  //    child_policy_; in this case, both child_policy_ and
 | 
	
		
			
				|  |  | -  //    pending_child_policy_ are non-null).  In this case:
 | 
	
		
			
				|  |  | -  //    a. If pending_child_policy_->name() equals child_policy_name,
 | 
	
		
			
				|  |  | -  //       then we update the existing pending child policy.
 | 
	
		
			
				|  |  | -  //    b. If pending_child_policy->name() does not equal
 | 
	
		
			
				|  |  | -  //       child_policy_name, then we create a new policy.  The new
 | 
	
		
			
				|  |  | -  //       policy is stored in pending_child_policy_ (replacing the one
 | 
	
		
			
				|  |  | -  //       that was there before, which will be immediately shut down)
 | 
	
		
			
				|  |  | -  //       and will later be swapped into child_policy_ by the helper
 | 
	
		
			
				|  |  | -  //       when the new child transitions into state READY.
 | 
	
		
			
				|  |  | -  const char* fallback_policy_name = update_args.config == nullptr
 | 
	
		
			
				|  |  | -                                         ? "round_robin"
 | 
	
		
			
				|  |  | -                                         : update_args.config->name();
 | 
	
		
			
				|  |  | -  const bool create_policy =
 | 
	
		
			
				|  |  | -      // case 1
 | 
	
		
			
				|  |  | -      fallback_policy_ == nullptr ||
 | 
	
		
			
				|  |  | -      // case 2b
 | 
	
		
			
				|  |  | -      (pending_fallback_policy_ == nullptr &&
 | 
	
		
			
				|  |  | -       strcmp(fallback_policy_->name(), fallback_policy_name) != 0) ||
 | 
	
		
			
				|  |  | -      // case 3b
 | 
	
		
			
				|  |  | -      (pending_fallback_policy_ != nullptr &&
 | 
	
		
			
				|  |  | -       strcmp(pending_fallback_policy_->name(), fallback_policy_name) != 0);
 | 
	
		
			
				|  |  | -  LoadBalancingPolicy* policy_to_update = nullptr;
 | 
	
		
			
				|  |  | -  if (create_policy) {
 | 
	
		
			
				|  |  | -    // Cases 1, 2b, and 3b: create a new child policy.
 | 
	
		
			
				|  |  | -    // If child_policy_ is null, we set it (case 1), else we set
 | 
	
		
			
				|  |  | -    // pending_child_policy_ (cases 2b and 3b).
 | 
	
		
			
				|  |  | -    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
 | 
	
		
			
				|  |  | -      gpr_log(GPR_INFO, "[xdslb %p] Creating new %sfallback policy %s", this,
 | 
	
		
			
				|  |  | -              fallback_policy_ == nullptr ? "" : "pending ",
 | 
	
		
			
				|  |  | -              fallback_policy_name);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    auto& lb_policy = fallback_policy_ == nullptr ? fallback_policy_
 | 
	
		
			
				|  |  | -                                                  : pending_fallback_policy_;
 | 
	
		
			
				|  |  | -    lb_policy =
 | 
	
		
			
				|  |  | -        CreateFallbackPolicyLocked(fallback_policy_name, update_args.args);
 | 
	
		
			
				|  |  | -    policy_to_update = lb_policy.get();
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    // Cases 2a and 3a: update an existing policy.
 | 
	
		
			
				|  |  | -    // If we have a pending child policy, send the update to the pending
 | 
	
		
			
				|  |  | -    // policy (case 3a), else send it to the current policy (case 2a).
 | 
	
		
			
				|  |  | -    policy_to_update = pending_fallback_policy_ != nullptr
 | 
	
		
			
				|  |  | -                           ? pending_fallback_policy_.get()
 | 
	
		
			
				|  |  | -                           : fallback_policy_.get();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  GPR_ASSERT(policy_to_update != nullptr);
 | 
	
		
			
				|  |  | -  // Update the policy.
 | 
	
		
			
				|  |  |    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
 | 
	
		
			
				|  |  | -    gpr_log(
 | 
	
		
			
				|  |  | -        GPR_INFO, "[xdslb %p] Updating %sfallback policy %p", this,
 | 
	
		
			
				|  |  | -        policy_to_update == pending_fallback_policy_.get() ? "pending " : "",
 | 
	
		
			
				|  |  | -        policy_to_update);
 | 
	
		
			
				|  |  | +    gpr_log(GPR_INFO, "[xdslb %p] Updating fallback child policy handler %p",
 | 
	
		
			
				|  |  | +            this, fallback_policy_.get());
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  policy_to_update->UpdateLocked(std::move(update_args));
 | 
	
		
			
				|  |  | +  fallback_policy_->UpdateLocked(std::move(update_args));
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  OrphanablePtr<LoadBalancingPolicy> XdsLb::CreateFallbackPolicyLocked(
 | 
	
		
			
				|  |  | -    const char* name, const grpc_channel_args* args) {
 | 
	
		
			
				|  |  | -  FallbackHelper* helper =
 | 
	
		
			
				|  |  | -      new FallbackHelper(Ref(DEBUG_LOCATION, "FallbackHelper"));
 | 
	
		
			
				|  |  | +    const grpc_channel_args* args) {
 | 
	
		
			
				|  |  |    LoadBalancingPolicy::Args lb_policy_args;
 | 
	
		
			
				|  |  |    lb_policy_args.combiner = combiner();
 | 
	
		
			
				|  |  |    lb_policy_args.args = args;
 | 
	
		
			
				|  |  |    lb_policy_args.channel_control_helper =
 | 
	
		
			
				|  |  | -      std::unique_ptr<ChannelControlHelper>(helper);
 | 
	
		
			
				|  |  | +      absl::make_unique<FallbackHelper>(Ref(DEBUG_LOCATION, "FallbackHelper"));
 | 
	
		
			
				|  |  |    OrphanablePtr<LoadBalancingPolicy> lb_policy =
 | 
	
		
			
				|  |  | -      LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
 | 
	
		
			
				|  |  | -          name, std::move(lb_policy_args));
 | 
	
		
			
				|  |  | -  if (GPR_UNLIKELY(lb_policy == nullptr)) {
 | 
	
		
			
				|  |  | -    gpr_log(GPR_ERROR, "[xdslb %p] Failure creating fallback policy %s", this,
 | 
	
		
			
				|  |  | -            name);
 | 
	
		
			
				|  |  | -    return nullptr;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  helper->set_child(lb_policy.get());
 | 
	
		
			
				|  |  | +      MakeOrphanable<ChildPolicyHandler>(std::move(lb_policy_args),
 | 
	
		
			
				|  |  | +                                         &grpc_lb_xds_trace);
 | 
	
		
			
				|  |  |    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
 | 
	
		
			
				|  |  | -    gpr_log(GPR_INFO, "[xdslb %p] Created new fallback policy %s (%p)", this,
 | 
	
		
			
				|  |  | -            name, lb_policy.get());
 | 
	
		
			
				|  |  | +    gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +            "[xdslb %p] Created new fallback child policy handler (%p)", this,
 | 
	
		
			
				|  |  | +            lb_policy.get());
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    // Add the xDS's interested_parties pollset_set to that of the newly created
 | 
	
		
			
				|  |  |    // child policy. This will make the child policy progress upon activity on xDS
 | 
	
	
		
			
				|  | @@ -1030,7 +875,6 @@ void XdsLb::MaybeExitFallbackMode() {
 | 
	
		
			
				|  |  |    if (fallback_policy_ == nullptr) return;
 | 
	
		
			
				|  |  |    gpr_log(GPR_INFO, "[xdslb %p] Exiting fallback mode", this);
 | 
	
		
			
				|  |  |    fallback_policy_.reset();
 | 
	
		
			
				|  |  | -  pending_fallback_policy_.reset();
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  //
 | 
	
	
		
			
				|  | @@ -1513,27 +1357,19 @@ grpc_channel_args* XdsLb::LocalityMap::Locality::CreateChildPolicyArgsLocked(
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  OrphanablePtr<LoadBalancingPolicy>
 | 
	
		
			
				|  |  |  XdsLb::LocalityMap::Locality::CreateChildPolicyLocked(
 | 
	
		
			
				|  |  | -    const char* name, const grpc_channel_args* args) {
 | 
	
		
			
				|  |  | -  Helper* helper = new Helper(this->Ref(DEBUG_LOCATION, "Helper"));
 | 
	
		
			
				|  |  | +    const grpc_channel_args* args) {
 | 
	
		
			
				|  |  |    LoadBalancingPolicy::Args lb_policy_args;
 | 
	
		
			
				|  |  |    lb_policy_args.combiner = xds_policy()->combiner();
 | 
	
		
			
				|  |  |    lb_policy_args.args = args;
 | 
	
		
			
				|  |  |    lb_policy_args.channel_control_helper =
 | 
	
		
			
				|  |  | -      std::unique_ptr<ChannelControlHelper>(helper);
 | 
	
		
			
				|  |  | +      absl::make_unique<Helper>(this->Ref(DEBUG_LOCATION, "Helper"));
 | 
	
		
			
				|  |  |    OrphanablePtr<LoadBalancingPolicy> lb_policy =
 | 
	
		
			
				|  |  | -      LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
 | 
	
		
			
				|  |  | -          name, std::move(lb_policy_args));
 | 
	
		
			
				|  |  | -  if (GPR_UNLIKELY(lb_policy == nullptr)) {
 | 
	
		
			
				|  |  | -    gpr_log(GPR_ERROR,
 | 
	
		
			
				|  |  | -            "[xdslb %p] Locality %p %s: failure creating child policy %s",
 | 
	
		
			
				|  |  | -            xds_policy(), this, name_->AsHumanReadableString(), name);
 | 
	
		
			
				|  |  | -    return nullptr;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  helper->set_child(lb_policy.get());
 | 
	
		
			
				|  |  | +      MakeOrphanable<ChildPolicyHandler>(std::move(lb_policy_args),
 | 
	
		
			
				|  |  | +                                         &grpc_lb_xds_trace);
 | 
	
		
			
				|  |  |    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
 | 
	
		
			
				|  |  |      gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | -            "[xdslb %p] Locality %p %s: Created new child policy %s (%p)",
 | 
	
		
			
				|  |  | -            xds_policy(), this, name_->AsHumanReadableString(), name,
 | 
	
		
			
				|  |  | +            "[xdslb %p] Locality %p %s: Created new child policy handler (%p)",
 | 
	
		
			
				|  |  | +            xds_policy(), this, name_->AsHumanReadableString(),
 | 
	
		
			
				|  |  |              lb_policy.get());
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    // Add the xDS's interested_parties pollset_set to that of the newly created
 | 
	
	
		
			
				|  | @@ -1560,101 +1396,19 @@ void XdsLb::LocalityMap::Locality::UpdateLocked(uint32_t locality_weight,
 | 
	
		
			
				|  |  |    update_args.addresses = std::move(serverlist);
 | 
	
		
			
				|  |  |    update_args.config = xds_policy()->config_->child_policy();
 | 
	
		
			
				|  |  |    update_args.args = CreateChildPolicyArgsLocked(xds_policy()->args_);
 | 
	
		
			
				|  |  | -  // If the child policy name changes, we need to create a new child
 | 
	
		
			
				|  |  | -  // policy.  When this happens, we leave child_policy_ as-is and store
 | 
	
		
			
				|  |  | -  // the new child policy in pending_child_policy_.  Once the new child
 | 
	
		
			
				|  |  | -  // policy transitions into state READY, we swap it into child_policy_,
 | 
	
		
			
				|  |  | -  // replacing the original child policy.  So pending_child_policy_ is
 | 
	
		
			
				|  |  | -  // non-null only between when we apply an update that changes the child
 | 
	
		
			
				|  |  | -  // policy name and when the new child reports state READY.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // Updates can arrive at any point during this transition.  We always
 | 
	
		
			
				|  |  | -  // apply updates relative to the most recently created child policy,
 | 
	
		
			
				|  |  | -  // even if the most recent one is still in pending_child_policy_.  This
 | 
	
		
			
				|  |  | -  // is true both when applying the updates to an existing child policy
 | 
	
		
			
				|  |  | -  // and when determining whether we need to create a new policy.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // As a result of this, there are several cases to consider here:
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // 1. We have no existing child policy (i.e., we have started up but
 | 
	
		
			
				|  |  | -  //    have not yet received a serverlist from the balancer or gone
 | 
	
		
			
				|  |  | -  //    into fallback mode; in this case, both child_policy_ and
 | 
	
		
			
				|  |  | -  //    pending_child_policy_ are null).  In this case, we create a
 | 
	
		
			
				|  |  | -  //    new child policy and store it in child_policy_.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // 2. We have an existing child policy and have no pending child policy
 | 
	
		
			
				|  |  | -  //    from a previous update (i.e., either there has not been a
 | 
	
		
			
				|  |  | -  //    previous update that changed the policy name, or we have already
 | 
	
		
			
				|  |  | -  //    finished swapping in the new policy; in this case, child_policy_
 | 
	
		
			
				|  |  | -  //    is non-null but pending_child_policy_ is null).  In this case:
 | 
	
		
			
				|  |  | -  //    a. If child_policy_->name() equals child_policy_name, then we
 | 
	
		
			
				|  |  | -  //       update the existing child policy.
 | 
	
		
			
				|  |  | -  //    b. If child_policy_->name() does not equal child_policy_name,
 | 
	
		
			
				|  |  | -  //       we create a new policy.  The policy will be stored in
 | 
	
		
			
				|  |  | -  //       pending_child_policy_ and will later be swapped into
 | 
	
		
			
				|  |  | -  //       child_policy_ by the helper when the new child transitions
 | 
	
		
			
				|  |  | -  //       into state READY.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // 3. We have an existing child policy and have a pending child policy
 | 
	
		
			
				|  |  | -  //    from a previous update (i.e., a previous update set
 | 
	
		
			
				|  |  | -  //    pending_child_policy_ as per case 2b above and that policy has
 | 
	
		
			
				|  |  | -  //    not yet transitioned into state READY and been swapped into
 | 
	
		
			
				|  |  | -  //    child_policy_; in this case, both child_policy_ and
 | 
	
		
			
				|  |  | -  //    pending_child_policy_ are non-null).  In this case:
 | 
	
		
			
				|  |  | -  //    a. If pending_child_policy_->name() equals child_policy_name,
 | 
	
		
			
				|  |  | -  //       then we update the existing pending child policy.
 | 
	
		
			
				|  |  | -  //    b. If pending_child_policy->name() does not equal
 | 
	
		
			
				|  |  | -  //       child_policy_name, then we create a new policy.  The new
 | 
	
		
			
				|  |  | -  //       policy is stored in pending_child_policy_ (replacing the one
 | 
	
		
			
				|  |  | -  //       that was there before, which will be immediately shut down)
 | 
	
		
			
				|  |  | -  //       and will later be swapped into child_policy_ by the helper
 | 
	
		
			
				|  |  | -  //       when the new child transitions into state READY.
 | 
	
		
			
				|  |  | -  // TODO(juanlishen): If the child policy is not configured via service config,
 | 
	
		
			
				|  |  | -  // use whatever algorithm is specified by the balancer.
 | 
	
		
			
				|  |  | -  const char* child_policy_name = update_args.config == nullptr
 | 
	
		
			
				|  |  | -                                      ? "round_robin"
 | 
	
		
			
				|  |  | -                                      : update_args.config->name();
 | 
	
		
			
				|  |  | -  const bool create_policy =
 | 
	
		
			
				|  |  | -      // case 1
 | 
	
		
			
				|  |  | -      child_policy_ == nullptr ||
 | 
	
		
			
				|  |  | -      // case 2b
 | 
	
		
			
				|  |  | -      (pending_child_policy_ == nullptr &&
 | 
	
		
			
				|  |  | -       strcmp(child_policy_->name(), child_policy_name) != 0) ||
 | 
	
		
			
				|  |  | -      // case 3b
 | 
	
		
			
				|  |  | -      (pending_child_policy_ != nullptr &&
 | 
	
		
			
				|  |  | -       strcmp(pending_child_policy_->name(), child_policy_name) != 0);
 | 
	
		
			
				|  |  | -  LoadBalancingPolicy* policy_to_update = nullptr;
 | 
	
		
			
				|  |  | -  if (create_policy) {
 | 
	
		
			
				|  |  | -    // Cases 1, 2b, and 3b: create a new child policy.
 | 
	
		
			
				|  |  | -    // If child_policy_ is null, we set it (case 1), else we set
 | 
	
		
			
				|  |  | -    // pending_child_policy_ (cases 2b and 3b).
 | 
	
		
			
				|  |  | -    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
 | 
	
		
			
				|  |  | -      gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | -              "[xdslb %p] Locality %p %s: Creating new %schild policy %s",
 | 
	
		
			
				|  |  | -              xds_policy(), this, name_->AsHumanReadableString(),
 | 
	
		
			
				|  |  | -              child_policy_ == nullptr ? "" : "pending ", child_policy_name);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    auto& lb_policy =
 | 
	
		
			
				|  |  | -        child_policy_ == nullptr ? child_policy_ : pending_child_policy_;
 | 
	
		
			
				|  |  | -    lb_policy = CreateChildPolicyLocked(child_policy_name, update_args.args);
 | 
	
		
			
				|  |  | -    policy_to_update = lb_policy.get();
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    // Cases 2a and 3a: update an existing policy.
 | 
	
		
			
				|  |  | -    // If we have a pending child policy, send the update to the pending
 | 
	
		
			
				|  |  | -    // policy (case 3a), else send it to the current policy (case 2a).
 | 
	
		
			
				|  |  | -    policy_to_update = pending_child_policy_ != nullptr
 | 
	
		
			
				|  |  | -                           ? pending_child_policy_.get()
 | 
	
		
			
				|  |  | -                           : child_policy_.get();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  GPR_ASSERT(policy_to_update != nullptr);
 | 
	
		
			
				|  |  | +  // Create child policy if needed.
 | 
	
		
			
				|  |  | +  if (child_policy_ == nullptr) {
 | 
	
		
			
				|  |  | +    child_policy_ = CreateChildPolicyLocked(update_args.args);
 | 
	
		
			
				|  |  | +    GPR_ASSERT(child_policy_ != nullptr);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    // Update the policy.
 | 
	
		
			
				|  |  |    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
 | 
	
		
			
				|  |  | -    gpr_log(GPR_INFO, "[xdslb %p] Locality %p %s: Updating %schild policy %p",
 | 
	
		
			
				|  |  | +    gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +            "[xdslb %p] Locality %p %s: Updating child policy handler %p",
 | 
	
		
			
				|  |  |              xds_policy(), this, name_->AsHumanReadableString(),
 | 
	
		
			
				|  |  | -            policy_to_update == pending_child_policy_.get() ? "pending " : "",
 | 
	
		
			
				|  |  | -            policy_to_update);
 | 
	
		
			
				|  |  | +            child_policy_.get());
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  policy_to_update->UpdateLocked(std::move(update_args));
 | 
	
		
			
				|  |  | +  child_policy_->UpdateLocked(std::move(update_args));
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsLb::LocalityMap::Locality::ShutdownLocked() {
 | 
	
	
		
			
				|  | @@ -1668,12 +1422,6 @@ void XdsLb::LocalityMap::Locality::ShutdownLocked() {
 | 
	
		
			
				|  |  |    grpc_pollset_set_del_pollset_set(child_policy_->interested_parties(),
 | 
	
		
			
				|  |  |                                     xds_policy()->interested_parties());
 | 
	
		
			
				|  |  |    child_policy_.reset();
 | 
	
		
			
				|  |  | -  if (pending_child_policy_ != nullptr) {
 | 
	
		
			
				|  |  | -    grpc_pollset_set_del_pollset_set(
 | 
	
		
			
				|  |  | -        pending_child_policy_->interested_parties(),
 | 
	
		
			
				|  |  | -        xds_policy()->interested_parties());
 | 
	
		
			
				|  |  | -    pending_child_policy_.reset();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |    // Drop our ref to the child's picker, in case it's holding a ref to
 | 
	
		
			
				|  |  |    // the child.
 | 
	
		
			
				|  |  |    load_reporting_picker_.reset();
 | 
	
	
		
			
				|  | @@ -1686,9 +1434,6 @@ void XdsLb::LocalityMap::Locality::ShutdownLocked() {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsLb::LocalityMap::Locality::ResetBackoffLocked() {
 | 
	
		
			
				|  |  |    child_policy_->ResetBackoffLocked();
 | 
	
		
			
				|  |  | -  if (pending_child_policy_ != nullptr) {
 | 
	
		
			
				|  |  | -    pending_child_policy_->ResetBackoffLocked();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsLb::LocalityMap::Locality::Orphan() {
 | 
	
	
		
			
				|  | @@ -1736,23 +1481,10 @@ void XdsLb::LocalityMap::Locality::OnDelayedRemovalTimerLocked(
 | 
	
		
			
				|  |  |  // XdsLb::LocalityMap::Locality::Helper
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -bool XdsLb::LocalityMap::Locality::Helper::CalledByPendingChild() const {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(child_ != nullptr);
 | 
	
		
			
				|  |  | -  return child_ == locality_->pending_child_policy_.get();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -bool XdsLb::LocalityMap::Locality::Helper::CalledByCurrentChild() const {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(child_ != nullptr);
 | 
	
		
			
				|  |  | -  return child_ == locality_->child_policy_.get();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  RefCountedPtr<SubchannelInterface>
 | 
	
		
			
				|  |  |  XdsLb::LocalityMap::Locality::Helper::CreateSubchannel(
 | 
	
		
			
				|  |  |      const grpc_channel_args& args) {
 | 
	
		
			
				|  |  | -  if (locality_->xds_policy()->shutting_down_ ||
 | 
	
		
			
				|  |  | -      (!CalledByPendingChild() && !CalledByCurrentChild())) {
 | 
	
		
			
				|  |  | -    return nullptr;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  if (locality_->xds_policy()->shutting_down_) return nullptr;
 | 
	
		
			
				|  |  |    return locality_->xds_policy()->channel_control_helper()->CreateSubchannel(
 | 
	
		
			
				|  |  |        args);
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -1760,25 +1492,6 @@ XdsLb::LocalityMap::Locality::Helper::CreateSubchannel(
 | 
	
		
			
				|  |  |  void XdsLb::LocalityMap::Locality::Helper::UpdateState(
 | 
	
		
			
				|  |  |      grpc_connectivity_state state, std::unique_ptr<SubchannelPicker> picker) {
 | 
	
		
			
				|  |  |    if (locality_->xds_policy()->shutting_down_) return;
 | 
	
		
			
				|  |  | -  // If this request is from the pending child policy, ignore it until
 | 
	
		
			
				|  |  | -  // it reports READY, at which point we swap it into place.
 | 
	
		
			
				|  |  | -  if (CalledByPendingChild()) {
 | 
	
		
			
				|  |  | -    if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
 | 
	
		
			
				|  |  | -      gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | -              "[xdslb %p helper %p] pending child policy %p reports state=%s",
 | 
	
		
			
				|  |  | -              locality_->xds_policy(), this,
 | 
	
		
			
				|  |  | -              locality_->pending_child_policy_.get(),
 | 
	
		
			
				|  |  | -              ConnectivityStateName(state));
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (state != GRPC_CHANNEL_READY) return;
 | 
	
		
			
				|  |  | -    grpc_pollset_set_del_pollset_set(
 | 
	
		
			
				|  |  | -        locality_->child_policy_->interested_parties(),
 | 
	
		
			
				|  |  | -        locality_->xds_policy()->interested_parties());
 | 
	
		
			
				|  |  | -    locality_->child_policy_ = std::move(locality_->pending_child_policy_);
 | 
	
		
			
				|  |  | -  } else if (!CalledByCurrentChild()) {
 | 
	
		
			
				|  |  | -    // This request is from an outdated child, so ignore it.
 | 
	
		
			
				|  |  | -    return;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  |    // Cache the state and picker in the locality.
 | 
	
		
			
				|  |  |    locality_->connectivity_state_ = state;
 | 
	
		
			
				|  |  |    locality_->picker_wrapper_ =
 | 
	
	
		
			
				|  | @@ -1789,10 +1502,7 @@ void XdsLb::LocalityMap::Locality::Helper::UpdateState(
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsLb::LocalityMap::Locality::Helper::AddTraceEvent(TraceSeverity severity,
 | 
	
		
			
				|  |  |                                                           StringView message) {
 | 
	
		
			
				|  |  | -  if (locality_->xds_policy()->shutting_down_ ||
 | 
	
		
			
				|  |  | -      (!CalledByPendingChild() && !CalledByCurrentChild())) {
 | 
	
		
			
				|  |  | -    return;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  if (locality_->xds_policy()->shutting_down_) return;
 | 
	
		
			
				|  |  |    locality_->xds_policy()->channel_control_helper()->AddTraceEvent(severity,
 | 
	
		
			
				|  |  |                                                                     message);
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -1823,34 +1533,48 @@ class XdsFactory : public LoadBalancingPolicyFactory {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      std::vector<grpc_error*> error_list;
 | 
	
		
			
				|  |  |      // Child policy.
 | 
	
		
			
				|  |  | -    RefCountedPtr<LoadBalancingPolicy::Config> child_policy;
 | 
	
		
			
				|  |  | +    Json json_tmp;
 | 
	
		
			
				|  |  | +    const Json* child_policy_json;
 | 
	
		
			
				|  |  |      auto it = json.object_value().find("childPolicy");
 | 
	
		
			
				|  |  | -    if (it != json.object_value().end()) {
 | 
	
		
			
				|  |  | -      grpc_error* parse_error = GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  | -      child_policy = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
 | 
	
		
			
				|  |  | -          it->second, &parse_error);
 | 
	
		
			
				|  |  | -      if (child_policy == nullptr) {
 | 
	
		
			
				|  |  | -        GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | -        std::vector<grpc_error*> child_errors;
 | 
	
		
			
				|  |  | -        child_errors.push_back(parse_error);
 | 
	
		
			
				|  |  | -        error_list.push_back(
 | 
	
		
			
				|  |  | -            GRPC_ERROR_CREATE_FROM_VECTOR("field:childPolicy", &child_errors));
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +    if (it == json.object_value().end()) {
 | 
	
		
			
				|  |  | +      json_tmp = Json::Array{Json::Object{
 | 
	
		
			
				|  |  | +          {"round_robin", Json::Object()},
 | 
	
		
			
				|  |  | +      }};
 | 
	
		
			
				|  |  | +      child_policy_json = &json_tmp;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      child_policy_json = &it->second;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grpc_error* parse_error = GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  | +    RefCountedPtr<LoadBalancingPolicy::Config> child_policy =
 | 
	
		
			
				|  |  | +        LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
 | 
	
		
			
				|  |  | +            *child_policy_json, &parse_error);
 | 
	
		
			
				|  |  | +    if (child_policy == nullptr) {
 | 
	
		
			
				|  |  | +      GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | +      std::vector<grpc_error*> child_errors;
 | 
	
		
			
				|  |  | +      child_errors.push_back(parse_error);
 | 
	
		
			
				|  |  | +      error_list.push_back(
 | 
	
		
			
				|  |  | +          GRPC_ERROR_CREATE_FROM_VECTOR("field:childPolicy", &child_errors));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      // Fallback policy.
 | 
	
		
			
				|  |  | -    RefCountedPtr<LoadBalancingPolicy::Config> fallback_policy;
 | 
	
		
			
				|  |  | +    const Json* fallback_policy_json;
 | 
	
		
			
				|  |  |      it = json.object_value().find("fallbackPolicy");
 | 
	
		
			
				|  |  | -    if (it != json.object_value().end()) {
 | 
	
		
			
				|  |  | -      grpc_error* parse_error = GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  | -      fallback_policy = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
 | 
	
		
			
				|  |  | -          it->second, &parse_error);
 | 
	
		
			
				|  |  | -      if (fallback_policy == nullptr) {
 | 
	
		
			
				|  |  | -        GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | -        std::vector<grpc_error*> child_errors;
 | 
	
		
			
				|  |  | -        child_errors.push_back(parse_error);
 | 
	
		
			
				|  |  | -        error_list.push_back(GRPC_ERROR_CREATE_FROM_VECTOR(
 | 
	
		
			
				|  |  | -            "field:fallbackPolicy", &child_errors));
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +    if (it == json.object_value().end()) {
 | 
	
		
			
				|  |  | +      json_tmp = Json::Array{Json::Object{
 | 
	
		
			
				|  |  | +          {"round_robin", Json::Object()},
 | 
	
		
			
				|  |  | +      }};
 | 
	
		
			
				|  |  | +      fallback_policy_json = &json_tmp;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      fallback_policy_json = &it->second;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    RefCountedPtr<LoadBalancingPolicy::Config> fallback_policy =
 | 
	
		
			
				|  |  | +        LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
 | 
	
		
			
				|  |  | +            *fallback_policy_json, &parse_error);
 | 
	
		
			
				|  |  | +    if (fallback_policy == nullptr) {
 | 
	
		
			
				|  |  | +      GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | +      std::vector<grpc_error*> child_errors;
 | 
	
		
			
				|  |  | +      child_errors.push_back(parse_error);
 | 
	
		
			
				|  |  | +      error_list.push_back(
 | 
	
		
			
				|  |  | +          GRPC_ERROR_CREATE_FROM_VECTOR("field:fallbackPolicy", &child_errors));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      // EDS service name.
 | 
	
		
			
				|  |  |      const char* eds_service_name = nullptr;
 |