|  | @@ -22,7 +22,9 @@
 | 
	
		
			
				|  |  |  #include <numeric>
 | 
	
		
			
				|  |  |  #include <set>
 | 
	
		
			
				|  |  |  #include <sstream>
 | 
	
		
			
				|  |  | +#include <string>
 | 
	
		
			
				|  |  |  #include <thread>
 | 
	
		
			
				|  |  | +#include <vector>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include <grpc/grpc.h>
 | 
	
		
			
				|  |  |  #include <grpc/support/alloc.h>
 | 
	
	
		
			
				|  | @@ -296,8 +298,9 @@ class ClientStats {
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    // Converts from proto message class.
 | 
	
		
			
				|  |  | -  ClientStats(const ClusterStats& cluster_stats)
 | 
	
		
			
				|  |  | -      : total_dropped_requests_(cluster_stats.total_dropped_requests()) {
 | 
	
		
			
				|  |  | +  explicit ClientStats(const ClusterStats& cluster_stats)
 | 
	
		
			
				|  |  | +      : cluster_name_(cluster_stats.cluster_name()),
 | 
	
		
			
				|  |  | +        total_dropped_requests_(cluster_stats.total_dropped_requests()) {
 | 
	
		
			
				|  |  |      for (const auto& input_locality_stats :
 | 
	
		
			
				|  |  |           cluster_stats.upstream_locality_stats()) {
 | 
	
		
			
				|  |  |        locality_stats_.emplace(input_locality_stats.locality().sub_zone(),
 | 
	
	
		
			
				|  | @@ -310,6 +313,11 @@ class ClientStats {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  const std::string& cluster_name() const { return cluster_name_; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const std::map<grpc::string, LocalityStats>& locality_stats() const {
 | 
	
		
			
				|  |  | +    return locality_stats_;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    uint64_t total_successful_requests() const {
 | 
	
		
			
				|  |  |      uint64_t sum = 0;
 | 
	
		
			
				|  |  |      for (auto& p : locality_stats_) {
 | 
	
	
		
			
				|  | @@ -338,7 +346,9 @@ class ClientStats {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      return sum;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    uint64_t total_dropped_requests() const { return total_dropped_requests_; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    uint64_t dropped_requests(const grpc::string& category) const {
 | 
	
		
			
				|  |  |      auto iter = dropped_requests_.find(category);
 | 
	
		
			
				|  |  |      GPR_ASSERT(iter != dropped_requests_.end());
 | 
	
	
		
			
				|  | @@ -346,6 +356,7 @@ class ClientStats {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  | +  std::string cluster_name_;
 | 
	
		
			
				|  |  |    std::map<grpc::string, LocalityStats> locality_stats_;
 | 
	
		
			
				|  |  |    uint64_t total_dropped_requests_;
 | 
	
		
			
				|  |  |    std::map<grpc::string, uint64_t> dropped_requests_;
 | 
	
	
		
			
				|  | @@ -391,7 +402,6 @@ class AdsServiceImpl : public AggregatedDiscoveryService::Service,
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    using Stream = ServerReaderWriter<DiscoveryResponse, DiscoveryRequest>;
 | 
	
		
			
				|  |  | -  using ResponseDelayPair = std::pair<DiscoveryResponse, int>;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    // A queue of resource type/name pairs that have changed since the client
 | 
	
		
			
				|  |  |    // subscribed to them.
 | 
	
	
		
			
				|  | @@ -933,60 +943,62 @@ class LrsServiceImpl : public LrsService,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    explicit LrsServiceImpl(int client_load_reporting_interval_seconds)
 | 
	
		
			
				|  |  |        : client_load_reporting_interval_seconds_(
 | 
	
		
			
				|  |  | -            client_load_reporting_interval_seconds) {}
 | 
	
		
			
				|  |  | +            client_load_reporting_interval_seconds),
 | 
	
		
			
				|  |  | +        cluster_names_({kDefaultResourceName}) {}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    Status StreamLoadStats(ServerContext* /*context*/, Stream* stream) override {
 | 
	
		
			
				|  |  |      gpr_log(GPR_INFO, "LRS[%p]: StreamLoadStats starts", this);
 | 
	
		
			
				|  |  | +    GPR_ASSERT(client_load_reporting_interval_seconds_ > 0);
 | 
	
		
			
				|  |  |      // Take a reference of the LrsServiceImpl object, reference will go
 | 
	
		
			
				|  |  |      // out of scope after this method exits.
 | 
	
		
			
				|  |  |      std::shared_ptr<LrsServiceImpl> lrs_service_impl = shared_from_this();
 | 
	
		
			
				|  |  | -    // Read request.
 | 
	
		
			
				|  |  | +    // Read initial request.
 | 
	
		
			
				|  |  |      LoadStatsRequest request;
 | 
	
		
			
				|  |  |      if (stream->Read(&request)) {
 | 
	
		
			
				|  |  | -      if (client_load_reporting_interval_seconds_ > 0) {
 | 
	
		
			
				|  |  | -        IncreaseRequestCount();
 | 
	
		
			
				|  |  | -        // Send response.
 | 
	
		
			
				|  |  | -        LoadStatsResponse response;
 | 
	
		
			
				|  |  | -        std::string server_name;
 | 
	
		
			
				|  |  | -        auto it = request.node().metadata().fields().find(
 | 
	
		
			
				|  |  | -            "PROXYLESS_CLIENT_HOSTNAME");
 | 
	
		
			
				|  |  | -        if (it != request.node().metadata().fields().end()) {
 | 
	
		
			
				|  |  | -          server_name = it->second.string_value();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        GPR_ASSERT(server_name != "");
 | 
	
		
			
				|  |  | -        response.add_clusters(server_name);
 | 
	
		
			
				|  |  | -        response.mutable_load_reporting_interval()->set_seconds(
 | 
	
		
			
				|  |  | -            client_load_reporting_interval_seconds_);
 | 
	
		
			
				|  |  | -        stream->Write(response);
 | 
	
		
			
				|  |  | -        IncreaseResponseCount();
 | 
	
		
			
				|  |  | -        // Wait for report.
 | 
	
		
			
				|  |  | -        request.Clear();
 | 
	
		
			
				|  |  | -        if (stream->Read(&request)) {
 | 
	
		
			
				|  |  | -          gpr_log(GPR_INFO, "LRS[%p]: received client load report message '%s'",
 | 
	
		
			
				|  |  | -                  this, request.DebugString().c_str());
 | 
	
		
			
				|  |  | -          GPR_ASSERT(request.cluster_stats().size() == 1);
 | 
	
		
			
				|  |  | -          const ClusterStats& cluster_stats = request.cluster_stats()[0];
 | 
	
		
			
				|  |  | -          // We need to acquire the lock here in order to prevent the notify_one
 | 
	
		
			
				|  |  | -          // below from firing before its corresponding wait is executed.
 | 
	
		
			
				|  |  | -          grpc_core::MutexLock lock(&load_report_mu_);
 | 
	
		
			
				|  |  | -          GPR_ASSERT(client_stats_ == nullptr);
 | 
	
		
			
				|  |  | -          client_stats_.reset(new ClientStats(cluster_stats));
 | 
	
		
			
				|  |  | -          load_report_ready_ = true;
 | 
	
		
			
				|  |  | -          load_report_cond_.Signal();
 | 
	
		
			
				|  |  | +      IncreaseRequestCount();  // Only for initial request.
 | 
	
		
			
				|  |  | +      // Verify server name set in metadata.
 | 
	
		
			
				|  |  | +      auto it =
 | 
	
		
			
				|  |  | +          request.node().metadata().fields().find("PROXYLESS_CLIENT_HOSTNAME");
 | 
	
		
			
				|  |  | +      GPR_ASSERT(it != request.node().metadata().fields().end());
 | 
	
		
			
				|  |  | +      EXPECT_EQ(it->second.string_value(), kDefaultResourceName);
 | 
	
		
			
				|  |  | +      // Send initial response.
 | 
	
		
			
				|  |  | +      LoadStatsResponse response;
 | 
	
		
			
				|  |  | +      for (const std::string& cluster_name : cluster_names_) {
 | 
	
		
			
				|  |  | +        response.add_clusters(cluster_name);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      response.mutable_load_reporting_interval()->set_seconds(
 | 
	
		
			
				|  |  | +          client_load_reporting_interval_seconds_);
 | 
	
		
			
				|  |  | +      stream->Write(response);
 | 
	
		
			
				|  |  | +      IncreaseResponseCount();
 | 
	
		
			
				|  |  | +      // Wait for report.
 | 
	
		
			
				|  |  | +      request.Clear();
 | 
	
		
			
				|  |  | +      while (stream->Read(&request)) {
 | 
	
		
			
				|  |  | +        gpr_log(GPR_INFO, "LRS[%p]: received client load report message: %s",
 | 
	
		
			
				|  |  | +                this, request.DebugString().c_str());
 | 
	
		
			
				|  |  | +        std::vector<ClientStats> stats;
 | 
	
		
			
				|  |  | +        for (const auto& cluster_stats : request.cluster_stats()) {
 | 
	
		
			
				|  |  | +          stats.emplace_back(cluster_stats);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        grpc_core::MutexLock lock(&load_report_mu_);
 | 
	
		
			
				|  |  | +        result_queue_.emplace_back(std::move(stats));
 | 
	
		
			
				|  |  | +        if (load_report_cond_ != nullptr) load_report_cond_->Signal();
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        // Wait until notified done.
 | 
	
		
			
				|  |  |        grpc_core::MutexLock lock(&lrs_mu_);
 | 
	
		
			
				|  |  | -      lrs_cv_.WaitUntil(&lrs_mu_, [this] { return lrs_done; });
 | 
	
		
			
				|  |  | +      lrs_cv_.WaitUntil(&lrs_mu_, [this] { return lrs_done_; });
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      gpr_log(GPR_INFO, "LRS[%p]: StreamLoadStats done", this);
 | 
	
		
			
				|  |  |      return Status::OK;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // Must be called before the LRS call is started.
 | 
	
		
			
				|  |  | +  void set_cluster_names(const std::set<std::string>& cluster_names) {
 | 
	
		
			
				|  |  | +    cluster_names_ = cluster_names;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    void Start() {
 | 
	
		
			
				|  |  | -    lrs_done = false;
 | 
	
		
			
				|  |  | -    load_report_ready_ = false;
 | 
	
		
			
				|  |  | -    client_stats_.reset();
 | 
	
		
			
				|  |  | +    lrs_done_ = false;
 | 
	
		
			
				|  |  | +    result_queue_.clear();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    void Shutdown() {
 | 
	
	
		
			
				|  | @@ -997,12 +1009,18 @@ class LrsServiceImpl : public LrsService,
 | 
	
		
			
				|  |  |      gpr_log(GPR_INFO, "LRS[%p]: shut down", this);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  ClientStats* WaitForLoadReport() {
 | 
	
		
			
				|  |  | +  std::vector<ClientStats> WaitForLoadReport() {
 | 
	
		
			
				|  |  |      grpc_core::MutexLock lock(&load_report_mu_);
 | 
	
		
			
				|  |  | -    load_report_cond_.WaitUntil(&load_report_mu_,
 | 
	
		
			
				|  |  | -                                [this] { return load_report_ready_; });
 | 
	
		
			
				|  |  | -    load_report_ready_ = false;
 | 
	
		
			
				|  |  | -    return client_stats_.get();
 | 
	
		
			
				|  |  | +    grpc_core::CondVar cv;
 | 
	
		
			
				|  |  | +    if (result_queue_.empty()) {
 | 
	
		
			
				|  |  | +      load_report_cond_ = &cv;
 | 
	
		
			
				|  |  | +      load_report_cond_->WaitUntil(&load_report_mu_,
 | 
	
		
			
				|  |  | +                                   [this] { return !result_queue_.empty(); });
 | 
	
		
			
				|  |  | +      load_report_cond_ = nullptr;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    std::vector<ClientStats> result = std::move(result_queue_.front());
 | 
	
		
			
				|  |  | +    result_queue_.pop_front();
 | 
	
		
			
				|  |  | +    return result;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    void NotifyDoneWithLrsCall() {
 | 
	
	
		
			
				|  | @@ -1010,26 +1028,24 @@ class LrsServiceImpl : public LrsService,
 | 
	
		
			
				|  |  |      NotifyDoneWithLrsCallLocked();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  |    void NotifyDoneWithLrsCallLocked() {
 | 
	
		
			
				|  |  | -    if (!lrs_done) {
 | 
	
		
			
				|  |  | -      lrs_done = true;
 | 
	
		
			
				|  |  | +    if (!lrs_done_) {
 | 
	
		
			
				|  |  | +      lrs_done_ = true;
 | 
	
		
			
				|  |  |        lrs_cv_.Broadcast();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | - private:
 | 
	
		
			
				|  |  |    const int client_load_reporting_interval_seconds_;
 | 
	
		
			
				|  |  | +  std::set<std::string> cluster_names_;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    grpc_core::CondVar lrs_cv_;
 | 
	
		
			
				|  |  | -  // Protect lrs_done.
 | 
	
		
			
				|  |  | -  grpc_core::Mutex lrs_mu_;
 | 
	
		
			
				|  |  | -  bool lrs_done = false;
 | 
	
		
			
				|  |  | +  grpc_core::Mutex lrs_mu_;  // Protects lrs_done_.
 | 
	
		
			
				|  |  | +  bool lrs_done_ = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  grpc_core::CondVar load_report_cond_;
 | 
	
		
			
				|  |  | -  // Protect the members below.
 | 
	
		
			
				|  |  | -  grpc_core::Mutex load_report_mu_;
 | 
	
		
			
				|  |  | -  std::unique_ptr<ClientStats> client_stats_;
 | 
	
		
			
				|  |  | -  bool load_report_ready_ = false;
 | 
	
		
			
				|  |  | +  grpc_core::Mutex load_report_mu_;  // Protects the members below.
 | 
	
		
			
				|  |  | +  grpc_core::CondVar* load_report_cond_ = nullptr;
 | 
	
		
			
				|  |  | +  std::deque<std::vector<ClientStats>> result_queue_;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class TestType {
 | 
	
	
		
			
				|  | @@ -1720,6 +1736,141 @@ TEST_P(XdsResolverOnlyTest, ClusterRemoved) {
 | 
	
		
			
				|  |  |              AdsServiceImpl::ACKED);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +class XdsResolverLoadReportingOnlyTest : public XdsEnd2endTest {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  XdsResolverLoadReportingOnlyTest() : XdsEnd2endTest(4, 1, 3) {}
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Tests load reporting when switching over from one cluster to another.
 | 
	
		
			
				|  |  | +TEST_P(XdsResolverLoadReportingOnlyTest, ChangeClusters) {
 | 
	
		
			
				|  |  | +  const char* kNewClusterName = "new_cluster_name";
 | 
	
		
			
				|  |  | +  balancers_[0]->lrs_service()->set_cluster_names(
 | 
	
		
			
				|  |  | +      {kDefaultResourceName, kNewClusterName});
 | 
	
		
			
				|  |  | +  SetNextResolution({});
 | 
	
		
			
				|  |  | +  SetNextResolutionForLbChannelAllBalancers();
 | 
	
		
			
				|  |  | +  // cluster kDefaultResourceName -> locality0 -> backends 0 and 1
 | 
	
		
			
				|  |  | +  AdsServiceImpl::EdsResourceArgs args({
 | 
	
		
			
				|  |  | +      {"locality0", GetBackendPorts(0, 2)},
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +  balancers_[0]->ads_service()->SetEdsResource(
 | 
	
		
			
				|  |  | +      AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
 | 
	
		
			
				|  |  | +  // cluster kNewClusterName -> locality1 -> backends 2 and 3
 | 
	
		
			
				|  |  | +  AdsServiceImpl::EdsResourceArgs args2({
 | 
	
		
			
				|  |  | +      {"locality1", GetBackendPorts(2, 4)},
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +  balancers_[0]->ads_service()->SetEdsResource(
 | 
	
		
			
				|  |  | +      AdsServiceImpl::BuildEdsResource(args2, kNewClusterName),
 | 
	
		
			
				|  |  | +      kNewClusterName);
 | 
	
		
			
				|  |  | +  // CDS resource for kNewClusterName.
 | 
	
		
			
				|  |  | +  Cluster new_cluster = balancers_[0]->ads_service()->default_cluster();
 | 
	
		
			
				|  |  | +  new_cluster.set_name(kNewClusterName);
 | 
	
		
			
				|  |  | +  balancers_[0]->ads_service()->SetCdsResource(new_cluster, kNewClusterName);
 | 
	
		
			
				|  |  | +  // Wait for all backends to come online.
 | 
	
		
			
				|  |  | +  int num_ok = 0;
 | 
	
		
			
				|  |  | +  int num_failure = 0;
 | 
	
		
			
				|  |  | +  int num_drops = 0;
 | 
	
		
			
				|  |  | +  std::tie(num_ok, num_failure, num_drops) = WaitForAllBackends(0, 2);
 | 
	
		
			
				|  |  | +  // The load report received at the balancer should be correct.
 | 
	
		
			
				|  |  | +  std::vector<ClientStats> load_report =
 | 
	
		
			
				|  |  | +      balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | +  EXPECT_THAT(
 | 
	
		
			
				|  |  | +      load_report,
 | 
	
		
			
				|  |  | +      ::testing::ElementsAre(::testing::AllOf(
 | 
	
		
			
				|  |  | +          ::testing::Property(&ClientStats::cluster_name, kDefaultResourceName),
 | 
	
		
			
				|  |  | +          ::testing::Property(
 | 
	
		
			
				|  |  | +              &ClientStats::locality_stats,
 | 
	
		
			
				|  |  | +              ::testing::ElementsAre(::testing::Pair(
 | 
	
		
			
				|  |  | +                  "locality0",
 | 
	
		
			
				|  |  | +                  ::testing::AllOf(
 | 
	
		
			
				|  |  | +                      ::testing::Field(&ClientStats::LocalityStats::
 | 
	
		
			
				|  |  | +                                           total_successful_requests,
 | 
	
		
			
				|  |  | +                                       num_ok),
 | 
	
		
			
				|  |  | +                      ::testing::Field(&ClientStats::LocalityStats::
 | 
	
		
			
				|  |  | +                                           total_requests_in_progress,
 | 
	
		
			
				|  |  | +                                       0UL),
 | 
	
		
			
				|  |  | +                      ::testing::Field(
 | 
	
		
			
				|  |  | +                          &ClientStats::LocalityStats::total_error_requests,
 | 
	
		
			
				|  |  | +                          num_failure),
 | 
	
		
			
				|  |  | +                      ::testing::Field(
 | 
	
		
			
				|  |  | +                          &ClientStats::LocalityStats::total_issued_requests,
 | 
	
		
			
				|  |  | +                          num_failure + num_ok))))),
 | 
	
		
			
				|  |  | +          ::testing::Property(&ClientStats::total_dropped_requests,
 | 
	
		
			
				|  |  | +                              num_drops))));
 | 
	
		
			
				|  |  | +  // Change RDS resource to point to new cluster.
 | 
	
		
			
				|  |  | +  RouteConfiguration new_route_config =
 | 
	
		
			
				|  |  | +      balancers_[0]->ads_service()->default_route_config();
 | 
	
		
			
				|  |  | +  new_route_config.mutable_virtual_hosts(0)
 | 
	
		
			
				|  |  | +      ->mutable_routes(0)
 | 
	
		
			
				|  |  | +      ->mutable_route()
 | 
	
		
			
				|  |  | +      ->set_cluster(kNewClusterName);
 | 
	
		
			
				|  |  | +  Listener listener =
 | 
	
		
			
				|  |  | +      balancers_[0]->ads_service()->BuildListener(new_route_config);
 | 
	
		
			
				|  |  | +  balancers_[0]->ads_service()->SetLdsResource(listener, kDefaultResourceName);
 | 
	
		
			
				|  |  | +  // Wait for all new backends to be used.
 | 
	
		
			
				|  |  | +  std::tie(num_ok, num_failure, num_drops) = WaitForAllBackends(2, 4);
 | 
	
		
			
				|  |  | +  // The load report received at the balancer should be correct.
 | 
	
		
			
				|  |  | +  load_report = balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | +  EXPECT_THAT(
 | 
	
		
			
				|  |  | +      load_report,
 | 
	
		
			
				|  |  | +      ::testing::ElementsAre(
 | 
	
		
			
				|  |  | +          ::testing::AllOf(
 | 
	
		
			
				|  |  | +              ::testing::Property(&ClientStats::cluster_name,
 | 
	
		
			
				|  |  | +                                  kDefaultResourceName),
 | 
	
		
			
				|  |  | +              ::testing::Property(
 | 
	
		
			
				|  |  | +                  &ClientStats::locality_stats,
 | 
	
		
			
				|  |  | +                  ::testing::ElementsAre(::testing::Pair(
 | 
	
		
			
				|  |  | +                      "locality0",
 | 
	
		
			
				|  |  | +                      ::testing::AllOf(
 | 
	
		
			
				|  |  | +                          ::testing::Field(&ClientStats::LocalityStats::
 | 
	
		
			
				|  |  | +                                               total_successful_requests,
 | 
	
		
			
				|  |  | +                                           ::testing::Lt(num_ok)),
 | 
	
		
			
				|  |  | +                          ::testing::Field(&ClientStats::LocalityStats::
 | 
	
		
			
				|  |  | +                                               total_requests_in_progress,
 | 
	
		
			
				|  |  | +                                           0UL),
 | 
	
		
			
				|  |  | +                          ::testing::Field(
 | 
	
		
			
				|  |  | +                              &ClientStats::LocalityStats::total_error_requests,
 | 
	
		
			
				|  |  | +                              ::testing::Le(num_failure)),
 | 
	
		
			
				|  |  | +                          ::testing::Field(
 | 
	
		
			
				|  |  | +                              &ClientStats::LocalityStats::
 | 
	
		
			
				|  |  | +                                  total_issued_requests,
 | 
	
		
			
				|  |  | +                              ::testing::Le(num_failure + num_ok)))))),
 | 
	
		
			
				|  |  | +              ::testing::Property(&ClientStats::total_dropped_requests,
 | 
	
		
			
				|  |  | +                                  num_drops)),
 | 
	
		
			
				|  |  | +          ::testing::AllOf(
 | 
	
		
			
				|  |  | +              ::testing::Property(&ClientStats::cluster_name, kNewClusterName),
 | 
	
		
			
				|  |  | +              ::testing::Property(
 | 
	
		
			
				|  |  | +                  &ClientStats::locality_stats,
 | 
	
		
			
				|  |  | +                  ::testing::ElementsAre(::testing::Pair(
 | 
	
		
			
				|  |  | +                      "locality1",
 | 
	
		
			
				|  |  | +                      ::testing::AllOf(
 | 
	
		
			
				|  |  | +                          ::testing::Field(&ClientStats::LocalityStats::
 | 
	
		
			
				|  |  | +                                               total_successful_requests,
 | 
	
		
			
				|  |  | +                                           ::testing::Le(num_ok)),
 | 
	
		
			
				|  |  | +                          ::testing::Field(&ClientStats::LocalityStats::
 | 
	
		
			
				|  |  | +                                               total_requests_in_progress,
 | 
	
		
			
				|  |  | +                                           0UL),
 | 
	
		
			
				|  |  | +                          ::testing::Field(
 | 
	
		
			
				|  |  | +                              &ClientStats::LocalityStats::total_error_requests,
 | 
	
		
			
				|  |  | +                              ::testing::Le(num_failure)),
 | 
	
		
			
				|  |  | +                          ::testing::Field(
 | 
	
		
			
				|  |  | +                              &ClientStats::LocalityStats::
 | 
	
		
			
				|  |  | +                                  total_issued_requests,
 | 
	
		
			
				|  |  | +                              ::testing::Le(num_failure + num_ok)))))),
 | 
	
		
			
				|  |  | +              ::testing::Property(&ClientStats::total_dropped_requests,
 | 
	
		
			
				|  |  | +                                  num_drops))));
 | 
	
		
			
				|  |  | +  int total_ok = 0;
 | 
	
		
			
				|  |  | +  int total_failure = 0;
 | 
	
		
			
				|  |  | +  for (const ClientStats& client_stats : load_report) {
 | 
	
		
			
				|  |  | +    total_ok += client_stats.total_successful_requests();
 | 
	
		
			
				|  |  | +    total_failure += client_stats.total_error_requests();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  EXPECT_EQ(total_ok, num_ok);
 | 
	
		
			
				|  |  | +  EXPECT_EQ(total_failure, num_failure);
 | 
	
		
			
				|  |  | +  // The LRS service got a single request, and sent a single response.
 | 
	
		
			
				|  |  | +  EXPECT_EQ(1U, balancers_[0]->lrs_service()->request_count());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(1U, balancers_[0]->lrs_service()->response_count());
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  using SecureNamingTest = BasicTest;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Tests that secure naming check passes if target name is expected.
 | 
	
	
		
			
				|  | @@ -3227,14 +3378,50 @@ TEST_P(ClientLoadReportingTest, Vanilla) {
 | 
	
		
			
				|  |  |    EXPECT_EQ(1U, balancers_[0]->lrs_service()->request_count());
 | 
	
		
			
				|  |  |    EXPECT_EQ(1U, balancers_[0]->lrs_service()->response_count());
 | 
	
		
			
				|  |  |    // The load report received at the balancer should be correct.
 | 
	
		
			
				|  |  | -  ClientStats* client_stats = balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | +  std::vector<ClientStats> load_report =
 | 
	
		
			
				|  |  | +      balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | +  ASSERT_EQ(load_report.size(), 1UL);
 | 
	
		
			
				|  |  | +  ClientStats& client_stats = load_report.front();
 | 
	
		
			
				|  |  |    EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + num_ok,
 | 
	
		
			
				|  |  | -            client_stats->total_successful_requests());
 | 
	
		
			
				|  |  | -  EXPECT_EQ(0U, client_stats->total_requests_in_progress());
 | 
	
		
			
				|  |  | +            client_stats.total_successful_requests());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(0U, client_stats.total_requests_in_progress());
 | 
	
		
			
				|  |  |    EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + num_ok,
 | 
	
		
			
				|  |  | -            client_stats->total_issued_requests());
 | 
	
		
			
				|  |  | -  EXPECT_EQ(0U, client_stats->total_error_requests());
 | 
	
		
			
				|  |  | -  EXPECT_EQ(0U, client_stats->total_dropped_requests());
 | 
	
		
			
				|  |  | +            client_stats.total_issued_requests());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(0U, client_stats.total_error_requests());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(0U, client_stats.total_dropped_requests());
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Tests that we don't include stats for clusters that are not requested
 | 
	
		
			
				|  |  | +// by the LRS server.
 | 
	
		
			
				|  |  | +TEST_P(ClientLoadReportingTest, HonorsClustersRequestedByLrsServer) {
 | 
	
		
			
				|  |  | +  balancers_[0]->lrs_service()->set_cluster_names({"bogus"});
 | 
	
		
			
				|  |  | +  SetNextResolution({});
 | 
	
		
			
				|  |  | +  SetNextResolutionForLbChannel({balancers_[0]->port()});
 | 
	
		
			
				|  |  | +  const size_t kNumRpcsPerAddress = 100;
 | 
	
		
			
				|  |  | +  AdsServiceImpl::EdsResourceArgs args({
 | 
	
		
			
				|  |  | +      {"locality0", GetBackendPorts()},
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +  balancers_[0]->ads_service()->SetEdsResource(
 | 
	
		
			
				|  |  | +      AdsServiceImpl::BuildEdsResource(args), kDefaultResourceName);
 | 
	
		
			
				|  |  | +  // Wait until all backends are ready.
 | 
	
		
			
				|  |  | +  int num_ok = 0;
 | 
	
		
			
				|  |  | +  int num_failure = 0;
 | 
	
		
			
				|  |  | +  int num_drops = 0;
 | 
	
		
			
				|  |  | +  std::tie(num_ok, num_failure, num_drops) = WaitForAllBackends();
 | 
	
		
			
				|  |  | +  // Send kNumRpcsPerAddress RPCs per server.
 | 
	
		
			
				|  |  | +  CheckRpcSendOk(kNumRpcsPerAddress * num_backends_);
 | 
	
		
			
				|  |  | +  // Each backend should have gotten 100 requests.
 | 
	
		
			
				|  |  | +  for (size_t i = 0; i < backends_.size(); ++i) {
 | 
	
		
			
				|  |  | +    EXPECT_EQ(kNumRpcsPerAddress,
 | 
	
		
			
				|  |  | +              backends_[i]->backend_service()->request_count());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // The LRS service got a single request, and sent a single response.
 | 
	
		
			
				|  |  | +  EXPECT_EQ(1U, balancers_[0]->lrs_service()->request_count());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(1U, balancers_[0]->lrs_service()->response_count());
 | 
	
		
			
				|  |  | +  // The load report received at the balancer should be correct.
 | 
	
		
			
				|  |  | +  std::vector<ClientStats> load_report =
 | 
	
		
			
				|  |  | +      balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | +  ASSERT_EQ(load_report.size(), 0UL);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Tests that if the balancer restarts, the client load report contains the
 | 
	
	
		
			
				|  | @@ -3257,12 +3444,15 @@ TEST_P(ClientLoadReportingTest, BalancerRestart) {
 | 
	
		
			
				|  |  |    std::tie(num_ok, num_failure, num_drops) =
 | 
	
		
			
				|  |  |        WaitForAllBackends(/* start_index */ 0,
 | 
	
		
			
				|  |  |                           /* stop_index */ kNumBackendsFirstPass);
 | 
	
		
			
				|  |  | -  ClientStats* client_stats = balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | +  std::vector<ClientStats> load_report =
 | 
	
		
			
				|  |  | +      balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | +  ASSERT_EQ(load_report.size(), 1UL);
 | 
	
		
			
				|  |  | +  ClientStats client_stats = std::move(load_report.front());
 | 
	
		
			
				|  |  |    EXPECT_EQ(static_cast<size_t>(num_ok),
 | 
	
		
			
				|  |  | -            client_stats->total_successful_requests());
 | 
	
		
			
				|  |  | -  EXPECT_EQ(0U, client_stats->total_requests_in_progress());
 | 
	
		
			
				|  |  | -  EXPECT_EQ(0U, client_stats->total_error_requests());
 | 
	
		
			
				|  |  | -  EXPECT_EQ(0U, client_stats->total_dropped_requests());
 | 
	
		
			
				|  |  | +            client_stats.total_successful_requests());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(0U, client_stats.total_requests_in_progress());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(0U, client_stats.total_error_requests());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(0U, client_stats.total_dropped_requests());
 | 
	
		
			
				|  |  |    // Shut down the balancer.
 | 
	
		
			
				|  |  |    balancers_[0]->Shutdown();
 | 
	
		
			
				|  |  |    // We should continue using the last EDS response we received from the
 | 
	
	
		
			
				|  | @@ -3294,11 +3484,13 @@ TEST_P(ClientLoadReportingTest, BalancerRestart) {
 | 
	
		
			
				|  |  |    CheckRpcSendOk(kNumBackendsSecondPass);
 | 
	
		
			
				|  |  |    num_started += kNumBackendsSecondPass;
 | 
	
		
			
				|  |  |    // Check client stats.
 | 
	
		
			
				|  |  | -  client_stats = balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | -  EXPECT_EQ(num_started, client_stats->total_successful_requests());
 | 
	
		
			
				|  |  | -  EXPECT_EQ(0U, client_stats->total_requests_in_progress());
 | 
	
		
			
				|  |  | -  EXPECT_EQ(0U, client_stats->total_error_requests());
 | 
	
		
			
				|  |  | -  EXPECT_EQ(0U, client_stats->total_dropped_requests());
 | 
	
		
			
				|  |  | +  load_report = balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | +  ASSERT_EQ(load_report.size(), 1UL);
 | 
	
		
			
				|  |  | +  client_stats = std::move(load_report.front());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(num_started, client_stats.total_successful_requests());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(0U, client_stats.total_requests_in_progress());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(0U, client_stats.total_error_requests());
 | 
	
		
			
				|  |  | +  EXPECT_EQ(0U, client_stats.total_dropped_requests());
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class ClientLoadReportingWithDropTest : public XdsEnd2endTest {
 | 
	
	
		
			
				|  | @@ -3352,15 +3544,18 @@ TEST_P(ClientLoadReportingWithDropTest, Vanilla) {
 | 
	
		
			
				|  |  |            ::testing::Ge(KDropRateForLbAndThrottle * (1 - kErrorTolerance)),
 | 
	
		
			
				|  |  |            ::testing::Le(KDropRateForLbAndThrottle * (1 + kErrorTolerance))));
 | 
	
		
			
				|  |  |    // Check client stats.
 | 
	
		
			
				|  |  | -  ClientStats* client_stats = balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | -  EXPECT_EQ(num_drops, client_stats->total_dropped_requests());
 | 
	
		
			
				|  |  | +  std::vector<ClientStats> load_report =
 | 
	
		
			
				|  |  | +      balancers_[0]->lrs_service()->WaitForLoadReport();
 | 
	
		
			
				|  |  | +  ASSERT_EQ(load_report.size(), 1UL);
 | 
	
		
			
				|  |  | +  ClientStats& client_stats = load_report.front();
 | 
	
		
			
				|  |  | +  EXPECT_EQ(num_drops, client_stats.total_dropped_requests());
 | 
	
		
			
				|  |  |    const size_t total_rpc = num_warmup + kNumRpcs;
 | 
	
		
			
				|  |  |    EXPECT_THAT(
 | 
	
		
			
				|  |  | -      client_stats->dropped_requests(kLbDropType),
 | 
	
		
			
				|  |  | +      client_stats.dropped_requests(kLbDropType),
 | 
	
		
			
				|  |  |        ::testing::AllOf(
 | 
	
		
			
				|  |  |            ::testing::Ge(total_rpc * kDropRateForLb * (1 - kErrorTolerance)),
 | 
	
		
			
				|  |  |            ::testing::Le(total_rpc * kDropRateForLb * (1 + kErrorTolerance))));
 | 
	
		
			
				|  |  | -  EXPECT_THAT(client_stats->dropped_requests(kThrottleDropType),
 | 
	
		
			
				|  |  | +  EXPECT_THAT(client_stats.dropped_requests(kThrottleDropType),
 | 
	
		
			
				|  |  |                ::testing::AllOf(
 | 
	
		
			
				|  |  |                    ::testing::Ge(total_rpc * (1 - kDropRateForLb) *
 | 
	
		
			
				|  |  |                                  kDropRateForThrottle * (1 - kErrorTolerance)),
 | 
	
	
		
			
				|  | @@ -3417,6 +3612,11 @@ INSTANTIATE_TEST_SUITE_P(XdsTest, XdsResolverOnlyTest,
 | 
	
		
			
				|  |  |                                             TestType(true, true)),
 | 
	
		
			
				|  |  |                           &TestTypeName);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// XdsResolverLoadReprtingOnlyTest depends on XdsResolver and load reporting.
 | 
	
		
			
				|  |  | +INSTANTIATE_TEST_SUITE_P(XdsTest, XdsResolverLoadReportingOnlyTest,
 | 
	
		
			
				|  |  | +                         ::testing::Values(TestType(true, true)),
 | 
	
		
			
				|  |  | +                         &TestTypeName);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  INSTANTIATE_TEST_SUITE_P(XdsTest, LocalityMapTest,
 | 
	
		
			
				|  |  |                           ::testing::Values(TestType(false, true),
 | 
	
		
			
				|  |  |                                             TestType(false, false),
 |