Imported Upstream version 1.32.0
[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/gprpp/atomic.h"
32 #include "src/core/lib/gprpp/memory.h"
33 #include "src/core/lib/gprpp/ref_counted.h"
34 #include "src/core/lib/gprpp/sync.h"
35 #include "src/core/lib/iomgr/exec_ctx.h"
36
37 namespace grpc_core {
38
39 // Forward declaration to avoid circular dependency.
40 class XdsClient;
41
42 // Locality name.
43 class XdsLocalityName : public RefCounted<XdsLocalityName> {
44  public:
45   struct Less {
46     bool operator()(const XdsLocalityName* lhs,
47                     const XdsLocalityName* rhs) const {
48       return lhs->Compare(*rhs) < 0;
49     }
50
51     bool operator()(const RefCountedPtr<XdsLocalityName>& lhs,
52                     const RefCountedPtr<XdsLocalityName>& rhs) const {
53       return (*this)(lhs.get(), rhs.get());
54     }
55   };
56
57   XdsLocalityName(std::string region, std::string zone, std::string subzone)
58       : region_(std::move(region)),
59         zone_(std::move(zone)),
60         sub_zone_(std::move(subzone)) {}
61
62   bool operator==(const XdsLocalityName& other) const {
63     return region_ == other.region_ && zone_ == other.zone_ &&
64            sub_zone_ == other.sub_zone_;
65   }
66
67   bool operator!=(const XdsLocalityName& other) const {
68     return !(*this == other);
69   }
70
71   int Compare(const XdsLocalityName& other) const {
72     int cmp_result = region_.compare(other.region_);
73     if (cmp_result != 0) return cmp_result;
74     cmp_result = zone_.compare(other.zone_);
75     if (cmp_result != 0) return cmp_result;
76     return sub_zone_.compare(other.sub_zone_);
77   }
78
79   const std::string& region() const { return region_; }
80   const std::string& zone() const { return zone_; }
81   const std::string& sub_zone() const { return sub_zone_; }
82
83   const std::string& AsHumanReadableString() {
84     if (human_readable_string_.empty()) {
85       human_readable_string_ =
86           absl::StrFormat("{region=\"%s\", zone=\"%s\", sub_zone=\"%s\"}",
87                           region_, zone_, sub_zone_);
88     }
89     return human_readable_string_;
90   }
91
92  private:
93   std::string region_;
94   std::string zone_;
95   std::string sub_zone_;
96   std::string human_readable_string_;
97 };
98
99 // Drop stats for an xds cluster.
100 class XdsClusterDropStats : public RefCounted<XdsClusterDropStats> {
101  public:
102   using DroppedRequestsMap = std::map<std::string /* category */, uint64_t>;
103
104   XdsClusterDropStats(RefCountedPtr<XdsClient> xds_client,
105                       absl::string_view lrs_server_name,
106                       absl::string_view cluster_name,
107                       absl::string_view eds_service_name);
108   ~XdsClusterDropStats();
109
110   // Returns a snapshot of this instance and resets all the counters.
111   DroppedRequestsMap GetSnapshotAndReset();
112
113   void AddCallDropped(const std::string& category);
114
115  private:
116   RefCountedPtr<XdsClient> xds_client_;
117   absl::string_view lrs_server_name_;
118   absl::string_view cluster_name_;
119   absl::string_view eds_service_name_;
120   // Protects dropped_requests_. A mutex is necessary because the length of
121   // dropped_requests_ can be accessed by both the picker (from data plane
122   // mutex) and the load reporting thread (from the control plane combiner).
123   Mutex mu_;
124   DroppedRequestsMap dropped_requests_;
125 };
126
127 // Locality stats for an xds cluster.
128 class XdsClusterLocalityStats : public RefCounted<XdsClusterLocalityStats> {
129  public:
130   struct BackendMetric {
131     uint64_t num_requests_finished_with_metric;
132     double total_metric_value;
133
134     BackendMetric& operator+=(const BackendMetric& other) {
135       num_requests_finished_with_metric +=
136           other.num_requests_finished_with_metric;
137       total_metric_value += other.total_metric_value;
138       return *this;
139     }
140
141     bool IsZero() const {
142       return num_requests_finished_with_metric == 0 && total_metric_value == 0;
143     }
144   };
145
146   struct Snapshot {
147     uint64_t total_successful_requests;
148     uint64_t total_requests_in_progress;
149     uint64_t total_error_requests;
150     uint64_t total_issued_requests;
151     std::map<std::string, BackendMetric> backend_metrics;
152
153     Snapshot& operator+=(const Snapshot& other) {
154       total_successful_requests += other.total_successful_requests;
155       total_requests_in_progress += other.total_requests_in_progress;
156       total_error_requests += other.total_error_requests;
157       total_issued_requests += other.total_issued_requests;
158       for (const auto& p : other.backend_metrics) {
159         backend_metrics[p.first] += p.second;
160       }
161       return *this;
162     }
163
164     bool IsZero() const {
165       if (total_successful_requests != 0 || total_requests_in_progress != 0 ||
166           total_error_requests != 0 || total_issued_requests != 0) {
167         return false;
168       }
169       for (const auto& p : backend_metrics) {
170         if (!p.second.IsZero()) return false;
171       }
172       return true;
173     }
174   };
175
176   XdsClusterLocalityStats(RefCountedPtr<XdsClient> xds_client,
177                           absl::string_view lrs_server_name,
178                           absl::string_view cluster_name,
179                           absl::string_view eds_service_name,
180                           RefCountedPtr<XdsLocalityName> name);
181   ~XdsClusterLocalityStats();
182
183   // Returns a snapshot of this instance and resets all the counters.
184   Snapshot GetSnapshotAndReset();
185
186   void AddCallStarted();
187   void AddCallFinished(bool fail = false);
188
189  private:
190   RefCountedPtr<XdsClient> xds_client_;
191   absl::string_view lrs_server_name_;
192   absl::string_view cluster_name_;
193   absl::string_view eds_service_name_;
194   RefCountedPtr<XdsLocalityName> name_;
195
196   Atomic<uint64_t> total_successful_requests_{0};
197   Atomic<uint64_t> total_requests_in_progress_{0};
198   Atomic<uint64_t> total_error_requests_{0};
199   Atomic<uint64_t> total_issued_requests_{0};
200
201   // Protects backend_metrics_. A mutex is necessary because the length of
202   // backend_metrics_ can be accessed by both the callback intercepting the
203   // call's recv_trailing_metadata (not from the control plane work serializer)
204   // and the load reporting thread (from the control plane work serializer).
205   Mutex backend_metrics_mu_;
206   std::map<std::string, BackendMetric> backend_metrics_;
207 };
208
209 }  // namespace grpc_core
210
211 #endif /* GRPC_CORE_EXT_XDS_XDS_CLIENT_STATS_H */