Skip to content

Commit bae84b6

Browse files
committed
refactor clients/http: use MonotonicByLabelStorage for metrics
commit_hash:96a83e13d97eb6419431cf2b01f819a9d4a2c736
1 parent 2fad38a commit bae84b6

File tree

11 files changed

+79
-85
lines changed

11 files changed

+79
-85
lines changed

core/include/userver/clients/http/request.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class Request final {
9696
explicit Request(
9797
impl::EasyWrapper&&,
9898
RequestStats&& req_stats,
99-
const std::shared_ptr<DestinationStatistics>& dest_stats,
99+
DestinationStatistics& dest_stats,
100100
clients::dns::Resolver* resolver,
101101
const tracing::TracingManagerBase& tracing_manager
102102
);

core/include/userver/utils/statistics/by_label_storage.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ struct ByLabelEntryEqual {
243243
/// @tparam Labels An aggregate type whose fields are convertible to `std::string_view`.
244244
/// @tparam Metric The metric type. Must support `DumpMetric(Writer&, const Metric&)`.
245245
template <typename Labels, typename Metric>
246-
requires impl::LabelsAggregate<Labels> && impl::DumpableMetric<Metric>
246+
requires impl::LabelsAggregate<Labels>
247247
class MonotonicByLabelStorage final {
248248
public:
249249
/// @brief Create an empty storage.
@@ -298,7 +298,9 @@ class MonotonicByLabelStorage final {
298298
}
299299

300300
/// @brief Dump all metrics to a Writer, using label names from `Labels` fields.
301-
friend void DumpMetric(Writer& writer, const MonotonicByLabelStorage& storage) {
301+
friend void DumpMetric(Writer& writer, const MonotonicByLabelStorage& storage)
302+
requires impl::DumpableMetric<Metric>
303+
{
302304
static constexpr auto kLabelNames = impl::GetLabelNames<Labels>();
303305
static constexpr std::size_t kFieldCount = boost::pfr::tuple_size_v<Labels>;
304306

core/src/clients/http/client_core.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ Request ClientCore::CreateRequest() {
117117
auto wrapper = impl::EasyWrapper{std::move(easy), *this};
118118
return Request{
119119
std::move(wrapper),
120-
statistics_[idx].CreateRequestStats(),
121-
destination_statistics_,
120+
RequestStats{statistics_[idx]},
121+
*destination_statistics_,
122122
resolver_,
123123
*tracing_manager_.GetBase()
124124
};
@@ -133,8 +133,8 @@ Request ClientCore::CreateRequest() {
133133
}).Get();
134134
return Request{
135135
std::move(wrapper),
136-
statistics_[i].CreateRequestStats(),
137-
destination_statistics_,
136+
RequestStats{statistics_[i]},
137+
*destination_statistics_,
138138
resolver_,
139139
*tracing_manager_.GetBase()
140140
};

core/src/clients/http/destination_statistics.cpp

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,14 @@ USERVER_NAMESPACE_BEGIN
77

88
namespace clients::http {
99

10-
std::shared_ptr<RequestStats> DestinationStatistics::GetStatisticsForDestination(const std::string& destination) {
11-
auto ptr = GetExistingStatisticsForDestination(destination);
12-
if (ptr) {
13-
return ptr;
14-
}
15-
16-
return CreateStatisticsForDestination(destination);
10+
RequestStats DestinationStatistics::GetStatisticsForDestination(std::string_view destination) {
11+
return RequestStats{metrics_[{.http_destination = destination}]};
1712
}
1813

19-
std::shared_ptr<RequestStats> DestinationStatistics::CreateStatisticsForDestination(const std::string& destination) {
20-
return std::make_shared<RequestStats>(*rcu_map_[destination]);
21-
}
22-
23-
std::shared_ptr<RequestStats> DestinationStatistics::GetExistingStatisticsForDestination(const std::string& destination
24-
) {
25-
/*
26-
* It's safe to return RequestStats holding a reference to Statistics as
27-
* RequestStats lifetime is less than clients::http::ClientCore's one.
28-
*/
29-
auto stats = rcu_map_.Get(destination);
14+
std::optional<RequestStats> DestinationStatistics::GetStatisticsForDestinationAuto(std::string_view destination) {
15+
auto* const stats = metrics_.GetIfExists({.http_destination = destination});
3016
if (stats) {
31-
return std::make_shared<RequestStats>(*stats);
32-
} else {
33-
return {};
34-
}
35-
}
36-
37-
std::shared_ptr<RequestStats> DestinationStatistics::GetStatisticsForDestinationAuto(const std::string& destination) {
38-
auto ptr = GetExistingStatisticsForDestination(destination);
39-
if (ptr) {
40-
return ptr;
17+
return std::optional<RequestStats>{std::in_place, *stats};
4118
}
4219

4320
// atomic [current++ iff current < max]
@@ -57,22 +34,26 @@ std::shared_ptr<RequestStats> DestinationStatistics::GetStatisticsForDestination
5734
} while (!current_auto_destinations_
5835
.compare_exchange_strong(current_auto_destinations, current_auto_destinations + 1));
5936

60-
return CreateStatisticsForDestination(destination);
37+
return GetStatisticsForDestination(destination);
6138
}
6239

6340
void DestinationStatistics::SetAutoMaxSize(size_t max_auto_destinations) {
6441
max_auto_destinations_ = max_auto_destinations;
6542
}
6643

67-
DestinationStatistics::DestinationsMap::ConstIterator DestinationStatistics::begin() const { return rcu_map_.begin(); }
68-
69-
DestinationStatistics::DestinationsMap::ConstIterator DestinationStatistics::end() const { return rcu_map_.end(); }
44+
void DestinationStatistics::VisitAllDebug(utils::function_ref<void(const DestinationLabels&, const Statistics&)> func
45+
) const {
46+
metrics_.VisitAll(func);
47+
}
7048

7149
void DumpMetric(utils::statistics::Writer& writer, const DestinationStatistics& stats) {
72-
for (const auto& [url, stat_ptr] : stats) {
73-
const InstanceStatistics instance_stat{*stat_ptr};
74-
writer.ValueWithLabels(DestinationStatisticsView{instance_stat}, {{"http_destination", url}, {"version", "2"}});
75-
}
50+
stats.metrics_.VisitAll([&writer](const DestinationLabels& labels, const Statistics& destination_stats) {
51+
const InstanceStatistics instance_stat{destination_stats};
52+
writer.ValueWithLabels(
53+
DestinationStatisticsView{instance_stat},
54+
{{"http_destination", labels.http_destination}, {"version", "2"}}
55+
);
56+
});
7657
}
7758

7859
} // namespace clients::http

core/src/clients/http/destination_statistics.hpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
#pragma once
22

3-
#include <memory>
4-
#include <unordered_map>
5-
6-
#include <userver/rcu/rcu_map.hpp>
3+
#include <atomic>
4+
#include <cstddef>
5+
#include <optional>
6+
#include <string_view>
7+
8+
#include <userver/utils/function_ref.hpp>
9+
#include <userver/utils/required.hpp>
10+
#include <userver/utils/statistics/by_label_storage.hpp>
711
#include <userver/utils/statistics/fwd.hpp>
812

913
#include <clients/http/statistics.hpp>
@@ -12,30 +16,29 @@ USERVER_NAMESPACE_BEGIN
1216

1317
namespace clients::http {
1418

19+
struct DestinationLabels final {
20+
utils::Required<std::string_view> http_destination;
21+
};
22+
1523
class DestinationStatistics final {
1624
public:
17-
// Return pointer to related RequestStats
18-
std::shared_ptr<RequestStats> GetStatisticsForDestination(const std::string& destination);
25+
// It's safe to return RequestStats holding a reference to Statistics as
26+
// RequestStats lifetime is less than clients::http::Client's one.
27+
RequestStats GetStatisticsForDestination(std::string_view destination);
1928

20-
// If max_auto_destinations reached, return nullptr, pointer to related
21-
// RequestStats otherwise
22-
std::shared_ptr<RequestStats> GetStatisticsForDestinationAuto(const std::string& destination);
29+
// If max_auto_destinations is reached, returns nullopt.
30+
std::optional<RequestStats> GetStatisticsForDestinationAuto(std::string_view destination);
2331

2432
void SetAutoMaxSize(size_t max_auto_destinations);
2533

26-
using DestinationsMap = rcu::RcuMap<std::string, Statistics>;
34+
void VisitAllDebug(utils::function_ref<void(const DestinationLabels&, const Statistics&)> func) const;
2735

28-
DestinationsMap::ConstIterator begin() const;
29-
DestinationsMap::ConstIterator end() const;
36+
friend void DumpMetric(utils::statistics::Writer& writer, const DestinationStatistics& stats);
3037

3138
private:
32-
std::shared_ptr<RequestStats> GetExistingStatisticsForDestination(const std::string& destination);
33-
34-
std::shared_ptr<RequestStats> CreateStatisticsForDestination(const std::string& destination);
35-
36-
rcu::RcuMap<std::string, Statistics> rcu_map_;
37-
size_t max_auto_destinations_{0};
38-
std::atomic<size_t> current_auto_destinations_{0};
39+
utils::statistics::MonotonicByLabelStorage<DestinationLabels, Statistics> metrics_;
40+
std::size_t max_auto_destinations_{0};
41+
std::atomic<std::size_t> current_auto_destinations_{0};
3942
};
4043

4144
void DumpMetric(utils::statistics::Writer& writer, const DestinationStatistics& stats);

core/src/clients/http/destination_statistics_test.cpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ USERVER_NAMESPACE_BEGIN
1414

1515
using HttpResponse = utest::SimpleServer::Response;
1616
using HttpRequest = utest::SimpleServer::Request;
17-
using HttpCallback = utest::SimpleServer::OnRequest;
1817

1918
static HttpResponse Callback(int code, const HttpRequest& request) {
2019
LOG_INFO() << "HTTP Server receive: " << request;
@@ -30,7 +29,10 @@ static HttpResponse Callback(int code, const HttpRequest& request) {
3029
UTEST(DestinationStatistics, Empty) {
3130
auto client = utest::impl::CreateHttpClientCore();
3231

33-
EXPECT_EQ(client->GetDestinationStatistics().begin(), client->GetDestinationStatistics().end());
32+
client->GetDestinationStatistics()
33+
.VisitAllDebug([](const clients::http::DestinationLabels&, const clients::http::Statistics&) {
34+
ADD_FAILURE() << "Should be empty";
35+
});
3436
}
3537

3638
UTEST(DestinationStatistics, Ok) {
@@ -41,23 +43,23 @@ UTEST(DestinationStatistics, Ok) {
4143

4244
auto response = client->CreateRequest().post(url).retry(1).timeout(std::chrono::milliseconds(100)).perform();
4345

44-
const auto& dest_stats = client->GetDestinationStatistics();
4546
size_t size = 0;
46-
for (const auto& [stat_url, stat_ptr] : dest_stats) {
47+
48+
const auto visitor = [&](const clients::http::DestinationLabels& labels, const clients::http::Statistics& stat) {
4749
ASSERT_EQ(1, ++size);
4850

49-
EXPECT_EQ(url, stat_url);
50-
ASSERT_NE(nullptr, stat_ptr);
51+
EXPECT_EQ(url, *labels.http_destination);
5152

52-
auto stats = clients::http::InstanceStatistics(*stat_ptr);
53+
auto stats = clients::http::InstanceStatistics(stat);
5354
auto ok = static_cast<size_t>(clients::http::Statistics::ErrorGroup::kOk);
5455
EXPECT_EQ(utils::statistics::Rate{1}, stats.error_count[ok]);
5556
for (size_t i = 0; i < clients::http::Statistics::kErrorGroupCount; i++) {
5657
if (i != ok) {
5758
EXPECT_EQ(utils::statistics::Rate{0}, stats.error_count[i]);
5859
}
5960
}
60-
}
61+
};
62+
client->GetDestinationStatistics().VisitAllDebug(visitor);
6163
}
6264

6365
UTEST(DestinationStatistics, CancelledFuture) {
@@ -99,25 +101,23 @@ UTEST(DestinationStatistics, Multiple) {
99101
auto response = client->CreateRequest().post(url).retry(1).timeout(std::chrono::milliseconds(100)).perform();
100102
response = client->CreateRequest().post(url2).retry(1).timeout(std::chrono::milliseconds(100)).perform();
101103

102-
const auto& dest_stats = client->GetDestinationStatistics();
103104
size_t size = 0;
104105
std::unordered_set<std::string> expected_urls{url, url2};
105-
for (const auto& [stat_url, stat_ptr] : dest_stats) {
106+
const auto visitor = [&](const clients::http::DestinationLabels& labels, const clients::http::Statistics& stat) {
106107
ASSERT_LE(++size, 2);
107108

108-
EXPECT_EQ(1, expected_urls.erase(stat_url));
109+
EXPECT_EQ(1, expected_urls.erase(std::string{labels.http_destination}));
109110

110-
ASSERT_NE(nullptr, stat_ptr);
111-
112-
auto stats = clients::http::InstanceStatistics(*stat_ptr);
111+
auto stats = clients::http::InstanceStatistics(stat);
113112
auto ok = static_cast<size_t>(clients::http::Statistics::ErrorGroup::kOk);
114113
EXPECT_EQ(utils::statistics::Rate{1}, stats.error_count[ok]);
115114
for (size_t i = 0; i < clients::http::Statistics::kErrorGroupCount; i++) {
116115
if (i != ok) {
117116
EXPECT_EQ(utils::statistics::Rate{0}, stats.error_count[i]) << i << " errors must be zero";
118117
}
119118
}
120-
}
119+
};
120+
client->GetDestinationStatistics().VisitAllDebug(visitor);
121121
}
122122

123123
USERVER_NAMESPACE_END

core/src/clients/http/request.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ ProxyAuthType ProxyAuthTypeFromString(std::string_view auth_name) {
204204
Request::Request(
205205
impl::EasyWrapper&& wrapper,
206206
RequestStats&& req_stats,
207-
const std::shared_ptr<DestinationStatistics>& dest_stats,
207+
DestinationStatistics& dest_stats,
208208
clients::dns::Resolver* resolver,
209209
const tracing::TracingManagerBase& tracing_manager
210210
)

core/src/clients/http/request_state.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ bool IsHttp11WithCompleteBody(const std::shared_ptr<Response> response) {
240240
RequestState::RequestState(
241241
impl::EasyWrapper&& wrapper,
242242
RequestStats&& req_stats,
243-
const std::shared_ptr<DestinationStatistics>& dest_stats,
243+
DestinationStatistics& dest_stats,
244244
clients::dns::Resolver* resolver,
245245
const tracing::TracingManagerBase& tracing_manager
246246
)
@@ -420,7 +420,7 @@ void RequestState::SetDestinationMetricNameAuto(std::string destination) {
420420
}
421421

422422
void RequestState::SetDestinationMetricName(const std::string& destination) {
423-
dest_req_stats_ = dest_stats_->GetStatisticsForDestination(destination);
423+
dest_req_stats_ = dest_stats_.GetStatisticsForDestination(destination);
424424
}
425425

426426
void RequestState::SetTestsuiteConfig(const std::shared_ptr<const TestsuiteConfig>& config) {
@@ -1215,7 +1215,7 @@ void RequestState::StartNewSpan(utils::impl::SourceLocation location) {
12151215

12161216
void RequestState::StartStats() {
12171217
if (!dest_req_stats_) {
1218-
dest_req_stats_ = dest_stats_->GetStatisticsForDestinationAuto(destination_metric_name_);
1218+
dest_req_stats_ = dest_stats_.GetStatisticsForDestinationAuto(destination_metric_name_);
12191219
}
12201220

12211221
WithRequestStats([](RequestStats& stats) { stats.Start(); });

core/src/clients/http/request_state.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class RequestState : public std::enable_shared_from_this<RequestState> {
5151
RequestState(
5252
impl::EasyWrapper&&,
5353
RequestStats&& req_stats,
54-
const std::shared_ptr<DestinationStatistics>& dest_stats,
54+
DestinationStatistics& dest_stats,
5555
clients::dns::Resolver* resolver,
5656
const tracing::TracingManagerBase& tracing_manager
5757
);
@@ -214,10 +214,10 @@ class RequestState : public std::enable_shared_from_this<RequestState> {
214214
/// curl handler wrapper
215215
impl::EasyWrapper easy_;
216216
RequestStats stats_;
217-
std::shared_ptr<RequestStats> dest_req_stats_;
217+
std::optional<RequestStats> dest_req_stats_;
218218
CancellationPolicy cancellation_policy_{CancellationPolicy::kCancel};
219219

220-
std::shared_ptr<DestinationStatistics> dest_stats_;
220+
DestinationStatistics& dest_stats_;
221221
std::string destination_metric_name_;
222222

223223
std::shared_ptr<const TestsuiteConfig> testsuite_config_;

core/src/clients/http/statistics.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ RequestStats::~RequestStats() {
4040

4141
RequestStats::RequestStats(RequestStats&& other) noexcept : stats_{std::exchange(other.stats_, nullptr)} {}
4242

43+
RequestStats& RequestStats::operator=(RequestStats&& other) noexcept {
44+
if (this != &other) {
45+
stats_ = std::exchange(other.stats_, nullptr);
46+
start_time_ = other.start_time_;
47+
}
48+
return *this;
49+
}
50+
4351
void RequestStats::Start() { start_time_ = std::chrono::steady_clock::now(); }
4452

4553
void RequestStats::FinishOk(int code, unsigned int attempts) noexcept {

0 commit comments

Comments
 (0)