Imported Upstream version 1.33.1
[platform/upstream/grpc.git] / src / core / ext / xds / xds_client_stats.h
1 /*
2  *
3  * Copyright 2018 gRPC authors.
4  *
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  */
18
19 #ifndef GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H
20 #define GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H
21
22 #include <grpc/support/port_platform.h>
23
24 #include <map>
25 #include <string>
26
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/string_view.h"
30
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"
37
38 namespace grpc_core {
39
40 // Forward declaration to avoid circular dependency.
41 class XdsClient;
42
43 // Locality name.
44 class XdsLocalityName : public RefCounted<XdsLocalityName> {
45  public:
46   struct Less {
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;
51     }
52
53     bool operator()(const RefCountedPtr<XdsLocalityName>& lhs,
54                     const RefCountedPtr<XdsLocalityName>& rhs) const {
55       return (*this)(lhs.get(), rhs.get());
56     }
57   };
58
59   XdsLocalityName(std::string region, std::string zone, std::string subzone)
60       : region_(std::move(region)),
61         zone_(std::move(zone)),
62         sub_zone_(std::move(subzone)) {}
63
64   bool operator==(const XdsLocalityName& other) const {
65     return region_ == other.region_ && zone_ == other.zone_ &&
66            sub_zone_ == other.sub_zone_;
67   }
68
69   bool operator!=(const XdsLocalityName& other) const {
70     return !(*this == other);
71   }
72
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_);
79   }
80
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_; }
84
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_);
90     }
91     return human_readable_string_;
92   }
93
94  private:
95   std::string region_;
96   std::string zone_;
97   std::string sub_zone_;
98   std::string human_readable_string_;
99 };
100
101 // Drop stats for an xds cluster.
102 class XdsClusterDropStats : public RefCounted<XdsClusterDropStats> {
103  public:
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>;
107   struct Snapshot {
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;
112
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;
117       }
118       return *this;
119     }
120
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;
125       }
126       return true;
127     }
128   };
129
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();
135
136   // Returns a snapshot of this instance and resets all the counters.
137   Snapshot GetSnapshotAndReset();
138
139   void AddUncategorizedDrops();
140   void AddCallDropped(const std::string& category);
141
142  private:
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).
151   Mutex mu_;
152   CategorizedDropsMap categorized_drops_;
153 };
154
155 // Locality stats for an xds cluster.
156 class XdsClusterLocalityStats : public RefCounted<XdsClusterLocalityStats> {
157  public:
158   struct BackendMetric {
159     uint64_t num_requests_finished_with_metric;
160     double total_metric_value;
161
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;
166       return *this;
167     }
168
169     bool IsZero() const {
170       return num_requests_finished_with_metric == 0 && total_metric_value == 0;
171     }
172   };
173
174   struct Snapshot {
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;
180
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;
188       }
189       return *this;
190     }
191
192     bool IsZero() const {
193       if (total_successful_requests != 0 || total_requests_in_progress != 0 ||
194           total_error_requests != 0 || total_issued_requests != 0) {
195         return false;
196       }
197       for (const auto& p : backend_metrics) {
198         if (!p.second.IsZero()) return false;
199       }
200       return true;
201     }
202   };
203
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();
210
211   // Returns a snapshot of this instance and resets all the counters.
212   Snapshot GetSnapshotAndReset();
213
214   void AddCallStarted();
215   void AddCallFinished(bool fail = false);
216
217  private:
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_;
223
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};
228
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 };
236
237 }  // namespace grpc_core
238
239 #endif /* GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H */