13#include <boost/container_hash/hash.hpp>
14#include <boost/pfr/core.hpp>
15#include <boost/pfr/core_name.hpp>
16#include <boost/pfr/tuple_size.hpp>
18#include <userver/concurrent/impl/monotonic_concurrent_set.hpp>
19#include <userver/utils/assert.hpp>
20#include <userver/utils/fixed_array.hpp>
21#include <userver/utils/statistics/labels.hpp>
22#include <userver/utils/statistics/writer.hpp>
24USERVER_NAMESPACE_BEGIN
26namespace utils::statistics {
30template <
typename Metric>
31concept DumpableMetric = kHasWriterSupport<Metric>;
33template <
typename Metric>
34concept ResettableMetric =
requires(Metric& m) { ResetMetric(m); };
36template <
typename Field>
37std::string_view FieldToStringView(
const Field& field) {
38 if constexpr (std::constructible_from<std::string_view,
const Field&>) {
39 return std::string_view{field};
42 return ToStringView(field);
46template <
typename Field>
47concept FieldConvertibleToStringView =
48 std::constructible_from<std::string_view,
const Field&> ||
requires(
const Field& f) {
51 } -> std::same_as<std::string_view>;
54template <
typename Field>
55concept FieldConstructibleFromStringView = std::constructible_from<Field, std::string_view>;
57template <
typename Labels>
58concept AllFieldsConvertibleToStringView = []<std::size_t... Is>(std::index_sequence<Is...>) {
59 return (FieldConvertibleToStringView<boost::pfr::tuple_element_t<Is, Labels>> && ...);
60}(std::make_index_sequence<boost::pfr::tuple_size_v<Labels>>{});
62template <
typename Labels>
63concept AllFieldsConstructibleFromStringView = []<std::size_t... Is>(std::index_sequence<Is...>) {
64 return (FieldConstructibleFromStringView<boost::pfr::tuple_element_t<Is, Labels>> && ...);
65}(std::make_index_sequence<boost::pfr::tuple_size_v<Labels>>{});
67template <
typename Labels>
68concept LabelsAggregate = std::is_aggregate_v<Labels> && AllFieldsConvertibleToStringView<Labels>;
70template <
typename Labels>
71auto LabelsStructToViewArray(
const Labels& labels) {
72 constexpr std::size_t kN = boost::pfr::tuple_size_v<Labels>;
73 std::array<std::string_view, kN> result{};
74 boost::pfr::for_each_field(labels, [&result](
const auto& field, std::size_t i) {
75 result[i] = FieldToStringView(field);
80template <
typename Labels>
81constexpr auto GetLabelNames()
noexcept {
82 return boost::pfr::names_as_array<Labels>();
85template <
typename Labels, std::size_t... Is>
86Labels LabelsArrayToStruct(
const utils::FixedArray<std::string>& arr, std::index_sequence<Is...>) {
87 UASSERT(arr.size() ==
sizeof...(Is));
88 return Labels{std::string_view{arr[Is]}...};
91template <
typename Labels>
92Labels LabelsArrayToStruct(
const utils::FixedArray<std::string>& arr) {
93 return LabelsArrayToStruct<Labels>(arr, std::make_index_sequence<boost::pfr::tuple_size_v<Labels>>{});
97struct FixedStringArrayHash {
98 using is_transparent [[maybe_unused]] =
void;
100 template <
typename StringViewRange>
101 std::size_t operator()(
const StringViewRange& arr)
const noexcept {
102 std::size_t hash = arr.size();
103 for (
const auto& s : arr) {
104 boost::hash_combine(hash, std::hash<std::string_view>{}(s));
111struct FixedStringArrayEqual {
112 using is_transparent [[maybe_unused]] =
void;
114 template <
typename StringViewRange1,
typename StringViewRange2>
115 bool operator()(
const StringViewRange1& a,
const StringViewRange2& b)
const noexcept {
117 for (std::size_t i = 0; i < a.size(); ++i) {
118 if (std::string_view{a[i]} != std::string_view{b[i]}) {
127template <
typename Metric>
129 utils::FixedArray<std::string> labels;
132 template <std::size_t N,
typename... Args>
133 explicit ByLabelEntry(
const std::array<std::string_view, N>& views, Args&&... args)
134 : labels(views.begin(), views.end()),
135 metric(std::forward<Args>(args)...)
140template <
typename Metric>
141struct ByLabelEntryHash {
142 using is_transparent [[maybe_unused]] =
void;
144 std::size_t operator()(
const ByLabelEntry<Metric>& entry)
const noexcept {
145 return FixedStringArrayHash{}(entry.labels);
148 template <std::size_t N>
149 std::size_t operator()(
const std::array<std::string_view, N>& key)
const noexcept {
150 return FixedStringArrayHash{}(key);
155template <
typename Metric>
156struct ByLabelEntryEqual {
157 using is_transparent [[maybe_unused]] =
void;
159 bool operator()(
const ByLabelEntry<Metric>& a,
const ByLabelEntry<Metric>& b)
const noexcept {
160 return FixedStringArrayEqual{}(a.labels, b.labels);
163 template <std::size_t N>
164 bool operator()(
const ByLabelEntry<Metric>& a,
const std::array<std::string_view, N>& b)
const noexcept {
165 return FixedStringArrayEqual{}(a.labels, b);
168 template <std::size_t N>
169 bool operator()(
const std::array<std::string_view, N>& a,
const ByLabelEntry<Metric>& b)
const noexcept {
170 return FixedStringArrayEqual{}(a, b.labels);
245template <
typename Labels,
typename Metric>
246requires impl::LabelsAggregate<Labels>
259 requires std::default_initializable<Metric>
261 return Emplace(labels);
268 template <
typename... Args>
269 requires std::constructible_from<Metric, Args...>
270 Metric&
Emplace(
const Labels& labels, Args&&... args) {
271 const auto view_array = impl::LabelsStructToViewArray(labels);
272 auto [entry, inserted] = set_.TryEmplace(view_array, view_array, std::forward<Args>(args)...);
280 const auto view_array = impl::LabelsStructToViewArray(labels);
281 auto ref = set_.Find(view_array);
302 requires impl::DumpableMetric<Metric>
304 static constexpr auto kLabelNames = impl::GetLabelNames<Labels>();
305 static constexpr std::size_t kFieldCount = boost::pfr::tuple_size_v<Labels>;
307 std::vector<LabelView> label_views;
308 label_views.reserve(kFieldCount);
310 storage.set_.Visit([&writer, &label_views](
const Entry& entry) {
312 for (std::size_t i = 0; i < kFieldCount; ++i) {
313 label_views.emplace_back(kLabelNames[i], entry.labels[i]);
315 writer.ValueWithLabels(entry.metric, label_views);
321 requires impl::ResettableMetric<Metric>
323 storage.set_.Visit([](Entry& entry) { ResetMetric(entry.metric); });
327 using Entry = impl::ByLabelEntry<Metric>;
328 using Set =
concurrent::impl::MonotonicConcurrentSet<
330 impl::ByLabelEntryHash<Metric>,
331 impl::ByLabelEntryEqual<Metric>>;