3 * Copyright 2018 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #ifndef GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H
20 #define GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H
22 #include <grpc/support/port_platform.h>
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/string_view.h"
31 #include "src/core/lib/gpr/useful.h"
32 #include "src/core/lib/gprpp/atomic.h"
33 #include "src/core/lib/gprpp/memory.h"
34 #include "src/core/lib/gprpp/ref_counted.h"
35 #include "src/core/lib/gprpp/sync.h"
36 #include "src/core/lib/iomgr/exec_ctx.h"
40 // Forward declaration to avoid circular dependency.
44 class XdsLocalityName : public RefCounted<XdsLocalityName> {
47 bool operator()(const XdsLocalityName* lhs,
48 const XdsLocalityName* rhs) const {
49 if (lhs == nullptr || rhs == nullptr) return GPR_ICMP(lhs, rhs);
50 return lhs->Compare(*rhs) < 0;
53 bool operator()(const RefCountedPtr<XdsLocalityName>& lhs,
54 const RefCountedPtr<XdsLocalityName>& rhs) const {
55 return (*this)(lhs.get(), rhs.get());
59 XdsLocalityName(std::string region, std::string zone, std::string sub_zone)
60 : region_(std::move(region)),
61 zone_(std::move(zone)),
62 sub_zone_(std::move(sub_zone)) {}
64 bool operator==(const XdsLocalityName& other) const {
65 return region_ == other.region_ && zone_ == other.zone_ &&
66 sub_zone_ == other.sub_zone_;
69 bool operator!=(const XdsLocalityName& other) const {
70 return !(*this == other);
73 int Compare(const XdsLocalityName& other) const {
74 int cmp_result = region_.compare(other.region_);
75 if (cmp_result != 0) return cmp_result;
76 cmp_result = zone_.compare(other.zone_);
77 if (cmp_result != 0) return cmp_result;
78 return sub_zone_.compare(other.sub_zone_);
81 const std::string& region() const { return region_; }
82 const std::string& zone() const { return zone_; }
83 const std::string& sub_zone() const { return sub_zone_; }
85 const std::string& AsHumanReadableString() {
86 if (human_readable_string_.empty()) {
87 human_readable_string_ =
88 absl::StrFormat("{region=\"%s\", zone=\"%s\", sub_zone=\"%s\"}",
89 region_, zone_, sub_zone_);
91 return human_readable_string_;
97 std::string sub_zone_;
98 std::string human_readable_string_;
101 // Drop stats for an xds cluster.
102 class XdsClusterDropStats : public RefCounted<XdsClusterDropStats> {
104 // The total number of requests dropped for any reason is the sum of
105 // uncategorized_drops, and dropped_requests map.
106 using CategorizedDropsMap = std::map<std::string /* category */, uint64_t>;
108 uint64_t uncategorized_drops = 0;
109 // The number of requests dropped for the specific drop categories
110 // outlined in the drop_overloads field in the EDS response.
111 CategorizedDropsMap categorized_drops;
113 Snapshot& operator+=(const Snapshot& other) {
114 uncategorized_drops += other.uncategorized_drops;
115 for (const auto& p : other.categorized_drops) {
116 categorized_drops[p.first] += p.second;
121 bool IsZero() const {
122 if (uncategorized_drops != 0) return false;
123 for (const auto& p : categorized_drops) {
124 if (p.second != 0) return false;
130 XdsClusterDropStats(RefCountedPtr<XdsClient> xds_client,
131 absl::string_view lrs_server_name,
132 absl::string_view cluster_name,
133 absl::string_view eds_service_name);
134 ~XdsClusterDropStats() override;
136 // Returns a snapshot of this instance and resets all the counters.
137 Snapshot GetSnapshotAndReset();
139 void AddUncategorizedDrops();
140 void AddCallDropped(const std::string& category);
143 RefCountedPtr<XdsClient> xds_client_;
144 absl::string_view lrs_server_name_;
145 absl::string_view cluster_name_;
146 absl::string_view eds_service_name_;
147 Atomic<uint64_t> uncategorized_drops_{0};
148 // Protects categorized_drops_. A mutex is necessary because the length of
149 // dropped_requests can be accessed by both the picker (from data plane
150 // mutex) and the load reporting thread (from the control plane combiner).
152 CategorizedDropsMap categorized_drops_ ABSL_GUARDED_BY(mu_);
155 // Locality stats for an xds cluster.
156 class XdsClusterLocalityStats : public RefCounted<XdsClusterLocalityStats> {
158 struct BackendMetric {
159 uint64_t num_requests_finished_with_metric;
160 double total_metric_value;
162 BackendMetric& operator+=(const BackendMetric& other) {
163 num_requests_finished_with_metric +=
164 other.num_requests_finished_with_metric;
165 total_metric_value += other.total_metric_value;
169 bool IsZero() const {
170 return num_requests_finished_with_metric == 0 && total_metric_value == 0;
175 uint64_t total_successful_requests;
176 uint64_t total_requests_in_progress;
177 uint64_t total_error_requests;
178 uint64_t total_issued_requests;
179 std::map<std::string, BackendMetric> backend_metrics;
181 Snapshot& operator+=(const Snapshot& other) {
182 total_successful_requests += other.total_successful_requests;
183 total_requests_in_progress += other.total_requests_in_progress;
184 total_error_requests += other.total_error_requests;
185 total_issued_requests += other.total_issued_requests;
186 for (const auto& p : other.backend_metrics) {
187 backend_metrics[p.first] += p.second;
192 bool IsZero() const {
193 if (total_successful_requests != 0 || total_requests_in_progress != 0 ||
194 total_error_requests != 0 || total_issued_requests != 0) {
197 for (const auto& p : backend_metrics) {
198 if (!p.second.IsZero()) return false;
204 XdsClusterLocalityStats(RefCountedPtr<XdsClient> xds_client,
205 absl::string_view lrs_server_name,
206 absl::string_view cluster_name,
207 absl::string_view eds_service_name,
208 RefCountedPtr<XdsLocalityName> name);
209 ~XdsClusterLocalityStats() override;
211 // Returns a snapshot of this instance and resets all the counters.
212 Snapshot GetSnapshotAndReset();
214 void AddCallStarted();
215 void AddCallFinished(bool fail = false);
218 RefCountedPtr<XdsClient> xds_client_;
219 absl::string_view lrs_server_name_;
220 absl::string_view cluster_name_;
221 absl::string_view eds_service_name_;
222 RefCountedPtr<XdsLocalityName> name_;
224 Atomic<uint64_t> total_successful_requests_{0};
225 Atomic<uint64_t> total_requests_in_progress_{0};
226 Atomic<uint64_t> total_error_requests_{0};
227 Atomic<uint64_t> total_issued_requests_{0};
229 // Protects backend_metrics_. A mutex is necessary because the length of
230 // backend_metrics_ can be accessed by both the callback intercepting the
231 // call's recv_trailing_metadata (not from the control plane work serializer)
232 // and the load reporting thread (from the control plane work serializer).
233 Mutex backend_metrics_mu_;
234 std::map<std::string, BackendMetric> backend_metrics_
235 ABSL_GUARDED_BY(backend_metrics_mu_);
238 } // namespace grpc_core
240 #endif /* GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H */