grpc 1.28.0.pre2 → 1.28.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grpc might be problematic. Click here for more details.

@@ -25,15 +25,18 @@
25
25
 
26
26
  #include <set>
27
27
 
28
+ #include "absl/types/optional.h"
29
+
28
30
  #include <grpc/slice_buffer.h>
29
31
 
30
32
  #include "src/core/ext/filters/client_channel/server_address.h"
31
33
  #include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h"
32
34
  #include "src/core/ext/filters/client_channel/xds/xds_client_stats.h"
33
- #include "src/core/lib/gprpp/optional.h"
34
35
 
35
36
  namespace grpc_core {
36
37
 
38
+ class XdsClient;
39
+
37
40
  class XdsApi {
38
41
  public:
39
42
  static const char* kLdsTypeUrl;
@@ -44,14 +47,25 @@ class XdsApi {
44
47
  struct RdsUpdate {
45
48
  // The name to use in the CDS request.
46
49
  std::string cluster_name;
50
+
51
+ bool operator==(const RdsUpdate& other) const {
52
+ return cluster_name == other.cluster_name;
53
+ }
47
54
  };
48
55
 
56
+ // TODO(roth): When we can use absl::variant<>, consider using that
57
+ // here, to enforce the fact that only one of the two fields can be set.
49
58
  struct LdsUpdate {
50
59
  // The name to use in the RDS request.
51
60
  std::string route_config_name;
52
61
  // The name to use in the CDS request. Present if the LDS response has it
53
62
  // inlined.
54
- Optional<RdsUpdate> rds_update;
63
+ absl::optional<RdsUpdate> rds_update;
64
+
65
+ bool operator==(const LdsUpdate& other) const {
66
+ return route_config_name == other.route_config_name &&
67
+ rds_update == other.rds_update;
68
+ }
55
69
  };
56
70
 
57
71
  using LdsUpdateMap = std::map<std::string /*server_name*/, LdsUpdate>;
@@ -66,7 +80,7 @@ class XdsApi {
66
80
  // If not set, load reporting will be disabled.
67
81
  // If set to the empty string, will use the same server we obtained the CDS
68
82
  // data from.
69
- Optional<std::string> lrs_load_reporting_server_name;
83
+ absl::optional<std::string> lrs_load_reporting_server_name;
70
84
  };
71
85
 
72
86
  using CdsUpdateMap = std::map<std::string /*cluster_name*/, CdsUpdate>;
@@ -150,6 +164,7 @@ class XdsApi {
150
164
  void AddCategory(std::string name, uint32_t parts_per_million) {
151
165
  drop_category_list_.emplace_back(
152
166
  DropCategory{std::move(name), parts_per_million});
167
+ if (parts_per_million == 1000000) drop_all_ = true;
153
168
  }
154
169
 
155
170
  // The only method invoked from the data plane combiner.
@@ -159,6 +174,8 @@ class XdsApi {
159
174
  return drop_category_list_;
160
175
  }
161
176
 
177
+ bool drop_all() const { return drop_all_; }
178
+
162
179
  bool operator==(const DropConfig& other) const {
163
180
  return drop_category_list_ == other.drop_category_list_;
164
181
  }
@@ -166,19 +183,19 @@ class XdsApi {
166
183
 
167
184
  private:
168
185
  DropCategoryList drop_category_list_;
186
+ bool drop_all_ = false;
169
187
  };
170
188
 
171
189
  struct EdsUpdate {
172
190
  PriorityListUpdate priority_list_update;
173
191
  RefCountedPtr<DropConfig> drop_config;
174
- bool drop_all = false;
175
192
  };
176
193
 
177
194
  using EdsUpdateMap = std::map<std::string /*eds_service_name*/, EdsUpdate>;
178
195
 
179
196
  struct ClusterLoadReport {
180
197
  XdsClusterDropStats::DroppedRequestsMap dropped_requests;
181
- std::map<XdsLocalityName*, XdsClusterLocalityStats::Snapshot,
198
+ std::map<RefCountedPtr<XdsLocalityName>, XdsClusterLocalityStats::Snapshot,
182
199
  XdsLocalityName::Less>
183
200
  locality_stats;
184
201
  grpc_millis load_report_interval;
@@ -187,7 +204,7 @@ class XdsApi {
187
204
  std::pair<std::string /*cluster_name*/, std::string /*eds_service_name*/>,
188
205
  ClusterLoadReport>;
189
206
 
190
- explicit XdsApi(const XdsBootstrap::Node* node);
207
+ XdsApi(XdsClient* client, TraceFlag* tracer, const XdsBootstrap::Node* node);
191
208
 
192
209
  // Creates a request to nack an unsupported resource type.
193
210
  // Takes ownership of \a error.
@@ -230,10 +247,12 @@ class XdsApi {
230
247
  const grpc_slice& encoded_response,
231
248
  const std::string& expected_server_name,
232
249
  const std::string& expected_route_config_name,
250
+ const std::set<StringView>& expected_cluster_names,
233
251
  const std::set<StringView>& expected_eds_service_names,
234
- LdsUpdate* lds_update, RdsUpdate* rds_update,
235
- CdsUpdateMap* cds_update_map, EdsUpdateMap* eds_update_map,
236
- std::string* version, std::string* nonce, std::string* type_url);
252
+ absl::optional<LdsUpdate>* lds_update,
253
+ absl::optional<RdsUpdate>* rds_update, CdsUpdateMap* cds_update_map,
254
+ EdsUpdateMap* eds_update_map, std::string* version, std::string* nonce,
255
+ std::string* type_url);
237
256
 
238
257
  // Creates an LRS request querying \a server_name.
239
258
  grpc_slice CreateLrsInitialRequest(const std::string& server_name);
@@ -249,6 +268,8 @@ class XdsApi {
249
268
  grpc_millis* load_reporting_interval);
250
269
 
251
270
  private:
271
+ XdsClient* client_;
272
+ TraceFlag* tracer_;
252
273
  const XdsBootstrap::Node* node_;
253
274
  const std::string build_version_;
254
275
  const std::string user_agent_name_;
@@ -29,20 +29,97 @@
29
29
 
30
30
  namespace grpc_core {
31
31
 
32
- std::unique_ptr<XdsBootstrap> XdsBootstrap::ReadFromFile(grpc_error** error) {
32
+ namespace {
33
+
34
+ UniquePtr<char> BootstrapString(const XdsBootstrap& bootstrap) {
35
+ gpr_strvec v;
36
+ gpr_strvec_init(&v);
37
+ char* tmp;
38
+ if (bootstrap.node() != nullptr) {
39
+ gpr_asprintf(&tmp,
40
+ "node={\n"
41
+ " id=\"%s\",\n"
42
+ " cluster=\"%s\",\n"
43
+ " locality={\n"
44
+ " region=\"%s\",\n"
45
+ " zone=\"%s\",\n"
46
+ " subzone=\"%s\"\n"
47
+ " },\n"
48
+ " metadata=%s,\n"
49
+ "},\n",
50
+ bootstrap.node()->id.c_str(),
51
+ bootstrap.node()->cluster.c_str(),
52
+ bootstrap.node()->locality_region.c_str(),
53
+ bootstrap.node()->locality_zone.c_str(),
54
+ bootstrap.node()->locality_subzone.c_str(),
55
+ bootstrap.node()->metadata.Dump().c_str());
56
+ gpr_strvec_add(&v, tmp);
57
+ }
58
+ gpr_asprintf(&tmp,
59
+ "servers=[\n"
60
+ " {\n"
61
+ " uri=\"%s\",\n"
62
+ " creds=[\n",
63
+ bootstrap.server().server_uri.c_str());
64
+ gpr_strvec_add(&v, tmp);
65
+ for (size_t i = 0; i < bootstrap.server().channel_creds.size(); ++i) {
66
+ const auto& creds = bootstrap.server().channel_creds[i];
67
+ gpr_asprintf(&tmp, " {type=\"%s\", config=%s},\n", creds.type.c_str(),
68
+ creds.config.Dump().c_str());
69
+ gpr_strvec_add(&v, tmp);
70
+ }
71
+ gpr_strvec_add(&v, gpr_strdup(" ]\n }\n]"));
72
+ UniquePtr<char> result(gpr_strvec_flatten(&v, nullptr));
73
+ gpr_strvec_destroy(&v);
74
+ return result;
75
+ }
76
+
77
+ } // namespace
78
+
79
+ std::unique_ptr<XdsBootstrap> XdsBootstrap::ReadFromFile(XdsClient* client,
80
+ TraceFlag* tracer,
81
+ grpc_error** error) {
33
82
  grpc_core::UniquePtr<char> path(gpr_getenv("GRPC_XDS_BOOTSTRAP"));
34
83
  if (path == nullptr) {
35
84
  *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
36
- "GRPC_XDS_BOOTSTRAP env var not set");
85
+ "Environment variable GRPC_XDS_BOOTSTRAP not defined");
37
86
  return nullptr;
38
87
  }
88
+ if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
89
+ gpr_log(GPR_INFO,
90
+ "[xds_client %p] Got bootstrap file location from "
91
+ "GRPC_XDS_BOOTSTRAP environment variable: %s",
92
+ client, path.get());
93
+ }
39
94
  grpc_slice contents;
40
95
  *error = grpc_load_file(path.get(), /*add_null_terminator=*/true, &contents);
41
96
  if (*error != GRPC_ERROR_NONE) return nullptr;
42
- Json json = Json::Parse(StringViewFromSlice(contents), error);
97
+ StringView contents_str_view = StringViewFromSlice(contents);
98
+ if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
99
+ UniquePtr<char> str = StringViewToCString(contents_str_view);
100
+ gpr_log(GPR_DEBUG, "[xds_client %p] Bootstrap file contents: %s", client,
101
+ str.get());
102
+ }
103
+ Json json = Json::Parse(contents_str_view, error);
43
104
  grpc_slice_unref_internal(contents);
44
- if (*error != GRPC_ERROR_NONE) return nullptr;
45
- return absl::make_unique<XdsBootstrap>(std::move(json), error);
105
+ if (*error != GRPC_ERROR_NONE) {
106
+ char* msg;
107
+ gpr_asprintf(&msg, "Failed to parse bootstrap file %s", path.get());
108
+ grpc_error* error_out =
109
+ GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, error, 1);
110
+ gpr_free(msg);
111
+ GRPC_ERROR_UNREF(*error);
112
+ *error = error_out;
113
+ return nullptr;
114
+ }
115
+ std::unique_ptr<XdsBootstrap> result =
116
+ absl::make_unique<XdsBootstrap>(std::move(json), error);
117
+ if (*error == GRPC_ERROR_NONE && GRPC_TRACE_FLAG_ENABLED(*tracer)) {
118
+ gpr_log(GPR_INFO,
119
+ "[xds_client %p] Bootstrap config for creating xds client:\n%s",
120
+ client, BootstrapString(*result).get());
121
+ }
122
+ return result;
46
123
  }
47
124
 
48
125
  XdsBootstrap::XdsBootstrap(Json json, grpc_error** error) {
@@ -33,6 +33,8 @@
33
33
 
34
34
  namespace grpc_core {
35
35
 
36
+ class XdsClient;
37
+
36
38
  class XdsBootstrap {
37
39
  public:
38
40
  struct Node {
@@ -56,7 +58,9 @@ class XdsBootstrap {
56
58
 
57
59
  // If *error is not GRPC_ERROR_NONE after returning, then there was an
58
60
  // error reading the file.
59
- static std::unique_ptr<XdsBootstrap> ReadFromFile(grpc_error** error);
61
+ static std::unique_ptr<XdsBootstrap> ReadFromFile(XdsClient* client,
62
+ TraceFlag* tracer,
63
+ grpc_error** error);
60
64
 
61
65
  // Do not instantiate directly -- use ReadFromFile() above instead.
62
66
  XdsBootstrap(Json json, grpc_error** error);
@@ -22,6 +22,8 @@
22
22
  #include <limits.h>
23
23
  #include <string.h>
24
24
 
25
+ #include "absl/strings/str_join.h"
26
+
25
27
  #include <grpc/byte_buffer_reader.h>
26
28
  #include <grpc/grpc.h>
27
29
  #include <grpc/support/alloc.h>
@@ -126,7 +128,8 @@ class XdsClient::ChannelState::AdsCallState
126
128
  bool seen_response() const { return seen_response_; }
127
129
 
128
130
  void Subscribe(const std::string& type_url, const std::string& name);
129
- void Unsubscribe(const std::string& type_url, const std::string& name);
131
+ void Unsubscribe(const std::string& type_url, const std::string& name,
132
+ bool delay_unsubscription);
130
133
 
131
134
  bool HasSubscribedResources() const;
132
135
 
@@ -238,8 +241,8 @@ class XdsClient::ChannelState::AdsCallState
238
241
 
239
242
  void SendMessageLocked(const std::string& type_url);
240
243
 
241
- void AcceptLdsUpdate(XdsApi::LdsUpdate lds_update);
242
- void AcceptRdsUpdate(XdsApi::RdsUpdate rds_update);
244
+ void AcceptLdsUpdate(absl::optional<XdsApi::LdsUpdate> lds_update);
245
+ void AcceptRdsUpdate(absl::optional<XdsApi::RdsUpdate> rds_update);
243
246
  void AcceptCdsUpdate(XdsApi::CdsUpdateMap cds_update_map);
244
247
  void AcceptEdsUpdate(XdsApi::EdsUpdateMap eds_update_map);
245
248
 
@@ -299,7 +302,6 @@ class XdsClient::ChannelState::LrsCallState
299
302
  void Orphan() override;
300
303
 
301
304
  void MaybeStartReportingLocked();
302
- bool ShouldSendLoadReports(const StringView& cluster_name) const;
303
305
 
304
306
  RetryableCall<LrsCallState>* parent() { return parent_.get(); }
305
307
  ChannelState* chand() const { return parent_->chand(); }
@@ -555,9 +557,10 @@ void XdsClient::ChannelState::Subscribe(const std::string& type_url,
555
557
  }
556
558
 
557
559
  void XdsClient::ChannelState::Unsubscribe(const std::string& type_url,
558
- const std::string& name) {
560
+ const std::string& name,
561
+ bool delay_unsubscription) {
559
562
  if (ads_calld_ != nullptr) {
560
- ads_calld_->calld()->Unsubscribe(type_url, name);
563
+ ads_calld_->calld()->Unsubscribe(type_url, name, delay_unsubscription);
561
564
  if (!ads_calld_->calld()->HasSubscribedResources()) ads_calld_.reset();
562
565
  }
563
566
  }
@@ -701,7 +704,8 @@ XdsClient::ChannelState::AdsCallState::AdsCallState(
701
704
  grpc_op* op = ops;
702
705
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
703
706
  op->data.send_initial_metadata.count = 0;
704
- op->flags = 0;
707
+ op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY |
708
+ GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET;
705
709
  op->reserved = nullptr;
706
710
  op++;
707
711
  call_error = grpc_call_start_batch_and_execute(call_, ops, (size_t)(op - ops),
@@ -712,6 +716,11 @@ XdsClient::ChannelState::AdsCallState::AdsCallState(
712
716
  grpc_schedule_on_exec_ctx);
713
717
  if (xds_client()->service_config_watcher_ != nullptr) {
714
718
  Subscribe(XdsApi::kLdsTypeUrl, xds_client()->server_name_);
719
+ if (xds_client()->lds_result_.has_value() &&
720
+ !xds_client()->lds_result_->route_config_name.empty()) {
721
+ Subscribe(XdsApi::kRdsTypeUrl,
722
+ xds_client()->lds_result_->route_config_name);
723
+ }
715
724
  }
716
725
  for (const auto& p : xds_client()->cluster_map_) {
717
726
  Subscribe(XdsApi::kCdsTypeUrl, std::string(p.first));
@@ -788,33 +797,47 @@ void XdsClient::ChannelState::AdsCallState::SendMessageLocked(
788
797
  return;
789
798
  }
790
799
  auto& state = state_map_[type_url];
791
- grpc_error* error = state.error;
792
- state.error = GRPC_ERROR_NONE;
793
800
  grpc_slice request_payload_slice;
801
+ std::set<StringView> resource_names;
794
802
  if (type_url == XdsApi::kLdsTypeUrl) {
803
+ resource_names.insert(xds_client()->server_name_);
795
804
  request_payload_slice = xds_client()->api_.CreateLdsRequest(
796
- xds_client()->server_name_, state.version, state.nonce, error,
797
- !sent_initial_message_);
805
+ xds_client()->server_name_, state.version, state.nonce,
806
+ GRPC_ERROR_REF(state.error), !sent_initial_message_);
798
807
  state.subscribed_resources[xds_client()->server_name_]->Start(Ref());
799
808
  } else if (type_url == XdsApi::kRdsTypeUrl) {
809
+ resource_names.insert(xds_client()->lds_result_->route_config_name);
800
810
  request_payload_slice = xds_client()->api_.CreateRdsRequest(
801
- xds_client()->route_config_name_, state.version, state.nonce, error,
802
- !sent_initial_message_);
803
- state.subscribed_resources[xds_client()->route_config_name_]->Start(Ref());
811
+ xds_client()->lds_result_->route_config_name, state.version,
812
+ state.nonce, GRPC_ERROR_REF(state.error), !sent_initial_message_);
813
+ state.subscribed_resources[xds_client()->lds_result_->route_config_name]
814
+ ->Start(Ref());
804
815
  } else if (type_url == XdsApi::kCdsTypeUrl) {
816
+ resource_names = ClusterNamesForRequest();
805
817
  request_payload_slice = xds_client()->api_.CreateCdsRequest(
806
- ClusterNamesForRequest(), state.version, state.nonce, error,
818
+ resource_names, state.version, state.nonce, GRPC_ERROR_REF(state.error),
807
819
  !sent_initial_message_);
808
820
  } else if (type_url == XdsApi::kEdsTypeUrl) {
821
+ resource_names = EdsServiceNamesForRequest();
809
822
  request_payload_slice = xds_client()->api_.CreateEdsRequest(
810
- EdsServiceNamesForRequest(), state.version, state.nonce, error,
823
+ resource_names, state.version, state.nonce, GRPC_ERROR_REF(state.error),
811
824
  !sent_initial_message_);
812
825
  } else {
813
826
  request_payload_slice = xds_client()->api_.CreateUnsupportedTypeNackRequest(
814
- type_url, state.nonce, state.error);
827
+ type_url, state.nonce, GRPC_ERROR_REF(state.error));
815
828
  state_map_.erase(type_url);
816
829
  }
817
830
  sent_initial_message_ = true;
831
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
832
+ gpr_log(GPR_INFO,
833
+ "[xds_client %p] sending ADS request: type=%s version=%s nonce=%s "
834
+ "error=%s resources=%s",
835
+ xds_client(), type_url.c_str(), state.version.c_str(),
836
+ state.nonce.c_str(), grpc_error_string(state.error),
837
+ absl::StrJoin(resource_names, " ").c_str());
838
+ }
839
+ GRPC_ERROR_UNREF(state.error);
840
+ state.error = GRPC_ERROR_NONE;
818
841
  // Create message payload.
819
842
  send_message_payload_ =
820
843
  grpc_raw_byte_buffer_create(&request_payload_slice, 1);
@@ -847,9 +870,10 @@ void XdsClient::ChannelState::AdsCallState::Subscribe(
847
870
  }
848
871
 
849
872
  void XdsClient::ChannelState::AdsCallState::Unsubscribe(
850
- const std::string& type_url, const std::string& name) {
873
+ const std::string& type_url, const std::string& name,
874
+ bool delay_unsubscription) {
851
875
  state_map_[type_url].subscribed_resources.erase(name);
852
- SendMessageLocked(type_url);
876
+ if (!delay_unsubscription) SendMessageLocked(type_url);
853
877
  }
854
878
 
855
879
  bool XdsClient::ChannelState::AdsCallState::HasSubscribedResources() const {
@@ -860,25 +884,33 @@ bool XdsClient::ChannelState::AdsCallState::HasSubscribedResources() const {
860
884
  }
861
885
 
862
886
  void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
863
- XdsApi::LdsUpdate lds_update) {
864
- const std::string& cluster_name =
865
- lds_update.rds_update.has_value()
866
- ? lds_update.rds_update.value().cluster_name
867
- : "";
887
+ absl::optional<XdsApi::LdsUpdate> lds_update) {
888
+ if (!lds_update.has_value()) {
889
+ gpr_log(GPR_INFO,
890
+ "[xds_client %p] LDS update does not include requested resource",
891
+ xds_client());
892
+ xds_client()->service_config_watcher_->OnError(
893
+ GRPC_ERROR_CREATE_FROM_STATIC_STRING(
894
+ "LDS update does not include requested resource"));
895
+ return;
896
+ }
868
897
  if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
869
898
  gpr_log(GPR_INFO,
870
- "[xds_client %p] LDS update received: "
871
- "route_config_name=%s, "
872
- "cluster_name=%s (empty if RDS is needed to obtain it)",
873
- xds_client(), lds_update.route_config_name.c_str(),
874
- cluster_name.c_str());
899
+ "[xds_client %p] LDS update received: route_config_name=%s, "
900
+ "cluster_name=%s",
901
+ xds_client(),
902
+ (!lds_update->route_config_name.empty()
903
+ ? lds_update->route_config_name.c_str()
904
+ : "<inlined>"),
905
+ (lds_update->rds_update.has_value()
906
+ ? lds_update->rds_update->cluster_name.c_str()
907
+ : "<to be obtained via RDS>"));
875
908
  }
876
909
  auto& lds_state = state_map_[XdsApi::kLdsTypeUrl];
877
910
  auto& state = lds_state.subscribed_resources[xds_client()->server_name_];
878
911
  if (state != nullptr) state->Finish();
879
912
  // Ignore identical update.
880
- if (xds_client()->route_config_name_ == lds_update.route_config_name &&
881
- xds_client()->cluster_name_ == cluster_name) {
913
+ if (xds_client()->lds_result_ == lds_update) {
882
914
  if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
883
915
  gpr_log(GPR_INFO,
884
916
  "[xds_client %p] LDS update identical to current, ignoring.",
@@ -886,15 +918,19 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
886
918
  }
887
919
  return;
888
920
  }
889
- xds_client()->route_config_name_ = std::move(lds_update.route_config_name);
890
- if (lds_update.rds_update.has_value()) {
891
- // If cluster_name was found inlined in LDS response, notify the watcher
892
- // immediately.
893
- xds_client()->cluster_name_ =
894
- std::move(lds_update.rds_update.value().cluster_name);
921
+ if (xds_client()->lds_result_.has_value() &&
922
+ !xds_client()->lds_result_->route_config_name.empty()) {
923
+ Unsubscribe(
924
+ XdsApi::kRdsTypeUrl, xds_client()->lds_result_->route_config_name,
925
+ /*delay_unsubscription=*/!lds_update->route_config_name.empty());
926
+ }
927
+ xds_client()->lds_result_ = std::move(lds_update);
928
+ if (xds_client()->lds_result_->rds_update.has_value()) {
929
+ // If the RouteConfiguration was found inlined in LDS response, notify
930
+ // the watcher immediately.
895
931
  RefCountedPtr<ServiceConfig> service_config;
896
932
  grpc_error* error = xds_client()->CreateServiceConfig(
897
- xds_client()->cluster_name_, &service_config);
933
+ xds_client()->lds_result_->rds_update->cluster_name, &service_config);
898
934
  if (error == GRPC_ERROR_NONE) {
899
935
  xds_client()->service_config_watcher_->OnServiceConfigChanged(
900
936
  std::move(service_config));
@@ -903,24 +939,33 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
903
939
  }
904
940
  } else {
905
941
  // Send RDS request for dynamic resolution.
906
- Subscribe(XdsApi::kRdsTypeUrl, xds_client()->route_config_name_);
942
+ Subscribe(XdsApi::kRdsTypeUrl,
943
+ xds_client()->lds_result_->route_config_name);
907
944
  }
908
945
  }
909
946
 
910
947
  void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
911
- XdsApi::RdsUpdate rds_update) {
912
- if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
948
+ absl::optional<XdsApi::RdsUpdate> rds_update) {
949
+ if (!rds_update.has_value()) {
913
950
  gpr_log(GPR_INFO,
914
- "[xds_client %p] RDS update received: "
915
- "cluster_name=%s",
916
- xds_client(), rds_update.cluster_name.c_str());
951
+ "[xds_client %p] RDS update does not include requested resource",
952
+ xds_client());
953
+ xds_client()->service_config_watcher_->OnError(
954
+ GRPC_ERROR_CREATE_FROM_STATIC_STRING(
955
+ "RDS update does not include requested resource"));
956
+ return;
957
+ }
958
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
959
+ gpr_log(GPR_INFO, "[xds_client %p] RDS update received: cluster_name=%s",
960
+ xds_client(), rds_update->cluster_name.c_str());
917
961
  }
918
962
  auto& rds_state = state_map_[XdsApi::kRdsTypeUrl];
919
963
  auto& state =
920
- rds_state.subscribed_resources[xds_client()->route_config_name_];
964
+ rds_state
965
+ .subscribed_resources[xds_client()->lds_result_->route_config_name];
921
966
  if (state != nullptr) state->Finish();
922
967
  // Ignore identical update.
923
- if (xds_client()->cluster_name_ == rds_update.cluster_name) {
968
+ if (xds_client()->rds_result_ == rds_update) {
924
969
  if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
925
970
  gpr_log(GPR_INFO,
926
971
  "[xds_client %p] RDS update identical to current, ignoring.",
@@ -928,11 +973,11 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
928
973
  }
929
974
  return;
930
975
  }
931
- xds_client()->cluster_name_ = std::move(rds_update.cluster_name);
976
+ xds_client()->rds_result_ = std::move(rds_update);
932
977
  // Notify the watcher.
933
978
  RefCountedPtr<ServiceConfig> service_config;
934
979
  grpc_error* error = xds_client()->CreateServiceConfig(
935
- xds_client()->cluster_name_, &service_config);
980
+ xds_client()->rds_result_->cluster_name, &service_config);
936
981
  if (error == GRPC_ERROR_NONE) {
937
982
  xds_client()->service_config_watcher_->OnServiceConfigChanged(
938
983
  std::move(service_config));
@@ -944,6 +989,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate(
944
989
  void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate(
945
990
  XdsApi::CdsUpdateMap cds_update_map) {
946
991
  auto& cds_state = state_map_[XdsApi::kCdsTypeUrl];
992
+ std::set<std::string> eds_resource_names_seen;
947
993
  for (auto& p : cds_update_map) {
948
994
  const char* cluster_name = p.first.c_str();
949
995
  XdsApi::CdsUpdate& cds_update = p.second;
@@ -952,21 +998,22 @@ void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate(
952
998
  if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
953
999
  gpr_log(GPR_INFO,
954
1000
  "[xds_client %p] CDS update (cluster=%s) received: "
955
- "eds_service_name=%s, "
956
- "lrs_load_reporting_server_name=%s",
1001
+ "eds_service_name=%s, lrs_load_reporting_server_name=%s",
957
1002
  xds_client(), cluster_name, cds_update.eds_service_name.c_str(),
958
1003
  cds_update.lrs_load_reporting_server_name.has_value()
959
1004
  ? cds_update.lrs_load_reporting_server_name.value().c_str()
960
1005
  : "(N/A)");
961
1006
  }
962
- ClusterState& cluster_state = xds_client()->cluster_map_[cluster_name];
1007
+ // Record the EDS resource names seen.
1008
+ eds_resource_names_seen.insert(cds_update.eds_service_name.empty()
1009
+ ? cluster_name
1010
+ : cds_update.eds_service_name);
963
1011
  // Ignore identical update.
1012
+ ClusterState& cluster_state = xds_client()->cluster_map_[cluster_name];
964
1013
  if (cluster_state.update.has_value() &&
965
- cds_update.eds_service_name ==
966
- cluster_state.update.value().eds_service_name &&
967
- cds_update.lrs_load_reporting_server_name.value() ==
968
- cluster_state.update.value()
969
- .lrs_load_reporting_server_name.value()) {
1014
+ cds_update.eds_service_name == cluster_state.update->eds_service_name &&
1015
+ cds_update.lrs_load_reporting_server_name ==
1016
+ cluster_state.update->lrs_load_reporting_server_name) {
970
1017
  if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
971
1018
  gpr_log(GPR_INFO,
972
1019
  "[xds_client %p] CDS update identical to current, ignoring.",
@@ -975,12 +1022,41 @@ void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate(
975
1022
  continue;
976
1023
  }
977
1024
  // Update the cluster state.
978
- cluster_state.update.emplace(std::move(cds_update));
1025
+ cluster_state.update = std::move(cds_update);
979
1026
  // Notify all watchers.
980
1027
  for (const auto& p : cluster_state.watchers) {
981
1028
  p.first->OnClusterChanged(cluster_state.update.value());
982
1029
  }
983
1030
  }
1031
+ // For any subscribed resource that is not present in the update,
1032
+ // remove it from the cache and notify watchers of the error.
1033
+ for (const auto& p : cds_state.subscribed_resources) {
1034
+ const std::string& cluster_name = p.first;
1035
+ if (cds_update_map.find(cluster_name) == cds_update_map.end()) {
1036
+ ClusterState& cluster_state = xds_client()->cluster_map_[cluster_name];
1037
+ cluster_state.update.reset();
1038
+ for (const auto& p : cluster_state.watchers) {
1039
+ p.first->OnError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1040
+ "Cluster not present in CDS update"));
1041
+ }
1042
+ }
1043
+ }
1044
+ // Also remove any EDS resources that are no longer referred to by any CDS
1045
+ // resources.
1046
+ auto& eds_state = state_map_[XdsApi::kEdsTypeUrl];
1047
+ for (const auto& p : eds_state.subscribed_resources) {
1048
+ const std::string& eds_resource_name = p.first;
1049
+ if (eds_resource_names_seen.find(eds_resource_name) ==
1050
+ eds_resource_names_seen.end()) {
1051
+ EndpointState& endpoint_state =
1052
+ xds_client()->endpoint_map_[eds_resource_name];
1053
+ endpoint_state.update.reset();
1054
+ for (const auto& p : endpoint_state.watchers) {
1055
+ p.first->OnError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1056
+ "ClusterLoadAssignment resource removed due to CDS update"));
1057
+ }
1058
+ }
1059
+ }
984
1060
  }
985
1061
 
986
1062
  void XdsClient::ChannelState::AdsCallState::AcceptEdsUpdate(
@@ -998,7 +1074,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptEdsUpdate(
998
1074
  " drop categories received (drop_all=%d)",
999
1075
  xds_client(), eds_update.priority_list_update.size(),
1000
1076
  eds_update.drop_config->drop_category_list().size(),
1001
- eds_update.drop_all);
1077
+ eds_update.drop_config->drop_all());
1002
1078
  for (size_t priority = 0;
1003
1079
  priority < eds_update.priority_list_update.size(); ++priority) {
1004
1080
  const auto* locality_map_update = eds_update.priority_list_update.Find(
@@ -1043,25 +1119,27 @@ void XdsClient::ChannelState::AdsCallState::AcceptEdsUpdate(
1043
1119
  EndpointState& endpoint_state =
1044
1120
  xds_client()->endpoint_map_[eds_service_name];
1045
1121
  // Ignore identical update.
1046
- const XdsApi::EdsUpdate& prev_update = endpoint_state.update;
1047
- const bool priority_list_changed =
1048
- prev_update.priority_list_update != eds_update.priority_list_update;
1049
- const bool drop_config_changed =
1050
- prev_update.drop_config == nullptr ||
1051
- *prev_update.drop_config != *eds_update.drop_config;
1052
- if (!priority_list_changed && !drop_config_changed) {
1053
- if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
1054
- gpr_log(GPR_INFO,
1055
- "[xds_client %p] EDS update identical to current, ignoring.",
1056
- xds_client());
1122
+ if (endpoint_state.update.has_value()) {
1123
+ const XdsApi::EdsUpdate& prev_update = endpoint_state.update.value();
1124
+ const bool priority_list_changed =
1125
+ prev_update.priority_list_update != eds_update.priority_list_update;
1126
+ const bool drop_config_changed =
1127
+ prev_update.drop_config == nullptr ||
1128
+ *prev_update.drop_config != *eds_update.drop_config;
1129
+ if (!priority_list_changed && !drop_config_changed) {
1130
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
1131
+ gpr_log(GPR_INFO,
1132
+ "[xds_client %p] EDS update identical to current, ignoring.",
1133
+ xds_client());
1134
+ }
1135
+ continue;
1057
1136
  }
1058
- continue;
1059
1137
  }
1060
1138
  // Update the cluster state.
1061
1139
  endpoint_state.update = std::move(eds_update);
1062
1140
  // Notify all watchers.
1063
1141
  for (const auto& p : endpoint_state.watchers) {
1064
- p.first->OnEndpointChanged(endpoint_state.update);
1142
+ p.first->OnEndpointChanged(endpoint_state.update.value());
1065
1143
  }
1066
1144
  }
1067
1145
  }
@@ -1135,8 +1213,8 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
1135
1213
  // mode. We will also need to cancel the timer when we receive a serverlist
1136
1214
  // from the balancer.
1137
1215
  // Parse the response.
1138
- XdsApi::LdsUpdate lds_update;
1139
- XdsApi::RdsUpdate rds_update;
1216
+ absl::optional<XdsApi::LdsUpdate> lds_update;
1217
+ absl::optional<XdsApi::RdsUpdate> rds_update;
1140
1218
  XdsApi::CdsUpdateMap cds_update_map;
1141
1219
  XdsApi::EdsUpdateMap eds_update_map;
1142
1220
  std::string version;
@@ -1144,13 +1222,18 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
1144
1222
  std::string type_url;
1145
1223
  // Note that ParseAdsResponse() also validates the response.
1146
1224
  grpc_error* parse_error = xds_client->api_.ParseAdsResponse(
1147
- response_slice, xds_client->server_name_, xds_client->route_config_name_,
1225
+ response_slice, xds_client->server_name_,
1226
+ (xds_client->lds_result_.has_value()
1227
+ ? xds_client->lds_result_->route_config_name
1228
+ : ""),
1229
+ ads_calld->ClusterNamesForRequest(),
1148
1230
  ads_calld->EdsServiceNamesForRequest(), &lds_update, &rds_update,
1149
1231
  &cds_update_map, &eds_update_map, &version, &nonce, &type_url);
1150
1232
  grpc_slice_unref_internal(response_slice);
1151
1233
  if (type_url.empty()) {
1152
1234
  // Ignore unparsable response.
1153
- gpr_log(GPR_ERROR, "[xds_client %p] No type_url found. error=%s",
1235
+ gpr_log(GPR_ERROR,
1236
+ "[xds_client %p] Error parsing ADS response (%s) -- ignoring",
1154
1237
  xds_client, grpc_error_string(parse_error));
1155
1238
  GRPC_ERROR_UNREF(parse_error);
1156
1239
  } else {
@@ -1162,10 +1245,11 @@ void XdsClient::ChannelState::AdsCallState::OnResponseReceivedLocked(
1162
1245
  GRPC_ERROR_UNREF(state.error);
1163
1246
  state.error = parse_error;
1164
1247
  // NACK unacceptable update.
1165
- gpr_log(
1166
- GPR_ERROR,
1167
- "[xds_client %p] ADS response can't be accepted, NACKing. error=%s",
1168
- xds_client, grpc_error_string(parse_error));
1248
+ gpr_log(GPR_ERROR,
1249
+ "[xds_client %p] ADS response invalid for resource type %s "
1250
+ "version %s, will NACK: nonce=%s error=%s",
1251
+ xds_client, type_url.c_str(), version.c_str(),
1252
+ state.nonce.c_str(), grpc_error_string(parse_error));
1169
1253
  ads_calld->SendMessageLocked(type_url);
1170
1254
  } else {
1171
1255
  ads_calld->seen_response_ = true;
@@ -1335,7 +1419,7 @@ bool LoadReportCountersAreZero(const XdsApi::ClusterLoadReportMap& snapshot) {
1335
1419
  void XdsClient::ChannelState::LrsCallState::Reporter::SendReportLocked() {
1336
1420
  // Construct snapshot from all reported stats.
1337
1421
  XdsApi::ClusterLoadReportMap snapshot =
1338
- xds_client()->BuildLoadReportSnapshot();
1422
+ xds_client()->BuildLoadReportSnapshot(parent_->cluster_names_);
1339
1423
  // Skip client load report if the counters were all zero in the last
1340
1424
  // report and they are still zero in this one.
1341
1425
  const bool old_val = last_report_counters_were_zero_;
@@ -1381,6 +1465,12 @@ void XdsClient::ChannelState::LrsCallState::Reporter::OnReportDoneLocked(
1381
1465
  Reporter* self = static_cast<Reporter*>(arg);
1382
1466
  grpc_byte_buffer_destroy(self->parent_->send_message_payload_);
1383
1467
  self->parent_->send_message_payload_ = nullptr;
1468
+ // If there are no more registered stats to report, cancel the call.
1469
+ if (self->xds_client()->load_report_map_.empty()) {
1470
+ self->parent_->chand()->StopLrsCall();
1471
+ self->Unref(DEBUG_LOCATION, "Reporter+report_done+no_more_reporters");
1472
+ return;
1473
+ }
1384
1474
  if (error != GRPC_ERROR_NONE || !self->IsCurrentReporterOnCall()) {
1385
1475
  // If this reporter is no longer the current one on the call, the reason
1386
1476
  // might be that it was orphaned for a new one due to config update.
@@ -1436,7 +1526,8 @@ XdsClient::ChannelState::LrsCallState::LrsCallState(
1436
1526
  grpc_op* op = ops;
1437
1527
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
1438
1528
  op->data.send_initial_metadata.count = 0;
1439
- op->flags = 0;
1529
+ op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY |
1530
+ GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET;
1440
1531
  op->reserved = nullptr;
1441
1532
  op++;
1442
1533
  // Op: send request message.
@@ -1524,20 +1615,16 @@ void XdsClient::ChannelState::LrsCallState::MaybeStartReportingLocked() {
1524
1615
  // Don't start if the ADS call hasn't received any valid response. Note that
1525
1616
  // this must be the first channel because it is the current channel but its
1526
1617
  // ADS call hasn't seen any response.
1527
- AdsCallState* ads_calld = chand()->ads_calld_->calld();
1528
- if (ads_calld == nullptr || !ads_calld->seen_response()) return;
1618
+ if (chand()->ads_calld_ == nullptr ||
1619
+ chand()->ads_calld_->calld() == nullptr ||
1620
+ !chand()->ads_calld_->calld()->seen_response()) {
1621
+ return;
1622
+ }
1529
1623
  // Start reporting.
1530
1624
  reporter_ = MakeOrphanable<Reporter>(
1531
1625
  Ref(DEBUG_LOCATION, "LRS+load_report+start"), load_reporting_interval_);
1532
1626
  }
1533
1627
 
1534
- bool XdsClient::ChannelState::LrsCallState::ShouldSendLoadReports(
1535
- const StringView& cluster_name) const {
1536
- // Only send load reports for the clusters that are asked for by the LRS
1537
- // server.
1538
- return cluster_names_.find(std::string(cluster_name)) != cluster_names_.end();
1539
- }
1540
-
1541
1628
  void XdsClient::ChannelState::LrsCallState::OnInitialRequestSent(
1542
1629
  void* arg, grpc_error* error) {
1543
1630
  LrsCallState* lrs_calld = static_cast<LrsCallState*>(arg);
@@ -1724,10 +1811,15 @@ XdsClient::XdsClient(Combiner* combiner, grpc_pollset_set* interested_parties,
1724
1811
  request_timeout_(GetRequestTimeout(channel_args)),
1725
1812
  combiner_(GRPC_COMBINER_REF(combiner, "xds_client")),
1726
1813
  interested_parties_(interested_parties),
1727
- bootstrap_(XdsBootstrap::ReadFromFile(error)),
1728
- api_(bootstrap_ == nullptr ? nullptr : bootstrap_->node()),
1814
+ bootstrap_(
1815
+ XdsBootstrap::ReadFromFile(this, &grpc_xds_client_trace, error)),
1816
+ api_(this, &grpc_xds_client_trace,
1817
+ bootstrap_ == nullptr ? nullptr : bootstrap_->node()),
1729
1818
  server_name_(server_name),
1730
1819
  service_config_watcher_(std::move(watcher)) {
1820
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
1821
+ gpr_log(GPR_INFO, "[xds_client %p] creating xds client", this);
1822
+ }
1731
1823
  if (*error != GRPC_ERROR_NONE) {
1732
1824
  gpr_log(GPR_ERROR, "[xds_client %p] failed to read bootstrap file: %s",
1733
1825
  this, grpc_error_string(*error));
@@ -1752,9 +1844,17 @@ XdsClient::XdsClient(Combiner* combiner, grpc_pollset_set* interested_parties,
1752
1844
  }
1753
1845
  }
1754
1846
 
1755
- XdsClient::~XdsClient() { GRPC_COMBINER_UNREF(combiner_, "xds_client"); }
1847
+ XdsClient::~XdsClient() {
1848
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
1849
+ gpr_log(GPR_INFO, "[xds_client %p] destroying xds client", this);
1850
+ }
1851
+ GRPC_COMBINER_UNREF(combiner_, "xds_client");
1852
+ }
1756
1853
 
1757
1854
  void XdsClient::Orphan() {
1855
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
1856
+ gpr_log(GPR_INFO, "[xds_client %p] shutting down xds client", this);
1857
+ }
1758
1858
  shutting_down_ = true;
1759
1859
  chand_.reset();
1760
1860
  // We do not clear cluster_map_ and endpoint_map_ if the xds client was
@@ -1779,13 +1879,18 @@ void XdsClient::WatchClusterData(
1779
1879
  // If we've already received an CDS update, notify the new watcher
1780
1880
  // immediately.
1781
1881
  if (cluster_state.update.has_value()) {
1882
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
1883
+ gpr_log(GPR_INFO, "[xds_client %p] returning cached cluster data for %s",
1884
+ this, StringViewToCString(cluster_name).get());
1885
+ }
1782
1886
  w->OnClusterChanged(cluster_state.update.value());
1783
1887
  }
1784
1888
  chand_->Subscribe(XdsApi::kCdsTypeUrl, cluster_name_str);
1785
1889
  }
1786
1890
 
1787
1891
  void XdsClient::CancelClusterDataWatch(StringView cluster_name,
1788
- ClusterWatcherInterface* watcher) {
1892
+ ClusterWatcherInterface* watcher,
1893
+ bool delay_unsubscription) {
1789
1894
  if (shutting_down_) return;
1790
1895
  std::string cluster_name_str = std::string(cluster_name);
1791
1896
  ClusterState& cluster_state = cluster_map_[cluster_name_str];
@@ -1794,7 +1899,8 @@ void XdsClient::CancelClusterDataWatch(StringView cluster_name,
1794
1899
  cluster_state.watchers.erase(it);
1795
1900
  if (cluster_state.watchers.empty()) {
1796
1901
  cluster_map_.erase(cluster_name_str);
1797
- chand_->Unsubscribe(XdsApi::kCdsTypeUrl, cluster_name_str);
1902
+ chand_->Unsubscribe(XdsApi::kCdsTypeUrl, cluster_name_str,
1903
+ delay_unsubscription);
1798
1904
  }
1799
1905
  }
1800
1906
  }
@@ -1808,14 +1914,19 @@ void XdsClient::WatchEndpointData(
1808
1914
  endpoint_state.watchers[w] = std::move(watcher);
1809
1915
  // If we've already received an EDS update, notify the new watcher
1810
1916
  // immediately.
1811
- if (!endpoint_state.update.priority_list_update.empty()) {
1812
- w->OnEndpointChanged(endpoint_state.update);
1917
+ if (endpoint_state.update.has_value()) {
1918
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
1919
+ gpr_log(GPR_INFO, "[xds_client %p] returning cached endpoint data for %s",
1920
+ this, StringViewToCString(eds_service_name).get());
1921
+ }
1922
+ w->OnEndpointChanged(endpoint_state.update.value());
1813
1923
  }
1814
1924
  chand_->Subscribe(XdsApi::kEdsTypeUrl, eds_service_name_str);
1815
1925
  }
1816
1926
 
1817
1927
  void XdsClient::CancelEndpointDataWatch(StringView eds_service_name,
1818
- EndpointWatcherInterface* watcher) {
1928
+ EndpointWatcherInterface* watcher,
1929
+ bool delay_unsubscription) {
1819
1930
  if (shutting_down_) return;
1820
1931
  std::string eds_service_name_str = std::string(eds_service_name);
1821
1932
  EndpointState& endpoint_state = endpoint_map_[eds_service_name_str];
@@ -1824,7 +1935,8 @@ void XdsClient::CancelEndpointDataWatch(StringView eds_service_name,
1824
1935
  endpoint_state.watchers.erase(it);
1825
1936
  if (endpoint_state.watchers.empty()) {
1826
1937
  endpoint_map_.erase(eds_service_name_str);
1827
- chand_->Unsubscribe(XdsApi::kEdsTypeUrl, eds_service_name_str);
1938
+ chand_->Unsubscribe(XdsApi::kEdsTypeUrl, eds_service_name_str,
1939
+ delay_unsubscription);
1828
1940
  }
1829
1941
  }
1830
1942
  }
@@ -1859,19 +1971,14 @@ void XdsClient::RemoveClusterDropStats(
1859
1971
  LoadReportState& load_report_state = load_report_it->second;
1860
1972
  // TODO(roth): When we add support for direct federation, use the
1861
1973
  // server name specified in lrs_server.
1862
- // TODO(roth): In principle, we should try to send a final load report
1863
- // containing whatever final stats have been accumulated since the
1864
- // last load report.
1865
1974
  auto it = load_report_state.drop_stats.find(cluster_drop_stats);
1866
1975
  if (it != load_report_state.drop_stats.end()) {
1867
- load_report_state.drop_stats.erase(it);
1868
- if (load_report_state.drop_stats.empty() &&
1869
- load_report_state.locality_stats.empty()) {
1870
- load_report_map_.erase(load_report_it);
1871
- if (chand_ != nullptr && load_report_map_.empty()) {
1872
- chand_->StopLrsCall();
1873
- }
1976
+ // Record final drop stats in deleted_drop_stats, which will be
1977
+ // added to the next load report.
1978
+ for (const auto& p : cluster_drop_stats->GetSnapshotAndReset()) {
1979
+ load_report_state.deleted_drop_stats[p.first] += p.second;
1874
1980
  }
1981
+ load_report_state.drop_stats.erase(it);
1875
1982
  }
1876
1983
  }
1877
1984
 
@@ -1892,7 +1999,7 @@ RefCountedPtr<XdsClusterLocalityStats> XdsClient::AddClusterLocalityStats(
1892
1999
  Ref(DEBUG_LOCATION, "LocalityStats"), lrs_server,
1893
2000
  it->first.first /*cluster_name*/, it->first.second /*eds_service_name*/,
1894
2001
  locality);
1895
- it->second.locality_stats[std::move(locality)].insert(
2002
+ it->second.locality_stats[std::move(locality)].locality_stats.insert(
1896
2003
  cluster_locality_stats.get());
1897
2004
  chand_->MaybeStartLrsCall();
1898
2005
  return cluster_locality_stats;
@@ -1908,25 +2015,16 @@ void XdsClient::RemoveClusterLocalityStats(
1908
2015
  LoadReportState& load_report_state = load_report_it->second;
1909
2016
  // TODO(roth): When we add support for direct federation, use the
1910
2017
  // server name specified in lrs_server.
1911
- // TODO(roth): In principle, we should try to send a final load report
1912
- // containing whatever final stats have been accumulated since the
1913
- // last load report.
1914
2018
  auto locality_it = load_report_state.locality_stats.find(locality);
1915
2019
  if (locality_it == load_report_state.locality_stats.end()) return;
1916
- auto& locality_set = locality_it->second;
2020
+ auto& locality_set = locality_it->second.locality_stats;
1917
2021
  auto it = locality_set.find(cluster_locality_stats);
1918
2022
  if (it != locality_set.end()) {
2023
+ // Record final snapshot in deleted_locality_stats, which will be
2024
+ // added to the next load report.
2025
+ locality_it->second.deleted_locality_stats.emplace_back(
2026
+ cluster_locality_stats->GetSnapshotAndReset());
1919
2027
  locality_set.erase(it);
1920
- if (locality_set.empty()) {
1921
- load_report_state.locality_stats.erase(locality_it);
1922
- if (load_report_state.locality_stats.empty() &&
1923
- load_report_state.drop_stats.empty()) {
1924
- load_report_map_.erase(load_report_it);
1925
- if (chand_ != nullptr && load_report_map_.empty()) {
1926
- chand_->StopLrsCall();
1927
- }
1928
- }
1929
- }
1930
2028
  }
1931
2029
  }
1932
2030
 
@@ -1955,32 +2053,70 @@ grpc_error* XdsClient::CreateServiceConfig(
1955
2053
  return error;
1956
2054
  }
1957
2055
 
1958
- XdsApi::ClusterLoadReportMap XdsClient::BuildLoadReportSnapshot() {
2056
+ XdsApi::ClusterLoadReportMap XdsClient::BuildLoadReportSnapshot(
2057
+ const std::set<std::string>& clusters) {
1959
2058
  XdsApi::ClusterLoadReportMap snapshot_map;
1960
- for (auto& p : load_report_map_) {
1961
- const auto& cluster_key = p.first; // cluster and EDS service name
1962
- LoadReportState& load_report = p.second;
1963
- XdsApi::ClusterLoadReport& snapshot = snapshot_map[cluster_key];
2059
+ for (auto load_report_it = load_report_map_.begin();
2060
+ load_report_it != load_report_map_.end();) {
2061
+ // Cluster key is cluster and EDS service name.
2062
+ const auto& cluster_key = load_report_it->first;
2063
+ LoadReportState& load_report = load_report_it->second;
2064
+ // If the CDS response for a cluster indicates to use LRS but the
2065
+ // LRS server does not say that it wants reports for this cluster,
2066
+ // then we'll have stats objects here whose data we're not going to
2067
+ // include in the load report. However, we still need to clear out
2068
+ // the data from the stats objects, so that if the LRS server starts
2069
+ // asking for the data in the future, we don't incorrectly include
2070
+ // data from previous reporting intervals in that future report.
2071
+ const bool record_stats =
2072
+ clusters.find(cluster_key.first) != clusters.end();
2073
+ XdsApi::ClusterLoadReport snapshot;
1964
2074
  // Aggregate drop stats.
2075
+ snapshot.dropped_requests = std::move(load_report.deleted_drop_stats);
1965
2076
  for (auto& drop_stats : load_report.drop_stats) {
1966
2077
  for (const auto& p : drop_stats->GetSnapshotAndReset()) {
1967
2078
  snapshot.dropped_requests[p.first] += p.second;
1968
2079
  }
1969
2080
  }
1970
2081
  // Aggregate locality stats.
1971
- for (auto& p : load_report.locality_stats) {
1972
- XdsLocalityName* locality_name = p.first.get();
1973
- auto& locality_stats_set = p.second;
2082
+ for (auto it = load_report.locality_stats.begin();
2083
+ it != load_report.locality_stats.end();) {
2084
+ const RefCountedPtr<XdsLocalityName>& locality_name = it->first;
2085
+ auto& locality_state = it->second;
1974
2086
  XdsClusterLocalityStats::Snapshot& locality_snapshot =
1975
2087
  snapshot.locality_stats[locality_name];
1976
- for (auto& locality_stats : locality_stats_set) {
2088
+ for (auto& locality_stats : locality_state.locality_stats) {
1977
2089
  locality_snapshot += locality_stats->GetSnapshotAndReset();
1978
2090
  }
2091
+ // Add final snapshots from recently deleted locality stats objects.
2092
+ for (auto& deleted_locality_stats :
2093
+ locality_state.deleted_locality_stats) {
2094
+ locality_snapshot += deleted_locality_stats;
2095
+ }
2096
+ locality_state.deleted_locality_stats.clear();
2097
+ // If the only thing left in this entry was final snapshots from
2098
+ // deleted locality stats objects, remove the entry.
2099
+ if (locality_state.locality_stats.empty()) {
2100
+ it = load_report.locality_stats.erase(it);
2101
+ } else {
2102
+ ++it;
2103
+ }
2104
+ }
2105
+ if (record_stats) {
2106
+ // Compute load report interval.
2107
+ const grpc_millis now = ExecCtx::Get()->Now();
2108
+ snapshot.load_report_interval = now - load_report.last_report_time;
2109
+ load_report.last_report_time = now;
2110
+ // Record snapshot.
2111
+ snapshot_map[cluster_key] = std::move(snapshot);
2112
+ }
2113
+ // If the only thing left in this entry was final snapshots from
2114
+ // deleted stats objects, remove the entry.
2115
+ if (load_report.locality_stats.empty() && load_report.drop_stats.empty()) {
2116
+ load_report_it = load_report_map_.erase(load_report_it);
2117
+ } else {
2118
+ ++load_report_it;
1979
2119
  }
1980
- // Compute load report interval.
1981
- const grpc_millis now = ExecCtx::Get()->Now();
1982
- snapshot.load_report_interval = now - load_report.last_report_time;
1983
- load_report.last_report_time = now;
1984
2120
  }
1985
2121
  return snapshot_map;
1986
2122
  }