• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
operatorLess47     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 
operatorLess53     bool operator()(const RefCountedPtr<XdsLocalityName>& lhs,
54                     const RefCountedPtr<XdsLocalityName>& rhs) const {
55       return (*this)(lhs.get(), rhs.get());
56     }
57   };
58 
XdsLocalityName(std::string region,std::string zone,std::string subzone)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 
Compare(const XdsLocalityName & 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_);
79   }
80 
region()81   const std::string& region() const { return region_; }
zone()82   const std::string& zone() const { return zone_; }
sub_zone()83   const std::string& sub_zone() const { return sub_zone_; }
84 
AsHumanReadableString()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 
IsZeroSnapshot121     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() override;
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 
IsZeroBackendMetric169     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 
IsZeroSnapshot192     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() override;
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 */
240