|  | @@ -128,7 +128,8 @@ class XdsClient::ChannelState::AdsCallState
 | 
	
		
			
				|  |  |    bool seen_response() const { return seen_response_; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    void Subscribe(const std::string& type_url, const std::string& name);
 | 
	
		
			
				|  |  | -  void Unsubscribe(const std::string& type_url, const std::string& name);
 | 
	
		
			
				|  |  | +  void Unsubscribe(const std::string& type_url, const std::string& name,
 | 
	
		
			
				|  |  | +                   bool delay_unsubscription);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    bool HasSubscribedResources() const;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -240,8 +241,8 @@ class XdsClient::ChannelState::AdsCallState
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    void SendMessageLocked(const std::string& type_url);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  void AcceptLdsUpdate(XdsApi::LdsUpdate lds_update);
 | 
	
		
			
				|  |  | -  void AcceptRdsUpdate(XdsApi::RdsUpdate rds_update);
 | 
	
		
			
				|  |  | +  void AcceptLdsUpdate(absl::optional<XdsApi::LdsUpdate> lds_update);
 | 
	
		
			
				|  |  | +  void AcceptRdsUpdate(absl::optional<XdsApi::RdsUpdate> rds_update);
 | 
	
		
			
				|  |  |    void AcceptCdsUpdate(XdsApi::CdsUpdateMap cds_update_map);
 | 
	
		
			
				|  |  |    void AcceptEdsUpdate(XdsApi::EdsUpdateMap eds_update_map);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -557,9 +558,10 @@ void XdsClient::ChannelState::Subscribe(const std::string& type_url,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsClient::ChannelState::Unsubscribe(const std::string& type_url,
 | 
	
		
			
				|  |  | -                                          const std::string& name) {
 | 
	
		
			
				|  |  | +                                          const std::string& name,
 | 
	
		
			
				|  |  | +                                          bool delay_unsubscription) {
 | 
	
		
			
				|  |  |    if (ads_calld_ != nullptr) {
 | 
	
		
			
				|  |  | -    ads_calld_->calld()->Unsubscribe(type_url, name);
 | 
	
		
			
				|  |  | +    ads_calld_->calld()->Unsubscribe(type_url, name, delay_unsubscription);
 | 
	
		
			
				|  |  |      if (!ads_calld_->calld()->HasSubscribedResources()) ads_calld_.reset();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -862,9 +864,10 @@ void XdsClient::ChannelState::AdsCallState::Subscribe(
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsClient::ChannelState::AdsCallState::Unsubscribe(
 | 
	
		
			
				|  |  | -    const std::string& type_url, const std::string& name) {
 | 
	
		
			
				|  |  | +    const std::string& type_url, const std::string& name,
 | 
	
		
			
				|  |  | +    bool delay_unsubscription) {
 | 
	
		
			
				|  |  |    state_map_[type_url].subscribed_resources.erase(name);
 | 
	
		
			
				|  |  | -  SendMessageLocked(type_url);
 | 
	
		
			
				|  |  | +  if (!delay_unsubscription) SendMessageLocked(type_url);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  bool XdsClient::ChannelState::AdsCallState::HasSubscribedResources() const {
 | 
	
	
		
			
				|  | @@ -875,24 +878,32 @@ bool XdsClient::ChannelState::AdsCallState::HasSubscribedResources() const {
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
 | 
	
		
			
				|  |  | -    XdsApi::LdsUpdate lds_update) {
 | 
	
		
			
				|  |  | +    absl::optional<XdsApi::LdsUpdate> lds_update) {
 | 
	
		
			
				|  |  | +  if (!lds_update.has_value()) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +            "[xds_client %p] LDS update does not include requested resource",
 | 
	
		
			
				|  |  | +            xds_client());
 | 
	
		
			
				|  |  | +    xds_client()->service_config_watcher_->OnError(
 | 
	
		
			
				|  |  | +        GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +            "LDS update does not include requested resource"));
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    const std::string& cluster_name =
 | 
	
		
			
				|  |  | -      lds_update.rds_update.has_value()
 | 
	
		
			
				|  |  | -          ? lds_update.rds_update.value().cluster_name
 | 
	
		
			
				|  |  | +      lds_update->rds_update.has_value()
 | 
	
		
			
				|  |  | +          ? lds_update->rds_update.value().cluster_name
 | 
	
		
			
				|  |  |            : "";
 | 
	
		
			
				|  |  |    if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  |      gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | -            "[xds_client %p] LDS update received: "
 | 
	
		
			
				|  |  | -            "route_config_name=%s, "
 | 
	
		
			
				|  |  | +            "[xds_client %p] LDS update received: route_config_name=%s, "
 | 
	
		
			
				|  |  |              "cluster_name=%s (empty if RDS is needed to obtain it)",
 | 
	
		
			
				|  |  | -            xds_client(), lds_update.route_config_name.c_str(),
 | 
	
		
			
				|  |  | +            xds_client(), lds_update->route_config_name.c_str(),
 | 
	
		
			
				|  |  |              cluster_name.c_str());
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    auto& lds_state = state_map_[XdsApi::kLdsTypeUrl];
 | 
	
		
			
				|  |  |    auto& state = lds_state.subscribed_resources[xds_client()->server_name_];
 | 
	
		
			
				|  |  |    if (state != nullptr) state->Finish();
 | 
	
		
			
				|  |  |    // Ignore identical update.
 | 
	
		
			
				|  |  | -  if (xds_client()->route_config_name_ == lds_update.route_config_name &&
 | 
	
		
			
				|  |  | +  if (xds_client()->route_config_name_ == lds_update->route_config_name &&
 | 
	
		
			
				|  |  |        xds_client()->cluster_name_ == cluster_name) {
 | 
	
		
			
				|  |  |      if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  |        gpr_log(GPR_INFO,
 | 
	
	
		
			
				|  | @@ -901,12 +912,17 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  xds_client()->route_config_name_ = std::move(lds_update.route_config_name);
 | 
	
		
			
				|  |  | -  if (lds_update.rds_update.has_value()) {
 | 
	
		
			
				|  |  | +  if (!xds_client()->route_config_name_.empty()) {
 | 
	
		
			
				|  |  | +    Unsubscribe(
 | 
	
		
			
				|  |  | +        XdsApi::kRdsTypeUrl, xds_client()->route_config_name_,
 | 
	
		
			
				|  |  | +        /*delay_unsubscription=*/!lds_update->route_config_name.empty());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  xds_client()->route_config_name_ = std::move(lds_update->route_config_name);
 | 
	
		
			
				|  |  | +  if (lds_update->rds_update.has_value()) {
 | 
	
		
			
				|  |  |      // If cluster_name was found inlined in LDS response, notify the watcher
 | 
	
		
			
				|  |  |      // immediately.
 | 
	
		
			
				|  |  |      xds_client()->cluster_name_ =
 | 
	
		
			
				|  |  | -        std::move(lds_update.rds_update.value().cluster_name);
 | 
	
		
			
				|  |  | +        std::move(lds_update->rds_update.value().cluster_name);
 | 
	
		
			
				|  |  |      RefCountedPtr<ServiceConfig> service_config;
 | 
	
		
			
				|  |  |      grpc_error* error = xds_client()->CreateServiceConfig(
 | 
	
		
			
				|  |  |          xds_client()->cluster_name_, &service_config);
 | 
	
	
		
			
				|  | @@ -923,19 +939,26 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
 | 
	
		
			
				|  |  | -    XdsApi::RdsUpdate rds_update) {
 | 
	
		
			
				|  |  | -  if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  | +    absl::optional<XdsApi::RdsUpdate> rds_update) {
 | 
	
		
			
				|  |  | +  if (!rds_update.has_value()) {
 | 
	
		
			
				|  |  |      gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | -            "[xds_client %p] RDS update received: "
 | 
	
		
			
				|  |  | -            "cluster_name=%s",
 | 
	
		
			
				|  |  | -            xds_client(), rds_update.cluster_name.c_str());
 | 
	
		
			
				|  |  | +            "[xds_client %p] RDS update does not include requested resource",
 | 
	
		
			
				|  |  | +            xds_client());
 | 
	
		
			
				|  |  | +    xds_client()->service_config_watcher_->OnError(
 | 
	
		
			
				|  |  | +        GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +            "RDS update does not include requested resource"));
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_INFO, "[xds_client %p] RDS update received: cluster_name=%s",
 | 
	
		
			
				|  |  | +            xds_client(), rds_update->cluster_name.c_str());
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    auto& rds_state = state_map_[XdsApi::kRdsTypeUrl];
 | 
	
		
			
				|  |  |    auto& state =
 | 
	
		
			
				|  |  |        rds_state.subscribed_resources[xds_client()->route_config_name_];
 | 
	
		
			
				|  |  |    if (state != nullptr) state->Finish();
 | 
	
		
			
				|  |  |    // Ignore identical update.
 | 
	
		
			
				|  |  | -  if (xds_client()->cluster_name_ == rds_update.cluster_name) {
 | 
	
		
			
				|  |  | +  if (xds_client()->cluster_name_ == rds_update->cluster_name) {
 | 
	
		
			
				|  |  |      if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  |        gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  |                "[xds_client %p] RDS update identical to current, ignoring.",
 | 
	
	
		
			
				|  | @@ -943,7 +966,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  xds_client()->cluster_name_ = std::move(rds_update.cluster_name);
 | 
	
		
			
				|  |  | +  xds_client()->cluster_name_ = std::move(rds_update->cluster_name);
 | 
	
		
			
				|  |  |    // Notify the watcher.
 | 
	
		
			
				|  |  |    RefCountedPtr<ServiceConfig> service_config;
 | 
	
		
			
				|  |  |    grpc_error* error = xds_client()->CreateServiceConfig(
 | 
	
	
		
			
				|  | @@ -959,6 +982,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
 | 
	
		
			
				|  |  |  void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate(
 | 
	
		
			
				|  |  |      XdsApi::CdsUpdateMap cds_update_map) {
 | 
	
		
			
				|  |  |    auto& cds_state = state_map_[XdsApi::kCdsTypeUrl];
 | 
	
		
			
				|  |  | +  std::set<std::string> eds_resource_names_seen;
 | 
	
		
			
				|  |  |    for (auto& p : cds_update_map) {
 | 
	
		
			
				|  |  |      const char* cluster_name = p.first.c_str();
 | 
	
		
			
				|  |  |      XdsApi::CdsUpdate& cds_update = p.second;
 | 
	
	
		
			
				|  | @@ -967,21 +991,22 @@ void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate(
 | 
	
		
			
				|  |  |      if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  |        gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  |                "[xds_client %p] CDS update (cluster=%s) received: "
 | 
	
		
			
				|  |  | -              "eds_service_name=%s, "
 | 
	
		
			
				|  |  | -              "lrs_load_reporting_server_name=%s",
 | 
	
		
			
				|  |  | +              "eds_service_name=%s, lrs_load_reporting_server_name=%s",
 | 
	
		
			
				|  |  |                xds_client(), cluster_name, cds_update.eds_service_name.c_str(),
 | 
	
		
			
				|  |  |                cds_update.lrs_load_reporting_server_name.has_value()
 | 
	
		
			
				|  |  |                    ? cds_update.lrs_load_reporting_server_name.value().c_str()
 | 
	
		
			
				|  |  |                    : "(N/A)");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    ClusterState& cluster_state = xds_client()->cluster_map_[cluster_name];
 | 
	
		
			
				|  |  | +    // Record the EDS resource names seen.
 | 
	
		
			
				|  |  | +    eds_resource_names_seen.insert(cds_update.eds_service_name.empty()
 | 
	
		
			
				|  |  | +                                       ? cluster_name
 | 
	
		
			
				|  |  | +                                       : cds_update.eds_service_name);
 | 
	
		
			
				|  |  |      // Ignore identical update.
 | 
	
		
			
				|  |  | +    ClusterState& cluster_state = xds_client()->cluster_map_[cluster_name];
 | 
	
		
			
				|  |  |      if (cluster_state.update.has_value() &&
 | 
	
		
			
				|  |  | -        cds_update.eds_service_name ==
 | 
	
		
			
				|  |  | -            cluster_state.update.value().eds_service_name &&
 | 
	
		
			
				|  |  | -        cds_update.lrs_load_reporting_server_name.value() ==
 | 
	
		
			
				|  |  | -            cluster_state.update.value()
 | 
	
		
			
				|  |  | -                .lrs_load_reporting_server_name.value()) {
 | 
	
		
			
				|  |  | +        cds_update.eds_service_name == cluster_state.update->eds_service_name &&
 | 
	
		
			
				|  |  | +        cds_update.lrs_load_reporting_server_name ==
 | 
	
		
			
				|  |  | +            cluster_state.update->lrs_load_reporting_server_name) {
 | 
	
		
			
				|  |  |        if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  |          gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  |                  "[xds_client %p] CDS update identical to current, ignoring.",
 | 
	
	
		
			
				|  | @@ -990,12 +1015,41 @@ void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate(
 | 
	
		
			
				|  |  |        continue;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      // Update the cluster state.
 | 
	
		
			
				|  |  | -    cluster_state.update.emplace(std::move(cds_update));
 | 
	
		
			
				|  |  | +    cluster_state.update = std::move(cds_update);
 | 
	
		
			
				|  |  |      // Notify all watchers.
 | 
	
		
			
				|  |  |      for (const auto& p : cluster_state.watchers) {
 | 
	
		
			
				|  |  |        p.first->OnClusterChanged(cluster_state.update.value());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +  // For any subscribed resource that is not present in the update,
 | 
	
		
			
				|  |  | +  // remove it from the cache and notify watchers of the error.
 | 
	
		
			
				|  |  | +  for (const auto& p : cds_state.subscribed_resources) {
 | 
	
		
			
				|  |  | +    const std::string& cluster_name = p.first;
 | 
	
		
			
				|  |  | +    if (cds_update_map.find(cluster_name) == cds_update_map.end()) {
 | 
	
		
			
				|  |  | +      ClusterState& cluster_state = xds_client()->cluster_map_[cluster_name];
 | 
	
		
			
				|  |  | +      cluster_state.update.reset();
 | 
	
		
			
				|  |  | +      for (const auto& p : cluster_state.watchers) {
 | 
	
		
			
				|  |  | +        p.first->OnError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +            "Cluster not present in CDS update"));
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // Also remove any EDS resources that are no longer referred to by any CDS
 | 
	
		
			
				|  |  | +  // resources.
 | 
	
		
			
				|  |  | +  auto& eds_state = state_map_[XdsApi::kEdsTypeUrl];
 | 
	
		
			
				|  |  | +  for (const auto& p : eds_state.subscribed_resources) {
 | 
	
		
			
				|  |  | +    const std::string& eds_resource_name = p.first;
 | 
	
		
			
				|  |  | +    if (eds_resource_names_seen.find(eds_resource_name) ==
 | 
	
		
			
				|  |  | +        eds_resource_names_seen.end()) {
 | 
	
		
			
				|  |  | +      EndpointState& endpoint_state =
 | 
	
		
			
				|  |  | +          xds_client()->endpoint_map_[eds_resource_name];
 | 
	
		
			
				|  |  | +      endpoint_state.update.reset();
 | 
	
		
			
				|  |  | +      for (const auto& p : endpoint_state.watchers) {
 | 
	
		
			
				|  |  | +        p.first->OnError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +            "ClusterLoadAssignment resource removed due to CDS update"));
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsClient::ChannelState::AdsCallState::AcceptEdsUpdate(
 | 
	
	
		
			
				|  | @@ -1058,25 +1112,27 @@ void XdsClient::ChannelState::AdsCallState::AcceptEdsUpdate(
 | 
	
		
			
				|  |  |      EndpointState& endpoint_state =
 | 
	
		
			
				|  |  |          xds_client()->endpoint_map_[eds_service_name];
 | 
	
		
			
				|  |  |      // Ignore identical update.
 | 
	
		
			
				|  |  | -    const XdsApi::EdsUpdate& prev_update = endpoint_state.update;
 | 
	
		
			
				|  |  | -    const bool priority_list_changed =
 | 
	
		
			
				|  |  | -        prev_update.priority_list_update != eds_update.priority_list_update;
 | 
	
		
			
				|  |  | -    const bool drop_config_changed =
 | 
	
		
			
				|  |  | -        prev_update.drop_config == nullptr ||
 | 
	
		
			
				|  |  | -        *prev_update.drop_config != *eds_update.drop_config;
 | 
	
		
			
				|  |  | -    if (!priority_list_changed && !drop_config_changed) {
 | 
	
		
			
				|  |  | -      if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  | -        gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | -                "[xds_client %p] EDS update identical to current, ignoring.",
 | 
	
		
			
				|  |  | -                xds_client());
 | 
	
		
			
				|  |  | +    if (endpoint_state.update.has_value()) {
 | 
	
		
			
				|  |  | +      const XdsApi::EdsUpdate& prev_update = endpoint_state.update.value();
 | 
	
		
			
				|  |  | +      const bool priority_list_changed =
 | 
	
		
			
				|  |  | +          prev_update.priority_list_update != eds_update.priority_list_update;
 | 
	
		
			
				|  |  | +      const bool drop_config_changed =
 | 
	
		
			
				|  |  | +          prev_update.drop_config == nullptr ||
 | 
	
		
			
				|  |  | +          *prev_update.drop_config != *eds_update.drop_config;
 | 
	
		
			
				|  |  | +      if (!priority_list_changed && !drop_config_changed) {
 | 
	
		
			
				|  |  | +        if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  | +          gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +                  "[xds_client %p] EDS update identical to current, ignoring.",
 | 
	
		
			
				|  |  | +                  xds_client());
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        continue;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      continue;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      // Update the cluster state.
 | 
	
		
			
				|  |  |      endpoint_state.update = std::move(eds_update);
 | 
	
		
			
				|  |  |      // Notify all watchers.
 | 
	
		
			
				|  |  |      for (const auto& p : endpoint_state.watchers) {
 | 
	
		
			
				|  |  | -      p.first->OnEndpointChanged(endpoint_state.update);
 | 
	
		
			
				|  |  | +      p.first->OnEndpointChanged(endpoint_state.update.value());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -1150,8 +1206,8 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
 | 
	
		
			
				|  |  |    // mode. We will also need to cancel the timer when we receive a serverlist
 | 
	
		
			
				|  |  |    // from the balancer.
 | 
	
		
			
				|  |  |    // Parse the response.
 | 
	
		
			
				|  |  | -  XdsApi::LdsUpdate lds_update;
 | 
	
		
			
				|  |  | -  XdsApi::RdsUpdate rds_update;
 | 
	
		
			
				|  |  | +  absl::optional<XdsApi::LdsUpdate> lds_update;
 | 
	
		
			
				|  |  | +  absl::optional<XdsApi::RdsUpdate> rds_update;
 | 
	
		
			
				|  |  |    XdsApi::CdsUpdateMap cds_update_map;
 | 
	
		
			
				|  |  |    XdsApi::EdsUpdateMap eds_update_map;
 | 
	
		
			
				|  |  |    std::string version;
 | 
	
	
		
			
				|  | @@ -1160,6 +1216,7 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
 | 
	
		
			
				|  |  |    // Note that ParseAdsResponse() also validates the response.
 | 
	
		
			
				|  |  |    grpc_error* parse_error = xds_client->api_.ParseAdsResponse(
 | 
	
		
			
				|  |  |        response_slice, xds_client->server_name_, xds_client->route_config_name_,
 | 
	
		
			
				|  |  | +      ads_calld->ClusterNamesForRequest(),
 | 
	
		
			
				|  |  |        ads_calld->EdsServiceNamesForRequest(), &lds_update, &rds_update,
 | 
	
		
			
				|  |  |        &cds_update_map, &eds_update_map, &version, &nonce, &type_url);
 | 
	
		
			
				|  |  |    grpc_slice_unref_internal(response_slice);
 | 
	
	
		
			
				|  | @@ -1822,7 +1879,8 @@ void XdsClient::WatchClusterData(
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsClient::CancelClusterDataWatch(StringView cluster_name,
 | 
	
		
			
				|  |  | -                                       ClusterWatcherInterface* watcher) {
 | 
	
		
			
				|  |  | +                                       ClusterWatcherInterface* watcher,
 | 
	
		
			
				|  |  | +                                       bool delay_unsubscription) {
 | 
	
		
			
				|  |  |    if (shutting_down_) return;
 | 
	
		
			
				|  |  |    std::string cluster_name_str = std::string(cluster_name);
 | 
	
		
			
				|  |  |    ClusterState& cluster_state = cluster_map_[cluster_name_str];
 | 
	
	
		
			
				|  | @@ -1831,7 +1889,8 @@ void XdsClient::CancelClusterDataWatch(StringView cluster_name,
 | 
	
		
			
				|  |  |      cluster_state.watchers.erase(it);
 | 
	
		
			
				|  |  |      if (cluster_state.watchers.empty()) {
 | 
	
		
			
				|  |  |        cluster_map_.erase(cluster_name_str);
 | 
	
		
			
				|  |  | -      chand_->Unsubscribe(XdsApi::kCdsTypeUrl, cluster_name_str);
 | 
	
		
			
				|  |  | +      chand_->Unsubscribe(XdsApi::kCdsTypeUrl, cluster_name_str,
 | 
	
		
			
				|  |  | +                          delay_unsubscription);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -1845,18 +1904,19 @@ void XdsClient::WatchEndpointData(
 | 
	
		
			
				|  |  |    endpoint_state.watchers[w] = std::move(watcher);
 | 
	
		
			
				|  |  |    // If we've already received an EDS update, notify the new watcher
 | 
	
		
			
				|  |  |    // immediately.
 | 
	
		
			
				|  |  | -  if (!endpoint_state.update.priority_list_update.empty()) {
 | 
	
		
			
				|  |  | +  if (endpoint_state.update.has_value()) {
 | 
	
		
			
				|  |  |      if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
 | 
	
		
			
				|  |  |        gpr_log(GPR_INFO, "[xds_client %p] returning cached endpoint data for %s",
 | 
	
		
			
				|  |  |                this, StringViewToCString(eds_service_name).get());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    w->OnEndpointChanged(endpoint_state.update);
 | 
	
		
			
				|  |  | +    w->OnEndpointChanged(endpoint_state.update.value());
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    chand_->Subscribe(XdsApi::kEdsTypeUrl, eds_service_name_str);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void XdsClient::CancelEndpointDataWatch(StringView eds_service_name,
 | 
	
		
			
				|  |  | -                                        EndpointWatcherInterface* watcher) {
 | 
	
		
			
				|  |  | +                                        EndpointWatcherInterface* watcher,
 | 
	
		
			
				|  |  | +                                        bool delay_unsubscription) {
 | 
	
		
			
				|  |  |    if (shutting_down_) return;
 | 
	
		
			
				|  |  |    std::string eds_service_name_str = std::string(eds_service_name);
 | 
	
		
			
				|  |  |    EndpointState& endpoint_state = endpoint_map_[eds_service_name_str];
 | 
	
	
		
			
				|  | @@ -1865,7 +1925,8 @@ void XdsClient::CancelEndpointDataWatch(StringView eds_service_name,
 | 
	
		
			
				|  |  |      endpoint_state.watchers.erase(it);
 | 
	
		
			
				|  |  |      if (endpoint_state.watchers.empty()) {
 | 
	
		
			
				|  |  |        endpoint_map_.erase(eds_service_name_str);
 | 
	
		
			
				|  |  | -      chand_->Unsubscribe(XdsApi::kEdsTypeUrl, eds_service_name_str);
 | 
	
		
			
				|  |  | +      chand_->Unsubscribe(XdsApi::kEdsTypeUrl, eds_service_name_str,
 | 
	
		
			
				|  |  | +                          delay_unsubscription);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 |