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