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_SRC_CORE_EXT_XDS_XDS_CLIENT_STATS_H 20 #define GRPC_SRC_CORE_EXT_XDS_XDS_CLIENT_STATS_H 21 22 #include <grpc/support/port_platform.h> 23 24 #include <atomic> 25 #include <cstdint> 26 #include <initializer_list> 27 #include <map> 28 #include <string> 29 #include <utility> 30 31 #include "absl/base/thread_annotations.h" 32 #include "absl/strings/str_format.h" 33 #include "absl/strings/string_view.h" 34 35 #include "src/core/ext/xds/xds_bootstrap.h" 36 #include "src/core/lib/gpr/useful.h" 37 #include "src/core/lib/gprpp/per_cpu.h" 38 #include "src/core/lib/gprpp/ref_counted.h" 39 #include "src/core/lib/gprpp/ref_counted_ptr.h" 40 #include "src/core/lib/gprpp/sync.h" 41 42 namespace grpc_core { 43 44 // Forward declaration to avoid circular dependency. 45 class XdsClient; 46 47 // Locality name. 48 class XdsLocalityName : public RefCounted<XdsLocalityName> { 49 public: 50 struct Less { operatorLess51 bool operator()(const XdsLocalityName* lhs, 52 const XdsLocalityName* rhs) const { 53 if (lhs == nullptr || rhs == nullptr) return QsortCompare(lhs, rhs); 54 return lhs->Compare(*rhs) < 0; 55 } 56 operatorLess57 bool operator()(const RefCountedPtr<XdsLocalityName>& lhs, 58 const RefCountedPtr<XdsLocalityName>& rhs) const { 59 return (*this)(lhs.get(), rhs.get()); 60 } 61 }; 62 XdsLocalityName(std::string region,std::string zone,std::string sub_zone)63 XdsLocalityName(std::string region, std::string zone, std::string sub_zone) 64 : region_(std::move(region)), 65 zone_(std::move(zone)), 66 sub_zone_(std::move(sub_zone)) {} 67 68 bool operator==(const XdsLocalityName& other) const { 69 return region_ == other.region_ && zone_ == other.zone_ && 70 sub_zone_ == other.sub_zone_; 71 } 72 73 bool operator!=(const XdsLocalityName& other) const { 74 return !(*this == other); 75 } 76 Compare(const XdsLocalityName & other)77 int Compare(const XdsLocalityName& other) const { 78 int cmp_result = region_.compare(other.region_); 79 if (cmp_result != 0) return cmp_result; 80 cmp_result = zone_.compare(other.zone_); 81 if (cmp_result != 0) return cmp_result; 82 return sub_zone_.compare(other.sub_zone_); 83 } 84 region()85 const std::string& region() const { return region_; } zone()86 const std::string& zone() const { return zone_; } sub_zone()87 const std::string& sub_zone() const { return sub_zone_; } 88 AsHumanReadableString()89 const std::string& AsHumanReadableString() { 90 if (human_readable_string_.empty()) { 91 human_readable_string_ = 92 absl::StrFormat("{region=\"%s\", zone=\"%s\", sub_zone=\"%s\"}", 93 region_, zone_, sub_zone_); 94 } 95 return human_readable_string_; 96 } 97 98 private: 99 std::string region_; 100 std::string zone_; 101 std::string sub_zone_; 102 std::string human_readable_string_; 103 }; 104 105 // Drop stats for an xds cluster. 106 class XdsClusterDropStats : public RefCounted<XdsClusterDropStats> { 107 public: 108 // The total number of requests dropped for any reason is the sum of 109 // uncategorized_drops, and dropped_requests map. 110 using CategorizedDropsMap = std::map<std::string /* category */, uint64_t>; 111 struct Snapshot { 112 uint64_t uncategorized_drops = 0; 113 // The number of requests dropped for the specific drop categories 114 // outlined in the drop_overloads field in the EDS response. 115 CategorizedDropsMap categorized_drops; 116 117 Snapshot& operator+=(const Snapshot& other) { 118 uncategorized_drops += other.uncategorized_drops; 119 for (const auto& p : other.categorized_drops) { 120 categorized_drops[p.first] += p.second; 121 } 122 return *this; 123 } 124 IsZeroSnapshot125 bool IsZero() const { 126 if (uncategorized_drops != 0) return false; 127 for (const auto& p : categorized_drops) { 128 if (p.second != 0) return false; 129 } 130 return true; 131 } 132 }; 133 134 XdsClusterDropStats(RefCountedPtr<XdsClient> xds_client, 135 const XdsBootstrap::XdsServer& lrs_server, 136 absl::string_view cluster_name, 137 absl::string_view eds_service_name); 138 ~XdsClusterDropStats() override; 139 140 // Returns a snapshot of this instance and resets all the counters. 141 Snapshot GetSnapshotAndReset(); 142 143 void AddUncategorizedDrops(); 144 void AddCallDropped(const std::string& category); 145 146 private: 147 RefCountedPtr<XdsClient> xds_client_; 148 const XdsBootstrap::XdsServer& lrs_server_; 149 absl::string_view cluster_name_; 150 absl::string_view eds_service_name_; 151 std::atomic<uint64_t> uncategorized_drops_{0}; 152 // Protects categorized_drops_. A mutex is necessary because the length of 153 // dropped_requests can be accessed by both the picker (from data plane 154 // mutex) and the load reporting thread (from the control plane combiner). 155 Mutex mu_; 156 CategorizedDropsMap categorized_drops_ ABSL_GUARDED_BY(mu_); 157 }; 158 159 // Locality stats for an xds cluster. 160 class XdsClusterLocalityStats : public RefCounted<XdsClusterLocalityStats> { 161 public: 162 struct BackendMetric { 163 uint64_t num_requests_finished_with_metric = 0; 164 double total_metric_value = 0; 165 166 BackendMetric& operator+=(const BackendMetric& other) { 167 num_requests_finished_with_metric += 168 other.num_requests_finished_with_metric; 169 total_metric_value += other.total_metric_value; 170 return *this; 171 } 172 IsZeroBackendMetric173 bool IsZero() const { 174 return num_requests_finished_with_metric == 0 && total_metric_value == 0; 175 } 176 }; 177 178 struct Snapshot { 179 uint64_t total_successful_requests = 0; 180 uint64_t total_requests_in_progress = 0; 181 uint64_t total_error_requests = 0; 182 uint64_t total_issued_requests = 0; 183 std::map<std::string, BackendMetric> backend_metrics; 184 185 Snapshot& operator+=(const Snapshot& other) { 186 total_successful_requests += other.total_successful_requests; 187 total_requests_in_progress += other.total_requests_in_progress; 188 total_error_requests += other.total_error_requests; 189 total_issued_requests += other.total_issued_requests; 190 for (const auto& p : other.backend_metrics) { 191 backend_metrics[p.first] += p.second; 192 } 193 return *this; 194 } 195 IsZeroSnapshot196 bool IsZero() const { 197 if (total_successful_requests != 0 || total_requests_in_progress != 0 || 198 total_error_requests != 0 || total_issued_requests != 0) { 199 return false; 200 } 201 for (const auto& p : backend_metrics) { 202 if (!p.second.IsZero()) return false; 203 } 204 return true; 205 } 206 }; 207 208 XdsClusterLocalityStats(RefCountedPtr<XdsClient> xds_client, 209 const XdsBootstrap::XdsServer& lrs_server_, 210 absl::string_view cluster_name, 211 absl::string_view eds_service_name, 212 RefCountedPtr<XdsLocalityName> name); 213 ~XdsClusterLocalityStats() override; 214 215 // Returns a snapshot of this instance and resets all the counters. 216 Snapshot GetSnapshotAndReset(); 217 218 void AddCallStarted(); 219 void AddCallFinished(const std::map<absl::string_view, double>* named_metrics, 220 bool fail = false); 221 222 private: 223 struct Stats { 224 std::atomic<uint64_t> total_successful_requests{0}; 225 std::atomic<uint64_t> total_requests_in_progress{0}; 226 std::atomic<uint64_t> total_error_requests{0}; 227 std::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 and the load reporting thread. 232 Mutex backend_metrics_mu; 233 std::map<std::string, BackendMetric> backend_metrics 234 ABSL_GUARDED_BY(backend_metrics_mu); 235 }; 236 237 RefCountedPtr<XdsClient> xds_client_; 238 const XdsBootstrap::XdsServer& lrs_server_; 239 absl::string_view cluster_name_; 240 absl::string_view eds_service_name_; 241 RefCountedPtr<XdsLocalityName> name_; 242 PerCpu<Stats> stats_{PerCpuOptions().SetMaxShards(32).SetCpusPerShard(4)}; 243 }; 244 245 } // namespace grpc_core 246 247 #endif // GRPC_SRC_CORE_EXT_XDS_XDS_CLIENT_STATS_H 248