blob: b300fc59256e1a0f2740a72f9727d1f320895b65 [file] [log] [blame]
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H
#define GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H
#include <grpc/support/port_platform.h>
#include <map>
#include <string>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/atomic.h"
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/exec_ctx.h"
namespace grpc_core {
// Forward declaration to avoid circular dependency.
class XdsClient;
// Locality name.
class XdsLocalityName : public RefCounted<XdsLocalityName> {
public:
struct Less {
bool operator()(const XdsLocalityName* lhs,
const XdsLocalityName* rhs) const {
if (lhs == nullptr || rhs == nullptr) return GPR_ICMP(lhs, rhs);
return lhs->Compare(*rhs) < 0;
}
bool operator()(const RefCountedPtr<XdsLocalityName>& lhs,
const RefCountedPtr<XdsLocalityName>& rhs) const {
return (*this)(lhs.get(), rhs.get());
}
};
XdsLocalityName(std::string region, std::string zone, std::string sub_zone)
: region_(std::move(region)),
zone_(std::move(zone)),
sub_zone_(std::move(sub_zone)) {}
bool operator==(const XdsLocalityName& other) const {
return region_ == other.region_ && zone_ == other.zone_ &&
sub_zone_ == other.sub_zone_;
}
bool operator!=(const XdsLocalityName& other) const {
return !(*this == other);
}
int Compare(const XdsLocalityName& other) const {
int cmp_result = region_.compare(other.region_);
if (cmp_result != 0) return cmp_result;
cmp_result = zone_.compare(other.zone_);
if (cmp_result != 0) return cmp_result;
return sub_zone_.compare(other.sub_zone_);
}
const std::string& region() const { return region_; }
const std::string& zone() const { return zone_; }
const std::string& sub_zone() const { return sub_zone_; }
const std::string& AsHumanReadableString() {
if (human_readable_string_.empty()) {
human_readable_string_ =
absl::StrFormat("{region=\"%s\", zone=\"%s\", sub_zone=\"%s\"}",
region_, zone_, sub_zone_);
}
return human_readable_string_;
}
private:
std::string region_;
std::string zone_;
std::string sub_zone_;
std::string human_readable_string_;
};
// Drop stats for an xds cluster.
class XdsClusterDropStats : public RefCounted<XdsClusterDropStats> {
public:
// The total number of requests dropped for any reason is the sum of
// uncategorized_drops, and dropped_requests map.
using CategorizedDropsMap = std::map<std::string /* category */, uint64_t>;
struct Snapshot {
uint64_t uncategorized_drops = 0;
// The number of requests dropped for the specific drop categories
// outlined in the drop_overloads field in the EDS response.
CategorizedDropsMap categorized_drops;
Snapshot& operator+=(const Snapshot& other) {
uncategorized_drops += other.uncategorized_drops;
for (const auto& p : other.categorized_drops) {
categorized_drops[p.first] += p.second;
}
return *this;
}
bool IsZero() const {
if (uncategorized_drops != 0) return false;
for (const auto& p : categorized_drops) {
if (p.second != 0) return false;
}
return true;
}
};
XdsClusterDropStats(RefCountedPtr<XdsClient> xds_client,
absl::string_view lrs_server_name,
absl::string_view cluster_name,
absl::string_view eds_service_name);
~XdsClusterDropStats() override;
// Returns a snapshot of this instance and resets all the counters.
Snapshot GetSnapshotAndReset();
void AddUncategorizedDrops();
void AddCallDropped(const std::string& category);
private:
RefCountedPtr<XdsClient> xds_client_;
absl::string_view lrs_server_name_;
absl::string_view cluster_name_;
absl::string_view eds_service_name_;
Atomic<uint64_t> uncategorized_drops_{0};
// Protects categorized_drops_. A mutex is necessary because the length of
// dropped_requests can be accessed by both the picker (from data plane
// mutex) and the load reporting thread (from the control plane combiner).
Mutex mu_;
CategorizedDropsMap categorized_drops_ ABSL_GUARDED_BY(mu_);
};
// Locality stats for an xds cluster.
class XdsClusterLocalityStats : public RefCounted<XdsClusterLocalityStats> {
public:
struct BackendMetric {
uint64_t num_requests_finished_with_metric;
double total_metric_value;
BackendMetric& operator+=(const BackendMetric& other) {
num_requests_finished_with_metric +=
other.num_requests_finished_with_metric;
total_metric_value += other.total_metric_value;
return *this;
}
bool IsZero() const {
return num_requests_finished_with_metric == 0 && total_metric_value == 0;
}
};
struct Snapshot {
uint64_t total_successful_requests;
uint64_t total_requests_in_progress;
uint64_t total_error_requests;
uint64_t total_issued_requests;
std::map<std::string, BackendMetric> backend_metrics;
Snapshot& operator+=(const Snapshot& other) {
total_successful_requests += other.total_successful_requests;
total_requests_in_progress += other.total_requests_in_progress;
total_error_requests += other.total_error_requests;
total_issued_requests += other.total_issued_requests;
for (const auto& p : other.backend_metrics) {
backend_metrics[p.first] += p.second;
}
return *this;
}
bool IsZero() const {
if (total_successful_requests != 0 || total_requests_in_progress != 0 ||
total_error_requests != 0 || total_issued_requests != 0) {
return false;
}
for (const auto& p : backend_metrics) {
if (!p.second.IsZero()) return false;
}
return true;
}
};
XdsClusterLocalityStats(RefCountedPtr<XdsClient> xds_client,
absl::string_view lrs_server_name,
absl::string_view cluster_name,
absl::string_view eds_service_name,
RefCountedPtr<XdsLocalityName> name);
~XdsClusterLocalityStats() override;
// Returns a snapshot of this instance and resets all the counters.
Snapshot GetSnapshotAndReset();
void AddCallStarted();
void AddCallFinished(bool fail = false);
private:
RefCountedPtr<XdsClient> xds_client_;
absl::string_view lrs_server_name_;
absl::string_view cluster_name_;
absl::string_view eds_service_name_;
RefCountedPtr<XdsLocalityName> name_;
Atomic<uint64_t> total_successful_requests_{0};
Atomic<uint64_t> total_requests_in_progress_{0};
Atomic<uint64_t> total_error_requests_{0};
Atomic<uint64_t> total_issued_requests_{0};
// Protects backend_metrics_. A mutex is necessary because the length of
// backend_metrics_ can be accessed by both the callback intercepting the
// call's recv_trailing_metadata (not from the control plane work serializer)
// and the load reporting thread (from the control plane work serializer).
Mutex backend_metrics_mu_;
std::map<std::string, BackendMetric> backend_metrics_
ABSL_GUARDED_BY(backend_metrics_mu_);
};
} // namespace grpc_core
#endif /* GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H */