3 * Copyright 2018 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <grpc/support/port_platform.h>
21 #include "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h"
23 #include <grpc/support/atm.h>
24 #include <grpc/support/string_util.h>
32 T GetAndResetCounter(Atomic<T>* from) {
33 return from->Exchange(0, MemoryOrder::RELAXED);
39 // XdsClientStats::LocalityStats::LoadMetric::Snapshot
42 bool XdsClientStats::LocalityStats::LoadMetric::Snapshot::IsAllZero() const {
43 return total_metric_value == 0 && num_requests_finished_with_metric == 0;
47 // XdsClientStats::LocalityStats::LoadMetric
50 XdsClientStats::LocalityStats::LoadMetric::Snapshot
51 XdsClientStats::LocalityStats::LoadMetric::GetSnapshotAndReset() {
52 Snapshot metric = {num_requests_finished_with_metric_, total_metric_value_};
53 num_requests_finished_with_metric_ = 0;
54 total_metric_value_ = 0;
59 // XdsClientStats::LocalityStats::Snapshot
62 bool XdsClientStats::LocalityStats::Snapshot::IsAllZero() {
63 if (total_successful_requests != 0 || total_requests_in_progress != 0 ||
64 total_error_requests != 0 || total_issued_requests != 0) {
67 for (auto& p : load_metric_stats) {
68 const LoadMetric::Snapshot& metric_value = p.second;
69 if (!metric_value.IsAllZero()) return false;
75 // XdsClientStats::LocalityStats
78 XdsClientStats::LocalityStats::Snapshot
79 XdsClientStats::LocalityStats::GetSnapshotAndReset() {
81 GetAndResetCounter(&total_successful_requests_),
82 // Don't reset total_requests_in_progress because it's not
83 // related to a single reporting interval.
84 total_requests_in_progress_.Load(MemoryOrder::RELAXED),
85 GetAndResetCounter(&total_error_requests_),
86 GetAndResetCounter(&total_issued_requests_)};
88 MutexLock lock(&load_metric_stats_mu_);
89 for (auto& p : load_metric_stats_) {
90 const char* metric_name = p.first.get();
91 LoadMetric& metric_value = p.second;
92 snapshot.load_metric_stats.emplace(
93 UniquePtr<char>(gpr_strdup(metric_name)),
94 metric_value.GetSnapshotAndReset());
100 void XdsClientStats::LocalityStats::AddCallStarted() {
101 total_issued_requests_.FetchAdd(1, MemoryOrder::RELAXED);
102 total_requests_in_progress_.FetchAdd(1, MemoryOrder::RELAXED);
105 void XdsClientStats::LocalityStats::AddCallFinished(bool fail) {
106 Atomic<uint64_t>& to_increment =
107 fail ? total_error_requests_ : total_successful_requests_;
108 to_increment.FetchAdd(1, MemoryOrder::RELAXED);
109 total_requests_in_progress_.FetchAdd(-1, MemoryOrder::ACQ_REL);
113 // XdsClientStats::Snapshot
116 bool XdsClientStats::Snapshot::IsAllZero() {
117 for (auto& p : upstream_locality_stats) {
118 if (!p.second.IsAllZero()) return false;
120 for (auto& p : dropped_requests) {
121 if (p.second != 0) return false;
123 return total_dropped_requests == 0;
130 XdsClientStats::Snapshot XdsClientStats::GetSnapshotAndReset() {
131 grpc_millis now = ExecCtx::Get()->Now();
132 // Record total_dropped_requests and reporting interval in the snapshot.
134 snapshot.total_dropped_requests =
135 GetAndResetCounter(&total_dropped_requests_);
136 snapshot.load_report_interval = now - last_report_time_;
137 // Update last report time.
138 last_report_time_ = now;
139 // Snapshot all the other stats.
140 for (auto& p : upstream_locality_stats_) {
141 snapshot.upstream_locality_stats.emplace(p.first,
142 p.second->GetSnapshotAndReset());
145 MutexLock lock(&dropped_requests_mu_);
146 #if GRPC_USE_CPP_STD_LIB
147 // This is a workaround for the case where some compilers cannot build
148 // move-assignment of map with non-copyable but movable key.
149 // https://stackoverflow.com/questions/36475497
150 std::swap(snapshot.dropped_requests, dropped_requests_);
151 dropped_requests_.clear();
153 snapshot.dropped_requests = std::move(dropped_requests_);
159 void XdsClientStats::MaybeInitLastReportTime() {
160 if (last_report_time_ == -1) last_report_time_ = ExecCtx::Get()->Now();
163 RefCountedPtr<XdsClientStats::LocalityStats> XdsClientStats::FindLocalityStats(
164 const RefCountedPtr<XdsLocalityName>& locality_name) {
165 auto iter = upstream_locality_stats_.find(locality_name);
166 if (iter == upstream_locality_stats_.end()) {
167 iter = upstream_locality_stats_
168 .emplace(locality_name, MakeRefCounted<LocalityStats>())
174 void XdsClientStats::PruneLocalityStats() {
175 auto iter = upstream_locality_stats_.begin();
176 while (iter != upstream_locality_stats_.end()) {
177 if (iter->second->IsSafeToDelete()) {
178 iter = upstream_locality_stats_.erase(iter);
185 void XdsClientStats::AddCallDropped(const UniquePtr<char>& category) {
186 total_dropped_requests_.FetchAdd(1, MemoryOrder::RELAXED);
187 MutexLock lock(&dropped_requests_mu_);
188 auto iter = dropped_requests_.find(category);
189 if (iter == dropped_requests_.end()) {
190 dropped_requests_.emplace(UniquePtr<char>(gpr_strdup(category.get())), 1);
196 } // namespace grpc_core