diff --git a/pp/WORKSPACE b/pp/WORKSPACE index 6e6ee41b6..717cf53bd 100644 --- a/pp/WORKSPACE +++ b/pp/WORKSPACE @@ -118,6 +118,9 @@ http_archive( "//third_party/patches/roaring:0001-disable-test-dependencies.patch", "//third_party/patches/roaring:0002-svacer_fixes.patch", ], + patch_cmds = [ + "cp cpp/* include/roaring" + ], sha256 = "a037e12a3f7c8c2abb3e81fc9669c23e274ffa2d8670d2034a2e05969e53689b", strip_prefix = "CRoaring-1.3.0/", url = "https://github.com/RoaringBitmap/CRoaring/archive/refs/tags/v1.3.0.zip", diff --git a/pp/primitives/label_set.h b/pp/primitives/label_set.h index c268fea8f..4ff3609f8 100644 --- a/pp/primitives/label_set.h +++ b/pp/primitives/label_set.h @@ -8,8 +8,6 @@ namespace PromPP::Primitives { template class Container = BareBones::Vector> class BasicLabelSet { - Container labels_; - public: using label_type = LabelType; @@ -68,6 +66,17 @@ class BasicLabelSet { } } + template + PROMPP_ALWAYS_INLINE void append_unsorted(const LabelSet& label_set) { + labels_.reserve(labels_.size() + label_set.size()); + + for (const auto& label : label_set) { + append(label.first, label.second); + } + + sort(); + } + template PROMPP_ALWAYS_INLINE void add(const LabelSet& label_set) { labels_.reserve(labels_.size() + label_set.size()); @@ -102,6 +111,8 @@ class BasicLabelSet { [[nodiscard]] PROMPP_ALWAYS_INLINE const_iterator end() const noexcept { return labels_.end(); } PROMPP_ALWAYS_INLINE iterator end() noexcept { return labels_.end(); } + [[nodiscard]] PROMPP_ALWAYS_INLINE const LabelType& operator[](uint32_t index) const noexcept { return labels_[index]; } + [[nodiscard]] PROMPP_ALWAYS_INLINE uint32_t allocated_memory() const noexcept { return BareBones::mem::allocated_memory(labels_); } template @@ -169,6 +180,13 @@ class BasicLabelSet { }; [[nodiscard]] PROMPP_ALWAYS_INLINE Names names() const noexcept { return Names(*this); } + + private: + Container labels_; + + PROMPP_ALWAYS_INLINE void sort() noexcept { + std::ranges::sort(labels_, [](const auto& a, const auto& b) { return a.first < b.first; }); + } }; using LabelSet = BasicLabelSet; diff --git a/pp/primitives/labels_builder.h b/pp/primitives/labels_builder.h index d9253f0f7..cc458c9c8 100644 --- a/pp/primitives/labels_builder.h +++ b/pp/primitives/labels_builder.h @@ -9,20 +9,11 @@ #include "primitives.h" namespace PromPP::Primitives { -class LabelsBuilderStateMap { - PromPP::Primitives::LabelViewSet building_buf_view_; - PromPP::Primitives::LabelSet building_buf_; - phmap::flat_hash_map buffer_; - template - void sort_labels(Labels& labels) { - std::ranges::sort(labels, [](const auto& a, const auto& b) { - if (a.first == b.first) { - return a.second < b.second; - } - return a.first < b.first; - }); - } +class LabelsBuilder { + LabelViewSet building_buf_view_; + LabelSet building_buf_; + phmap::flat_hash_map buffer_; public: // del add label name to remove from label set. @@ -33,8 +24,8 @@ class LabelsBuilderStateMap { // extract we extract(move) the lebel from the builder. PROMPP_ALWAYS_INLINE Label extract(const std::string_view& lname) { - if (auto it = buffer_.find(lname); it != buffer_.end()) { - auto node = buffer_.extract(it); + if (const auto it = buffer_.find(lname); it != buffer_.end()) { + auto&& node = buffer_.extract(it); return {std::move(const_cast(node.key())), std::move(node.mapped())}; } @@ -42,17 +33,17 @@ class LabelsBuilderStateMap { } // get returns the value for the label with the given name. Returns an empty string if the label doesn't exist. - PROMPP_ALWAYS_INLINE std::string_view get(const std::string_view lname) { - if (auto it = buffer_.find(lname); it != buffer_.end()) { - return (*it).second; + PROMPP_ALWAYS_INLINE std::string_view get(const std::string_view& lname) { + if (const auto it = buffer_.find(lname); it != buffer_.end()) { + return it->second; } return ""; } // contains check the given name if exist. - [[nodiscard]] PROMPP_ALWAYS_INLINE bool contains(const std::string_view lname) const noexcept { - if (auto it = buffer_.find(lname); it != buffer_.end()) { + [[nodiscard]] PROMPP_ALWAYS_INLINE bool contains(const std::string_view& lname) const noexcept { + if (const auto it = buffer_.find(lname); it != buffer_.end()) { return true; } @@ -68,7 +59,7 @@ class LabelsBuilderStateMap { } if (auto it = buffer_.find(lname); it != buffer_.end()) { - (*it).second = lvalue; + it->second = lvalue; return; } @@ -76,61 +67,34 @@ class LabelsBuilderStateMap { } // returns size of building labels. - PROMPP_ALWAYS_INLINE size_t size() { return buffer_.size(); } + [[nodiscard]] PROMPP_ALWAYS_INLINE size_t size() const { return buffer_.size(); } // returns true if ls represents an empty set of labels. - PROMPP_ALWAYS_INLINE bool is_empty() { return buffer_.size() == 0; } + [[nodiscard]] PROMPP_ALWAYS_INLINE bool is_empty() const { return buffer_.empty(); } // label_view_set - returns the label_view set from the builder. If no modifications were made, the original labels are returned. PROMPP_ALWAYS_INLINE const LabelViewSet& label_view_set() { building_buf_view_.clear(); - for (const auto& it : buffer_) { - if (it.second == "") [[unlikely]] { - continue; - } - - building_buf_view_.add(LabelView{it.first, it.second}); - } - - if (building_buf_view_.size() != 0) { - sort_labels(building_buf_view_); - } - + building_buf_view_.append_unsorted(buffer_); return building_buf_view_; } // label_set - returns the label set from the builder. If no modifications were made, the original labels are returned. PROMPP_ALWAYS_INLINE const LabelSet& label_set() { building_buf_.clear(); - - for (const auto& it : buffer_) { - if (it.second == "") [[unlikely]] { - continue; - } - - building_buf_.add(Label{it.first, it.second}); - } - - if (building_buf_.size() != 0) { - sort_labels(building_buf_); - } - + building_buf_.append_unsorted(buffer_); return building_buf_; } - // range - calls f on each label in the builder. - // TODO without copy buffer_, all changes in a another cycle. + // range - calls f on each label in the builder. Don't modify LabelsBuilderStateMap inside callback template - PROMPP_ALWAYS_INLINE void range(Callback func) { - // take a copy of add and del, so they are unaffected by calls to set() or del(). - phmap::flat_hash_map cbuffer_ = buffer_; - - for (const auto& it : cbuffer_) { - if (it.second == "") [[unlikely]] { + PROMPP_ALWAYS_INLINE void range(Callback func) const { + for (const auto& it : buffer_) { + if (it.second.empty()) [[unlikely]] { continue; } - if (bool ok = func(it.first, it.second); !ok) { + if (!func(it.first, it.second)) { return; } } @@ -145,83 +109,15 @@ class LabelsBuilderStateMap { // reset - clears all current state for the builder. template - PROMPP_ALWAYS_INLINE void reset(SomeLabelSet& base) { - building_buf_view_.clear(); - building_buf_.clear(); - buffer_.clear(); + PROMPP_ALWAYS_INLINE void reset(const SomeLabelSet& base) { + reset(); for (const auto& [lname, lvalue] : base) { - if (lvalue == "") { - continue; + if (!lvalue.empty()) [[likely]] { + buffer_[lname] = lvalue; } - - buffer_[lname] = lvalue; } } }; -// LabelsBuilder - builder for label set. -template -class LabelsBuilder { - BuilderState& state_; - - public: - PROMPP_ALWAYS_INLINE explicit LabelsBuilder(BuilderState& state) : state_(state) {} - - template - PROMPP_ALWAYS_INLINE explicit LabelsBuilder(BuilderState& state, SomeLabelSet* ls) : state_(state) { - reset(ls); - } - - // del - add label name to remove from label set. - template - PROMPP_ALWAYS_INLINE void del(const LNameType& lname) { - state_.del(lname); - } - - // extract we extract(move) the lebel from the builder. - PROMPP_ALWAYS_INLINE Label extract(const std::string_view& lname) { return state_.extract(lname); } - - // get - returns the value for the label with the given name. Returns an empty string if the label doesn't exist. - PROMPP_ALWAYS_INLINE std::string_view get(const std::string_view lname) { return state_.get(lname); } - - // contains check the given name if exist. - [[nodiscard]] PROMPP_ALWAYS_INLINE bool contains(const std::string_view lname) const noexcept { return state_.contains(lname); } - - // returns size of building labels. - PROMPP_ALWAYS_INLINE size_t size() { return state_.size(); } - - // returns true if ls represents an empty set of labels. - PROMPP_ALWAYS_INLINE bool is_empty() { return state_.is_empty(); } - - // label_view_set - returns the label_view set from the builder. If no modifications were made, the original labels are returned. - PROMPP_ALWAYS_INLINE const PromPP::Primitives::LabelViewSet& label_view_set() { return state_.label_view_set(); } - - // label_set - returns the label set from the builder. If no modifications were made, the original labels are returned. - PROMPP_ALWAYS_INLINE const PromPP::Primitives::LabelSet& label_set() { return state_.label_set(); } - - // range - calls f on each label in the builder. - template - PROMPP_ALWAYS_INLINE void range(Callback func) { - state_.range(func); - } - - // reset - clears all current state for the builder. - PROMPP_ALWAYS_INLINE void reset() { state_.reset(); } - - // reset - clears all current state for the builder and init from LabelSet. - template - PROMPP_ALWAYS_INLINE void reset(const SomeLabelSet& ls) { - state_.reset(ls); - } - - // set - the name/value pair as a label. A value of "" means delete that label. - template - PROMPP_ALWAYS_INLINE void set(const LNameType& lname, const LValueType& lvalue) { - state_.set(lname, lvalue); - } - - PROMPP_ALWAYS_INLINE LabelsBuilder(LabelsBuilder&&) noexcept = default; - PROMPP_ALWAYS_INLINE ~LabelsBuilder() = default; -}; } // namespace PromPP::Primitives \ No newline at end of file diff --git a/pp/primitives/tests/labels_builder_tests.cpp b/pp/primitives/tests/labels_builder_tests.cpp index 6454b4b48..8ef24b03c 100644 --- a/pp/primitives/tests/labels_builder_tests.cpp +++ b/pp/primitives/tests/labels_builder_tests.cpp @@ -5,8 +5,7 @@ namespace { struct TestLabelsBuilder : public testing::Test { - PromPP::Primitives::LabelsBuilderStateMap builder_state_; - PromPP::Primitives::LabelsBuilder builder_{builder_state_}; + PromPP::Primitives::LabelsBuilder builder_; PromPP::Primitives::LabelViewSet ls_view_; PromPP::Primitives::LabelSet ls_; std::vector> DATA{{"qwe", "ewq"}, {"asd", "dsa"}, {"zxc", "cxz"}}; @@ -68,7 +67,7 @@ TEST_F(TestLabelsBuilder, SetEmpty) { ls_.add({DATA[i].first, DATA[i].second}); } - std::string empty = ""; + const std::string empty; builder_.set(DATA[0].first, empty); EXPECT_EQ(builder_.label_view_set(), ls_view_); @@ -85,7 +84,7 @@ TEST_F(TestLabelsBuilder, SetChange) { ls_.add({lname, lvalue}); } - std::string value = "zxcv"; + const std::string value = "zxcv"; builder_.set(DATA[0].first, value); EXPECT_NE(builder_.label_view_set(), ls_view_); @@ -110,7 +109,7 @@ TEST_F(TestLabelsBuilder, Extract) { builder_.set(lname, lvalue); } - auto l = builder_.extract(DATA[0].first); + const auto l = builder_.extract(DATA[0].first); EXPECT_EQ(DATA[0], l); EXPECT_EQ(builder_.get(l.first), ""); @@ -123,7 +122,7 @@ TEST_F(TestLabelsBuilder, Del) { builder_.del(DATA[0].first); - std::string_view b_lvalue = builder_.get(DATA[0].first); + const std::string_view b_lvalue = builder_.get(DATA[0].first); EXPECT_EQ(b_lvalue, ""); } @@ -142,72 +141,6 @@ TEST_F(TestLabelsBuilder, SetDelSet) { EXPECT_EQ(builder_.label_set(), ls_); } -TEST_F(TestLabelsBuilder, Range) { - for (auto& [lname, lvalue] : DATA) { - ls_view_.add({lvalue, lname}); - builder_.set(lname, lvalue); - ls_.add({lvalue, lname}); - } - - builder_.range([&](LNameType& lname, LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { - builder_.del(lname); - builder_.set(lvalue, lname); - return true; - }); - - EXPECT_EQ(builder_.label_view_set(), ls_view_); - EXPECT_EQ(builder_.label_set(), ls_); -} - -TEST_F(TestLabelsBuilder, RangeFastExit) { - for (size_t i = 0; i < DATA.size(); ++i) { - builder_.set(DATA[i].first, DATA[i].second); - if (i == 2) { - // for last label not swap lname and lvalue - ls_view_.add({DATA[i].first, DATA[i].second}); - ls_.add({DATA[i].first, DATA[i].second}); - continue; - } - ls_view_.add({DATA[i].second, DATA[i].first}); - ls_.add({DATA[i].second, DATA[i].first}); - } - - size_t count{0}; - builder_.range([&](LNameType& lname, LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { - builder_.del(lname); - builder_.set(lvalue, lname); - ++count; - if (count == 2) { - return false; - }; - return true; - }); - - EXPECT_EQ(count, 2); - EXPECT_EQ(builder_.label_view_set(), ls_view_); - EXPECT_EQ(builder_.label_set(), ls_); -} - -TEST_F(TestLabelsBuilder, ResetRange) { - PromPP::Primitives::LabelViewSet ls; - for (auto& [lname, lvalue] : DATA) { - ls.add({lname, lvalue}); - ls_view_.add({lvalue, lname}); - ls_.add({lvalue, lname}); - } - - builder_.reset(ls); - - builder_.range([&](LNameType& lname, LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { - builder_.del(lname); - builder_.set(lvalue, lname); - return true; - }); - - EXPECT_EQ(builder_.label_view_set(), ls_view_); - EXPECT_EQ(builder_.label_set(), ls_); -} - TEST_F(TestLabelsBuilder, NotIsEmpty) { for (auto& [lname, lvalue] : DATA) { builder_.set(lname, lvalue); diff --git a/pp/prometheus/relabeler.h b/pp/prometheus/relabeler.h index 97ed91c1c..f6bb8df60 100644 --- a/pp/prometheus/relabeler.h +++ b/pp/prometheus/relabeler.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -29,14 +28,19 @@ struct MetricLimits { size_t label_value_length_limit{0}; size_t sample_limit{0}; - PROMPP_ALWAYS_INLINE bool label_limit_exceeded(size_t labels_count) { return label_limit > 0 && labels_count > label_limit; } - - PROMPP_ALWAYS_INLINE bool samples_limit_exceeded(size_t samples_count) { return sample_limit > 0 && samples_count >= sample_limit; } + [[nodiscard]] PROMPP_ALWAYS_INLINE bool label_limit_exceeded(size_t labels_count) const { return label_limit > 0 && labels_count > label_limit; } + [[nodiscard]] PROMPP_ALWAYS_INLINE bool samples_limit_exceeded(size_t samples_count) const { return sample_limit > 0 && samples_count >= sample_limit; } + [[nodiscard]] PROMPP_ALWAYS_INLINE bool label_name_length_limit_exceeded(size_t label_name_length) const { + return label_name_length_limit > 0 && label_name_length > label_name_length_limit; + } + [[nodiscard]] PROMPP_ALWAYS_INLINE bool label_value_length_limit_exceeded(size_t label_value_length) const { + return label_value_length_limit > 0 && label_value_length > label_value_length_limit; + } }; // hard_validate on empty, name label(__name__) mandatory, valid label name and value) validate label set. template -PROMPP_ALWAYS_INLINE void hard_validate(relabelStatus& rstatus, LabelsBuilder& builder, MetricLimits* limits) { +PROMPP_ALWAYS_INLINE void hard_validate(relabelStatus& rstatus, LabelsBuilder& builder, const MetricLimits* limits) { if (rstatus == rsDrop) { return; } @@ -54,13 +58,17 @@ PROMPP_ALWAYS_INLINE void hard_validate(relabelStatus& rstatus, LabelsBuilder& b } // validate labels - builder.range([&](LNameType& lname, LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { - if (lname == kMetricLabelName && !metric_name_value_is_valid(lvalue)) { - rstatus = rsInvalid; - return false; + builder.range([&](const auto& lname, const auto& lvalue) PROMPP_LAMBDA_INLINE -> bool { + if (lname == kMetricLabelName) [[unlikely]] { + if (!metric_name_value_is_valid(lvalue)) [[unlikely]] { + rstatus = rsInvalid; + return false; + } + + return true; } - if (!label_name_is_valid(lname) || !label_value_is_valid(lvalue)) { + if (!label_name_is_valid(lname) || !label_value_is_valid(lvalue)) [[unlikely]] { rstatus = rsInvalid; return false; } @@ -86,13 +94,13 @@ PROMPP_ALWAYS_INLINE void hard_validate(relabelStatus& rstatus, LabelsBuilder& b } // check limit len label name and value - builder.range([&](LNameType& lname, LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { - if (limits->label_name_length_limit > 0 && lname.size() > limits->label_name_length_limit) { + builder.range([&](const auto& lname, auto& lvalue) PROMPP_LAMBDA_INLINE -> bool { + if (limits->label_name_length_limit_exceeded(lname.size())) { rstatus = rsInvalid; return false; } - if (limits->label_value_length_limit > 0 && lvalue.size() > limits->label_value_length_limit) { + if (limits->label_value_length_limit_exceeded(lvalue.size())) { rstatus = rsInvalid; return false; } @@ -106,7 +114,7 @@ PROMPP_ALWAYS_INLINE void hard_validate(relabelStatus& rstatus, LabelsBuilder& b // samples - incoming samples; // ls_id - relabeling ls id from lss; struct InnerSerie { - PromPP::Primitives::Sample sample; + Primitives::Sample sample; uint32_t ls_id; PROMPP_ALWAYS_INLINE bool operator==(const InnerSerie& rt) const noexcept = default; @@ -121,17 +129,29 @@ class InnerSeries { BareBones::Vector data_; public: - PROMPP_ALWAYS_INLINE const BareBones::Vector& data() const { return data_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE const BareBones::Vector& data() const { return data_; } - PROMPP_ALWAYS_INLINE size_t size() const { return size_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE size_t size() const { return size_; } PROMPP_ALWAYS_INLINE void reserve(size_t n) { data_.reserve(n); } - PROMPP_ALWAYS_INLINE void emplace_back(const PromPP::Primitives::Sample& sample, const uint32_t& ls_id) { + PROMPP_ALWAYS_INLINE void emplace_back(const Primitives::Sample& sample, uint32_t ls_id) { data_.emplace_back(sample, ls_id); ++size_; } + PROMPP_ALWAYS_INLINE void emplace_back(auto const& samples, uint32_t ls_id) { + data_.reserve_and_write(samples.size(), [&](InnerSerie* series_buffer, uint32_t series_size) { + for (const auto& sample : samples) { + std::construct_at(series_buffer, sample, ls_id); + ++series_buffer; + } + return series_size; + }); + + size_ += samples.size(); + } + PROMPP_ALWAYS_INLINE void clear() noexcept { data_.clear(); size_ = 0; @@ -145,8 +165,8 @@ class InnerSeries { // hash - hash sum from ls; // ls_id - incoming ls id from lss; struct RelabeledSerie { - PromPP::Primitives::LabelSet ls; - BareBones::Vector samples; + Primitives::LabelSet ls; + BareBones::Vector samples; size_t hash; uint32_t ls_id; }; @@ -160,17 +180,25 @@ class RelabeledSeries { std::vector data_; public: - PROMPP_ALWAYS_INLINE const std::vector& data() const { return data_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE const std::vector& data() const { return data_; } - PROMPP_ALWAYS_INLINE size_t size() const { return size_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE size_t size() const { return size_; } - PROMPP_ALWAYS_INLINE void emplace_back(PromPP::Primitives::LabelSet& ls, - const BareBones::Vector& samples, + PROMPP_ALWAYS_INLINE void emplace_back(const Primitives::LabelSet& ls, + const BareBones::Vector& samples, const size_t hash, const uint32_t ls_id) { data_.emplace_back(ls, samples, hash, ls_id); ++size_; } + + PROMPP_ALWAYS_INLINE void emplace_back(const Primitives::LabelSet& ls, + BareBones::Vector&& samples, + const size_t hash, + const uint32_t ls_id) { + data_.emplace_back(ls, std::move(samples), hash, ls_id); + ++size_; + } }; // CacheValue - value for cache map. @@ -189,15 +217,15 @@ struct IncomingAndRelabeledLsID { }; // RelabelerStateUpdate - container for update states. -using RelabelerStateUpdate = PromPP::Primitives::Go::Slice; +using RelabelerStateUpdate = Primitives::Go::Slice; class NoOpStaleNaNsState { public: - PROMPP_ALWAYS_INLINE void add_input([[maybe_unused]] uint32_t id) {} - PROMPP_ALWAYS_INLINE void add_target([[maybe_unused]] uint32_t id) {} + PROMPP_ALWAYS_INLINE static void add_input([[maybe_unused]] uint32_t id) {} + PROMPP_ALWAYS_INLINE static void add_target([[maybe_unused]] uint32_t id) {} template - PROMPP_ALWAYS_INLINE void swap([[maybe_unused]] InputCallback input_fn, [[maybe_unused]] TargetCallback target_fn) {} + PROMPP_ALWAYS_INLINE static void swap([[maybe_unused]] InputCallback input_fn, [[maybe_unused]] TargetCallback target_fn) {} }; // StaleNaNsState state for stale nans. @@ -208,8 +236,6 @@ class StaleNaNsState { roaring::Roaring prev_target_bitset_{}; public: - PROMPP_ALWAYS_INLINE explicit StaleNaNsState() {} - PROMPP_ALWAYS_INLINE void add_input(uint32_t id) { input_bitset_.add(id); } PROMPP_ALWAYS_INLINE void add_target(uint32_t id) { target_bitset_.add(id); } @@ -235,20 +261,14 @@ class StaleNaNsState { // Cache stateless cache for relabeler. class Cache { size_t cache_allocated_memory_{0}; - phmap::parallel_flat_hash_map, - std::equal_to<>, - BareBones::Allocator>> - cache_relabel_{{}, {}, BareBones::Allocator>{cache_allocated_memory_}}; + phmap::parallel_flat_hash_map, std::equal_to<>, BareBones::Allocator>> + cache_relabel_{{}, {}, BareBones::Allocator>{cache_allocated_memory_}}; roaring::Roaring cache_keep_{}; roaring::Roaring cache_drop_{}; public: - PROMPP_ALWAYS_INLINE explicit Cache() {} - // allocated_memory return size of allocated memory for caches. - PROMPP_ALWAYS_INLINE size_t allocated_memory() const noexcept { + [[nodiscard]] PROMPP_ALWAYS_INLINE size_t allocated_memory() const noexcept { return cache_allocated_memory_ + cache_keep_.getSizeInBytes() + cache_drop_.getSizeInBytes(); } @@ -269,13 +289,13 @@ class Cache { cache_drop_.runOptimize(); } - PROMPP_ALWAYS_INLINE double part_of_drops() { + [[nodiscard]] PROMPP_ALWAYS_INLINE double part_of_drops() const { if (cache_drop_.cardinality() == 0) { return 0; } return std::bit_cast(cache_drop_.cardinality()) / - std::bit_cast(cache_drop_.cardinality() + cache_keep_.cardinality() + static_cast(cache_relabel_.size())); + std::bit_cast(cache_drop_.cardinality() + cache_keep_.cardinality() + cache_relabel_.size()); } struct CheckResult { @@ -293,13 +313,12 @@ class Cache { template PROMPP_ALWAYS_INLINE CheckResult check(const InputLSS& input_lss, const TargetLSS& target_lss, LabelSet& label_set, size_t hash) { - if (std::optional ls_id = input_lss.find(label_set, hash); ls_id.has_value()) { - auto res = check_input(ls_id.value()); - if (res.status != CheckResult::kNotFound) { + if (const auto ls_id = input_lss.find(label_set, hash); ls_id.has_value()) { + if (auto res = check_input(ls_id.value()); res.status != CheckResult::kNotFound) { return res; } } - if (std::optional ls_id = target_lss.find(label_set, hash); ls_id.has_value()) { + if (const auto ls_id = target_lss.find(label_set, hash); ls_id.has_value()) { return check_target(ls_id.value()); } return {}; @@ -310,14 +329,14 @@ class Cache { return {.status = CheckResult::Status::kDrop}; } - if (auto it = cache_relabel_.find(ls_id); it != cache_relabel_.end()) { + if (const auto it = cache_relabel_.find(ls_id); it != cache_relabel_.end()) { return {.status = CheckResult::Status::kRelabel, .shard_id = it->second.shard_id, .ls_id = it->second.ls_id, .source_ls_id = ls_id}; } return {}; } - PROMPP_ALWAYS_INLINE CheckResult check_target(uint32_t ls_id) { + [[nodiscard]] PROMPP_ALWAYS_INLINE CheckResult check_target(uint32_t ls_id) const { if (cache_keep_.contains(ls_id)) { return {.status = CheckResult::Status::kKeep, .ls_id = ls_id}; } @@ -335,7 +354,7 @@ class Cache { }; struct RelabelerOptions { - PromPP::Primitives::Go::SliceView> target_labels{}; + Primitives::Go::SliceView> target_labels{}; MetricLimits* metric_limits{nullptr}; bool honor_labels{false}; bool track_timestamps_staleness{false}; @@ -351,21 +370,20 @@ struct RelabelerOptions { // shard_id_ - current shard id; // log_shards_ - logarithm to the base 2 of total shards count; class PerShardRelabeler { - std::stringstream buf_; - PromPP::Primitives::LabelsBuilderStateMap builder_state_; - std::vector external_labels_{}; - PromPP::Primitives::TimeseriesSemiview timeseries_buf_; + std::string buf_; + Primitives::LabelsBuilder builder_; + std::vector external_labels_{}; + Primitives::TimeseriesSemiview timeseries_buf_; StatelessRelabeler* stateless_relabeler_; uint16_t number_of_shards_; uint16_t shard_id_; public: // PerShardRelabeler - constructor. Init only with pre-initialized LSS* and StatelessRelabeler*. - PROMPP_ALWAYS_INLINE PerShardRelabeler( - PromPP::Primitives::Go::SliceView>& external_labels, - StatelessRelabeler* stateless_relabeler, - const uint16_t number_of_shards, - const uint16_t shard_id) + PROMPP_ALWAYS_INLINE PerShardRelabeler(Primitives::Go::SliceView>& external_labels, + StatelessRelabeler* stateless_relabeler, + const uint16_t number_of_shards, + const uint16_t shard_id) : stateless_relabeler_(stateless_relabeler), number_of_shards_(number_of_shards), shard_id_(shard_id) { if (stateless_relabeler_ == nullptr) [[unlikely]] { throw BareBones::Exception(0xabd6db40882fd6aa, "stateless relabeler is null pointer"); @@ -378,18 +396,18 @@ class PerShardRelabeler { } private: - PROMPP_ALWAYS_INLINE bool resolve_timestamps(PromPP::Primitives::Timestamp def_timestamp, - BareBones::Vector& samples, - const RelabelerOptions& o) { + PROMPP_ALWAYS_INLINE static bool resolve_timestamps(Primitives::Timestamp def_timestamp, + BareBones::Vector& samples, + const RelabelerOptions& o) { // skip resolve without stalenans - if (def_timestamp == PromPP::Primitives::kNullTimestamp) { + if (def_timestamp == Primitives::kNullTimestamp) { return false; } bool track_staleness{true}; for (auto& sample : samples) { // replace null timestamp on def timestamp - if (sample.timestamp() == PromPP::Primitives::kNullTimestamp) { + if (sample.timestamp() == Primitives::kNullTimestamp) { sample.timestamp() = def_timestamp; continue; } @@ -426,14 +444,15 @@ class PerShardRelabeler { const Hashdex& hashdex, const RelabelerOptions& o, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, - PromPP::Primitives::Go::SliceView& shards_relabeled_series, + Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_relabeled_series, StNaNsState& stale_nan_state, - PromPP::Primitives::Timestamp def_timestamp) { + Primitives::Timestamp def_timestamp) { assert(number_of_shards_ > 0); - size_t n = std::min(static_cast(hashdex.size()), static_cast((hashdex.size() * (1 - cache.part_of_drops()) * 1.1) / number_of_shards_)); - for (auto i = 0; i < number_of_shards_; ++i) { + const size_t n = + std::min(static_cast(hashdex.size()), static_cast((hashdex.size() * (1 - cache.part_of_drops()) * 1.1) / number_of_shards_)); + for (uint16_t i = 0; i < number_of_shards_; ++i) { if (shards_inner_series[i]->size() >= n) { continue; } @@ -441,7 +460,6 @@ class PerShardRelabeler { shards_inner_series[i]->reserve(n); } - PromPP::Primitives::LabelsBuilder builder{builder_state_}; size_t samples_count{0}; for (auto it = skip_shard_inner_series(hashdex, shards_inner_series[shard_id_]->size()); it != hashdex.end(); ++it) { @@ -455,14 +473,9 @@ class PerShardRelabeler { Cache::CheckResult check_result = cache.check(input_lss, target_lss, timeseries_buf_.label_set(), it->hash()); switch (check_result.status) { case Cache::CheckResult::kNotFound: { - builder.reset(timeseries_buf_.label_set()); - auto rstatus = relabel(o, builder); - switch (rstatus) { - case rsDrop: { - cache.add_drop(input_lss.find_or_emplace(timeseries_buf_.label_set(), it->hash())); - ++stats.series_drop; - continue; - } + builder_.reset(timeseries_buf_.label_set()); + switch (relabel(o, builder_)) { + case rsDrop: case rsInvalid: { cache.add_drop(input_lss.find_or_emplace(timeseries_buf_.label_set(), it->hash())); ++stats.series_drop; @@ -472,11 +485,11 @@ class PerShardRelabeler { auto ls_id = target_lss.find_or_emplace(timeseries_buf_.label_set(), it->hash()); cache.add_keep(ls_id); auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); if (o.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_target(ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { + for (const Primitives::Sample& sample : samples) { shards_inner_series[shard_id_]->emplace_back(sample, ls_id); } ++stats.series_added; @@ -485,11 +498,11 @@ class PerShardRelabeler { } case rsRelabel: { auto ls_id = input_lss.find_or_emplace(timeseries_buf_.label_set(), it->hash()); - PromPP::Primitives::LabelSet new_label_set = builder.label_set(); + const auto& new_label_set = builder_.label_set(); size_t new_hash = hash_value(new_label_set); - size_t new_shard_id = new_hash % number_of_shards_; + const size_t new_shard_id = new_hash % number_of_shards_; auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); if (o.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_input(ls_id); } @@ -504,11 +517,11 @@ class PerShardRelabeler { } case Cache::CheckResult::kKeep: { auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); if (o.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_target(check_result.ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { + for (const Primitives::Sample& sample : samples) { shards_inner_series[shard_id_]->emplace_back(sample, check_result.ls_id); } @@ -516,11 +529,11 @@ class PerShardRelabeler { } case Cache::CheckResult::kRelabel: { auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); if (o.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_input(check_result.source_ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { + for (const Primitives::Sample& sample : samples) { shards_inner_series[check_result.shard_id]->emplace_back(sample, check_result.ls_id); } @@ -530,7 +543,7 @@ class PerShardRelabeler { continue; } - stats.samples_added += static_cast(timeseries_buf_.samples().size()); + stats.samples_added += timeseries_buf_.samples().size(); if (o.metric_limits == nullptr) { continue; @@ -542,15 +555,15 @@ class PerShardRelabeler { } } - PromPP::Primitives::Sample smpl{def_timestamp, kStaleNan}; + const Primitives::Sample smpl{def_timestamp, kStaleNan}; stale_nan_state.swap( [&](uint32_t ls_id) { - if (auto res = cache.check_input(ls_id); res.status == Cache::CheckResult::kRelabel) { + if (const auto res = cache.check_input(ls_id); res.status == Cache::CheckResult::kRelabel) { shards_inner_series[res.shard_id]->emplace_back(smpl, res.ls_id); } }, [&](uint32_t ls_id) { - if (auto res = cache.check_target(ls_id); res.status == Cache::CheckResult::kKeep) { + if (const auto res = cache.check_target(ls_id); res.status == Cache::CheckResult::kKeep) { shards_inner_series[shard_id_]->emplace_back(smpl, res.ls_id); } }); @@ -564,18 +577,19 @@ class PerShardRelabeler { const Hashdex& hashdex, const RelabelerOptions& o, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_inner_series, StNaNsState& stale_nan_state, - PromPP::Primitives::Timestamp def_timestamp) { + Primitives::Timestamp def_timestamp) { assert(number_of_shards_ > 0); - size_t n = std::min(static_cast(hashdex.size()), static_cast((hashdex.size() * (1 - cache.part_of_drops()) * 1.1) / number_of_shards_)); - for (auto i = 0; i < number_of_shards_; ++i) { + const size_t n = + std::min(static_cast(hashdex.size()), static_cast((hashdex.size() * (1 - cache.part_of_drops()) * 1.1) / number_of_shards_)); + for (uint16_t i = 0; i < number_of_shards_; ++i) { shards_inner_series[i]->reserve(n); } size_t samples_count{0}; - PromPP::Primitives::TimeseriesSemiview timeseries_buf; + Primitives::TimeseriesSemiview timeseries_buf; for (const auto& item : hashdex) { if ((item.hash() % number_of_shards_) != shard_id_) { @@ -584,18 +598,18 @@ class PerShardRelabeler { timeseries_buf.clear(); item.read(timeseries_buf); - Cache::CheckResult check_result = cache.check(input_lss, target_lss, timeseries_buf.label_set(), item.hash()); + const auto check_result = cache.check(input_lss, target_lss, timeseries_buf.label_set(), item.hash()); switch (check_result.status) { case Cache::CheckResult::kNotFound: { return false; }; case Cache::CheckResult::kKeep: { auto& samples = timeseries_buf.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); if (o.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_target(check_result.ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { + for (const Primitives::Sample& sample : samples) { shards_inner_series[shard_id_]->emplace_back(sample, check_result.ls_id); } @@ -603,11 +617,11 @@ class PerShardRelabeler { } case Cache::CheckResult::kRelabel: { auto& samples = timeseries_buf.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, o); if (o.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_input(check_result.source_ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { + for (const Primitives::Sample& sample : samples) { shards_inner_series[check_result.shard_id]->emplace_back(sample, check_result.ls_id); } @@ -617,7 +631,7 @@ class PerShardRelabeler { continue; } - stats.samples_added += static_cast(timeseries_buf.samples().size()); + stats.samples_added += timeseries_buf.samples().size(); if (o.metric_limits == nullptr) { continue; @@ -629,15 +643,15 @@ class PerShardRelabeler { } } - PromPP::Primitives::Sample smpl{def_timestamp, kStaleNan}; + const Primitives::Sample smpl{def_timestamp, kStaleNan}; stale_nan_state.swap( [&](uint32_t ls_id) { - if (auto res = cache.check_input(ls_id); res.status == Cache::CheckResult::kRelabel) { + if (const auto res = cache.check_input(ls_id); res.status == Cache::CheckResult::kRelabel) { shards_inner_series[res.shard_id]->emplace_back(smpl, res.ls_id); } }, [&](uint32_t ls_id) { - if (auto res = cache.check_target(ls_id); res.status == Cache::CheckResult::kKeep) { + if (const auto res = cache.check_target(ls_id); res.status == Cache::CheckResult::kKeep) { shards_inner_series[shard_id_]->emplace_back(smpl, res.ls_id); } }); @@ -647,7 +661,7 @@ class PerShardRelabeler { template PROMPP_ALWAYS_INLINE relabelStatus relabel(const RelabelerOptions& o, LabelsBuilder& builder) { - bool changed = inject_target_labels(builder, o); + const bool changed = inject_target_labels(builder, o); relabelStatus rstatus = stateless_relabeler_->relabeling_process(buf_, builder); hard_validate(rstatus, builder, o.metric_limits); @@ -659,7 +673,7 @@ class PerShardRelabeler { } // calculate_samples counts the number of samples excluding stale_nan. - PROMPP_ALWAYS_INLINE size_t calculate_samples(const BareBones::Vector& samples) noexcept { + PROMPP_ALWAYS_INLINE static size_t calculate_samples(const BareBones::Vector& samples) noexcept { size_t samples_count{0}; for (const auto smpl : samples) { if (is_stale_nan(smpl.value())) { @@ -692,9 +706,9 @@ class PerShardRelabeler { return changed; } - std::vector conflicting_exposed_labels; + std::vector conflicting_exposed_labels; for (const auto& [lname, lvalue] : o.target_labels) { - PromPP::Primitives::Label existing_label = target_builder.extract(static_cast(lname)); + Primitives::Label existing_label = target_builder.extract(static_cast(lname)); if (!existing_label.second.empty()) [[likely]] { conflicting_exposed_labels.emplace_back(std::move(existing_label)); } @@ -714,9 +728,9 @@ class PerShardRelabeler { // resolve_conflicting_exposed_labels add prefix to conflicting label name. template - PROMPP_ALWAYS_INLINE void resolve_conflicting_exposed_labels(LabelsBuilder& builder, std::vector& conflicting_exposed_labels) { + PROMPP_ALWAYS_INLINE void resolve_conflicting_exposed_labels(LabelsBuilder& builder, std::vector& conflicting_exposed_labels) { std::stable_sort(conflicting_exposed_labels.begin(), conflicting_exposed_labels.end(), - [](PromPP::Primitives::LabelView a, PromPP::Primitives::LabelView b) { return a.first.size() < b.first.size(); }); + [](const Primitives::LabelView& a, const Primitives::LabelView& b) { return a.first.size() < b.first.size(); }); for (auto& [ln, lv] : conflicting_exposed_labels) { while (true) { @@ -736,11 +750,10 @@ class PerShardRelabeler { const Hashdex& hashdex, const RelabelerOptions& o, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, - PromPP::Primitives::Go::SliceView& shards_relabeled_series) { + Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_relabeled_series) { NoOpStaleNaNsState state{}; - input_relabeling_internal(input_lss, target_lss, cache, hashdex, o, stats, shards_inner_series, shards_relabeled_series, state, - PromPP::Primitives::kNullTimestamp); + input_relabeling_internal(input_lss, target_lss, cache, hashdex, o, stats, shards_inner_series, shards_relabeled_series, state, Primitives::kNullTimestamp); } template @@ -750,10 +763,10 @@ class PerShardRelabeler { const Hashdex& hashdex, const RelabelerOptions& o, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, - PromPP::Primitives::Go::SliceView& shards_relabeled_series, + Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_relabeled_series, StaleNaNsState& state, - PromPP::Primitives::Timestamp def_timestamp) { + Primitives::Timestamp def_timestamp) { input_relabeling_internal(input_lss, target_lss, cache, hashdex, o, stats, shards_inner_series, shards_relabeled_series, state, def_timestamp); } @@ -764,10 +777,9 @@ class PerShardRelabeler { const Hashdex& hashdex, const RelabelerOptions& o, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series) { + Primitives::Go::SliceView& shards_inner_series) { NoOpStaleNaNsState state{}; - return input_relabeling_from_cache_internal(input_lss, target_lss, cache, hashdex, o, stats, shards_inner_series, state, - PromPP::Primitives::kNullTimestamp); + return input_relabeling_from_cache_internal(input_lss, target_lss, cache, hashdex, o, stats, shards_inner_series, state, Primitives::kNullTimestamp); } template @@ -777,25 +789,25 @@ class PerShardRelabeler { const Hashdex& hashdex, const RelabelerOptions& o, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_inner_series, StaleNaNsState& state, - PromPP::Primitives::Timestamp def_timestamp) { + Primitives::Timestamp def_timestamp) { return input_relabeling_from_cache_internal(input_lss, target_lss, cache, hashdex, o, stats, shards_inner_series, state, def_timestamp); } PROMPP_ALWAYS_INLINE void input_collect_stalenans(Cache& cache, - PromPP::Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_inner_series, StaleNaNsState& state, - PromPP::Primitives::Timestamp stale_ts) { - PromPP::Primitives::Sample smpl{stale_ts, kStaleNan}; + Primitives::Timestamp stale_ts) const { + const Primitives::Sample smpl{stale_ts, kStaleNan}; state.swap( [&](uint32_t ls_id) { - if (auto res = cache.check_input(ls_id); res.status == Cache::CheckResult::kRelabel) { + if (const auto res = cache.check_input(ls_id); res.status == Cache::CheckResult::kRelabel) { shards_inner_series[res.shard_id]->emplace_back(smpl, res.ls_id); } }, [&](uint32_t ls_id) { - if (auto res = cache.check_target(ls_id); res.status == Cache::CheckResult::kKeep) { + if (const auto res = cache.check_target(ls_id); res.status == Cache::CheckResult::kKeep) { shards_inner_series[shard_id_]->emplace_back(smpl, res.ls_id); } }); @@ -804,10 +816,10 @@ class PerShardRelabeler { // append_relabeler_series add relabeled ls to lss, add to result and add to cache update(second stage). template - PROMPP_ALWAYS_INLINE void append_relabeler_series(LSS& lss, - InnerSeries* inner_series, - const RelabeledSeries* relabeled_series, - RelabelerStateUpdate* relabeler_state_update) { + PROMPP_ALWAYS_INLINE static void append_relabeler_series(LSS& lss, + InnerSeries* inner_series, + const RelabeledSeries* relabeled_series, + RelabelerStateUpdate* relabeler_state_update) { relabeler_state_update->reserve(relabeler_state_update->size() + relabeled_series->size()); inner_series->reserve(inner_series->size() + relabeled_series->size()); if constexpr (BareBones::concepts::has_reserve) { @@ -825,7 +837,7 @@ class PerShardRelabeler { } // update_relabeler_state - add to cache relabled data(third stage). - PROMPP_ALWAYS_INLINE void update_relabeler_state(Cache& cache, const RelabelerStateUpdate* relabeler_state_update, const uint16_t relabeled_shard_id) { + PROMPP_ALWAYS_INLINE static void update_relabeler_state(Cache& cache, const RelabelerStateUpdate* relabeler_state_update, const uint16_t relabeled_shard_id) { for (const auto& update : *relabeler_state_update) { cache.add_relabel(update.incoming_ls_id, update.relabeled_ls_id, relabeled_shard_id); } @@ -836,18 +848,15 @@ class PerShardRelabeler { PROMPP_ALWAYS_INLINE void output_relabeling(const LSS& lss, Cache& cache, RelabeledSeries* relabeled_series, - PromPP::Primitives::Go::SliceView& incoming_inner_series, - PromPP::Primitives::Go::SliceView& encoders_inner_series) { - std::ranges::for_each(incoming_inner_series, [&](const InnerSeries* inner_series) PROMPP_LAMBDA_INLINE { + Primitives::Go::SliceView& incoming_inner_series, + Primitives::Go::SliceView& encoders_inner_series) { + for (const auto inner_series : incoming_inner_series) { if (inner_series == nullptr || inner_series->size() == 0) { - return; + continue; } - // TODO move ctor builder from ranges for; - PromPP::Primitives::LabelsBuilder builder{builder_state_}; - std::ranges::for_each(inner_series->data(), [&](const InnerSerie& inner_serie) PROMPP_LAMBDA_INLINE { - auto res = cache.check_input(inner_serie.ls_id); + const auto res = cache.check_input(inner_serie.ls_id); if (res.status == Cache::CheckResult::kDrop) { return; } @@ -860,30 +869,27 @@ class PerShardRelabeler { if (inner_serie.ls_id >= lss.size()) [[unlikely]] { throw BareBones::Exception(0x7763a97e1717e835, "ls_id out of range: %d size: %d shard_id: %d", inner_serie.ls_id, lss.size(), shard_id_); } - typename LSS::value_type labels = lss[inner_serie.ls_id]; - builder.reset(labels); - process_external_labels(builder, external_labels_); + builder_.reset(lss[inner_serie.ls_id]); + process_external_labels(builder_, external_labels_); - relabelStatus rstatus = stateless_relabeler_->relabeling_process(buf_, builder); - soft_validate(rstatus, builder); + relabelStatus rstatus = stateless_relabeler_->relabeling_process(buf_, builder_); + soft_validate(rstatus, builder_); if (rstatus == rsDrop) { cache.add_drop(inner_serie.ls_id); return; } - PromPP::Primitives::LabelSet new_label_set = builder.label_set(); - relabeled_series->emplace_back(new_label_set, BareBones::Vector{inner_serie.sample}, hash_value(new_label_set), - inner_serie.ls_id); + const auto& new_label_set = builder_.label_set(); + relabeled_series->emplace_back(new_label_set, BareBones::Vector{inner_serie.sample}, hash_value(new_label_set), inner_serie.ls_id); }); - }); + } cache.optimize(); } // reset set new number_of_shards and external_labels. - PROMPP_ALWAYS_INLINE void reset_to( - const PromPP::Primitives::Go::SliceView>& external_labels, - const uint16_t number_of_shards) { + PROMPP_ALWAYS_INLINE void reset_to(const Primitives::Go::SliceView>& external_labels, + const uint16_t number_of_shards) { number_of_shards_ = number_of_shards; external_labels_.clear(); external_labels_.reserve(external_labels.size()); @@ -891,8 +897,6 @@ class PerShardRelabeler { external_labels_.emplace_back(static_cast(ln), static_cast(lv)); } } - - PROMPP_ALWAYS_INLINE ~PerShardRelabeler() = default; }; // @@ -901,9 +905,9 @@ class PerShardRelabeler { // PerGoroutineRelabeler stateful relabeler for shard goroutines. class PerGoroutineRelabeler { - std::stringstream buf_; - PromPP::Primitives::LabelsBuilderStateMap builder_state_; - PromPP::Primitives::TimeseriesSemiview timeseries_buf_; + std::string buf_; + Primitives::LabelsBuilder builder_; + Primitives::TimeseriesSemiview timeseries_buf_; uint16_t number_of_shards_; uint16_t shard_id_; @@ -913,28 +917,8 @@ class PerGoroutineRelabeler { : number_of_shards_(number_of_shards), shard_id_(shard_id) {} private: - // calculate_samples counts the number of samples excluding stale_nan. - PROMPP_ALWAYS_INLINE size_t calculate_samples(const BareBones::Vector& samples) noexcept { - size_t samples_count{0}; - for (const auto smpl : samples) { - if (is_stale_nan(smpl.value())) { - continue; - } - - ++samples_count; - } - - return samples_count; - } - - // check_target_lss check label_set in target lss. - template - PROMPP_ALWAYS_INLINE Cache::CheckResult check_target_lss(const TargetLSS& target_lss, LabelSet& label_set, size_t hash) { - if (std::optional ls_id = target_lss.find(label_set, hash); ls_id.has_value()) { - return {.status = Cache::CheckResult::Status::kKeep, .ls_id = ls_id.value()}; - } - - return {}; + PROMPP_ALWAYS_INLINE static size_t non_stale_nan_samples_count(const BareBones::Vector& samples) noexcept { + return std::ranges::count_if(samples, [](const Primitives::Sample& sample) { return !is_stale_nan(sample.value()); }); } // inject_target_labels add labels from target to builder. @@ -959,9 +943,9 @@ class PerGoroutineRelabeler { return changed; } - std::vector conflicting_exposed_labels; + std::vector conflicting_exposed_labels; for (const auto& [lname, lvalue] : options.target_labels) { - PromPP::Primitives::Label existing_label = target_builder.extract(static_cast(lname)); + Primitives::Label existing_label = target_builder.extract(static_cast(lname)); if (!existing_label.second.empty()) [[likely]] { conflicting_exposed_labels.emplace_back(std::move(existing_label)); } @@ -986,84 +970,63 @@ class PerGoroutineRelabeler { const Hashdex& hashdex, const RelabelerOptions& options, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_inner_series, StNaNsState& stale_nan_state, - PromPP::Primitives::Timestamp def_timestamp) { - assert(number_of_shards_ > 0); - - size_t n = std::min(static_cast(hashdex.size()), static_cast((hashdex.size() * (1 - cache.part_of_drops()) * 1.1) / number_of_shards_)); - for (auto i = 0; i < number_of_shards_; ++i) { - shards_inner_series[i]->reserve(n); - } - - size_t samples_count{0}; - - for (const auto& item : hashdex) { - if ((item.hash() % number_of_shards_) != shard_id_) { - continue; - } - - timeseries_buf_.clear(); - item.read(timeseries_buf_); + Primitives::Timestamp def_timestamp) { + bool result{true}; + size_t samples_count{}; + fill_inner_series(hashdex, hashdex.begin(), shards_inner_series, [&](auto& item) { Cache::CheckResult check_result = cache.check(input_lss, target_lss, timeseries_buf_.label_set(), item.hash()); switch (check_result.status) { case Cache::CheckResult::kNotFound: { + result = false; return false; }; + case Cache::CheckResult::kKeep: { auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); if (options.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_target(check_result.ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { - shards_inner_series[shard_id_]->emplace_back(sample, check_result.ls_id); - } + shards_inner_series[shard_id_]->emplace_back(samples, check_result.ls_id); break; } + case Cache::CheckResult::kRelabel: { auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); if (options.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_input(check_result.source_ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { - shards_inner_series[check_result.shard_id]->emplace_back(sample, check_result.ls_id); - } + shards_inner_series[check_result.shard_id]->emplace_back(samples, check_result.ls_id); break; } - default: - continue; + + default: { + return true; + } } - stats.samples_added += static_cast(timeseries_buf_.samples().size()); + stats.samples_added += timeseries_buf_.samples().size(); - if (options.metric_limits == nullptr) { - continue; + if (options.metric_limits != nullptr) { + samples_count += non_stale_nan_samples_count(timeseries_buf_.samples()); + if (options.metric_limits->samples_limit_exceeded(samples_count)) { + return false; + } } - samples_count += calculate_samples(timeseries_buf_.samples()); - if (options.metric_limits->samples_limit_exceeded(samples_count)) { - break; - } - } + return true; + }); - PromPP::Primitives::Sample smpl{def_timestamp, kStaleNan}; - stale_nan_state.swap( - [&](uint32_t ls_id) { - if (auto res = cache.check_input(ls_id); res.status == Cache::CheckResult::kRelabel) { - shards_inner_series[res.shard_id]->emplace_back(smpl, res.ls_id); - } - }, - [&](uint32_t ls_id) { - if (auto res = cache.check_target(ls_id); res.status == Cache::CheckResult::kKeep) { - shards_inner_series[shard_id_]->emplace_back(smpl, res.ls_id); - } - }); + if (result) { + add_stale_nans(cache, shards_inner_series, stale_nan_state, def_timestamp); + } - return true; + return result; } template @@ -1074,74 +1037,46 @@ class PerGoroutineRelabeler { const RelabelerOptions& options, const StatelessRelabeler& stateless_relabeler, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, - PromPP::Primitives::Go::SliceView& shards_relabeled_series, + Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_relabeled_series, StNaNsState& stale_nan_state, - PromPP::Primitives::Timestamp def_timestamp) { - assert(number_of_shards_ > 0); - - size_t n = std::min(static_cast(hashdex.size()), static_cast((hashdex.size() * (1 - cache.part_of_drops()) * 1.1) / number_of_shards_)); - for (auto i = 0; i < number_of_shards_; ++i) { - if (shards_inner_series[i]->size() >= n) { - continue; - } - - shards_inner_series[i]->reserve(n); - } - - PromPP::Primitives::LabelsBuilder builder{builder_state_}; - size_t samples_count{0}; - - for (auto it = skip_shard_inner_series(hashdex, shards_inner_series[shard_id_]->size()); it != hashdex.end(); ++it) { - if ((it->hash() % number_of_shards_) != shard_id_) { - continue; - } - - timeseries_buf_.clear(); - it->read(timeseries_buf_); - - Cache::CheckResult check_result = cache.check(input_lss, target_lss, timeseries_buf_.label_set(), it->hash()); + Primitives::Timestamp def_timestamp) { + size_t samples_count{}; + fill_inner_series(hashdex, skip_shard_inner_series(hashdex, shards_inner_series[shard_id_]->size()), shards_inner_series, [&](auto& item) { + Cache::CheckResult check_result = cache.check(input_lss, target_lss, timeseries_buf_.label_set(), item.hash()); switch (check_result.status) { case Cache::CheckResult::kNotFound: { - builder.reset(timeseries_buf_.label_set()); - auto rstatus = relabel(options, stateless_relabeler, builder); - switch (rstatus) { - case rsDrop: { - cache.add_drop(input_lss.find_or_emplace(timeseries_buf_.label_set(), it->hash())); - ++stats.series_drop; - - continue; - } + builder_.reset(timeseries_buf_.label_set()); + switch (relabel(options, stateless_relabeler, builder_)) { + case rsDrop: case rsInvalid: { - cache.add_drop(input_lss.find_or_emplace(timeseries_buf_.label_set(), it->hash())); + cache.add_drop(input_lss.find_or_emplace(timeseries_buf_.label_set(), item.hash())); ++stats.series_drop; - - continue; + return true; } + case rsKeep: { - auto ls_id = target_lss.find_or_emplace(timeseries_buf_.label_set(), it->hash()); + auto ls_id = target_lss.find_or_emplace(timeseries_buf_.label_set(), item.hash()); cache.add_keep(ls_id); auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); if (options.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_target(ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { - shards_inner_series[shard_id_]->emplace_back(sample, ls_id); - } - + shards_inner_series[shard_id_]->emplace_back(samples, ls_id); ++stats.series_added; break; } + case rsRelabel: { - auto ls_id = input_lss.find_or_emplace(timeseries_buf_.label_set(), it->hash()); - PromPP::Primitives::LabelSet new_label_set = builder.label_set(); + auto ls_id = input_lss.find_or_emplace(timeseries_buf_.label_set(), item.hash()); + const auto& new_label_set = builder_.label_set(); size_t new_hash = hash_value(new_label_set); - size_t new_shard_id = new_hash % number_of_shards_; + const size_t new_shard_id = new_hash % number_of_shards_; auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); if (options.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_input(ls_id); } @@ -1155,65 +1090,53 @@ class PerGoroutineRelabeler { break; } + case Cache::CheckResult::kKeep: { auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); if (options.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_target(check_result.ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { - shards_inner_series[shard_id_]->emplace_back(sample, check_result.ls_id); - } - + shards_inner_series[shard_id_]->emplace_back(samples, check_result.ls_id); break; } + case Cache::CheckResult::kRelabel: { auto& samples = timeseries_buf_.samples(); - bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); + const bool all_samples_reseted_to_scrape_ts = resolve_timestamps(def_timestamp, samples, options); if (options.track_timestamps_staleness || all_samples_reseted_to_scrape_ts) { stale_nan_state.add_input(check_result.source_ls_id); } - for (const PromPP::Primitives::Sample& sample : samples) { - shards_inner_series[check_result.shard_id]->emplace_back(sample, check_result.ls_id); - } + shards_inner_series[check_result.shard_id]->emplace_back(samples, check_result.ls_id); + break; + } + + default: { break; } - default: - continue; } - stats.samples_added += static_cast(timeseries_buf_.samples().size()); + stats.samples_added += timeseries_buf_.samples().size(); - if (options.metric_limits == nullptr) { - continue; + if (options.metric_limits != nullptr) { + samples_count += non_stale_nan_samples_count(timeseries_buf_.samples()); + if (options.metric_limits->samples_limit_exceeded(samples_count)) { + return false; + } } - samples_count += calculate_samples(timeseries_buf_.samples()); - if (options.metric_limits->samples_limit_exceeded(samples_count)) { - break; - } - } + return true; + }); - PromPP::Primitives::Sample smpl{def_timestamp, kStaleNan}; - stale_nan_state.swap( - [&](uint32_t ls_id) { - if (auto res = cache.check_input(ls_id); res.status == Cache::CheckResult::kRelabel) { - shards_inner_series[res.shard_id]->emplace_back(smpl, res.ls_id); - } - }, - [&](uint32_t ls_id) { - if (auto res = cache.check_target(ls_id); res.status == Cache::CheckResult::kKeep) { - shards_inner_series[shard_id_]->emplace_back(smpl, res.ls_id); - } - }); + add_stale_nans(cache, shards_inner_series, stale_nan_state, def_timestamp); cache.optimize(); } template PROMPP_ALWAYS_INLINE relabelStatus relabel(const RelabelerOptions& options, const StatelessRelabeler& stateless_relabeler, LabelsBuilder& builder) { - bool changed = inject_target_labels(builder, options); + const bool changed = inject_target_labels(builder, options); relabelStatus rstatus = stateless_relabeler.relabeling_process(buf_, builder); hard_validate(rstatus, builder, options.metric_limits); @@ -1226,9 +1149,9 @@ class PerGoroutineRelabeler { // resolve_conflicting_exposed_labels add prefix to conflicting label name. template - PROMPP_ALWAYS_INLINE void resolve_conflicting_exposed_labels(LabelsBuilder& builder, std::vector& conflicting_exposed_labels) { + PROMPP_ALWAYS_INLINE void resolve_conflicting_exposed_labels(LabelsBuilder& builder, std::vector& conflicting_exposed_labels) { std::stable_sort(conflicting_exposed_labels.begin(), conflicting_exposed_labels.end(), - [](PromPP::Primitives::LabelView a, PromPP::Primitives::LabelView b) { return a.first.size() < b.first.size(); }); + [](const Primitives::LabelView& a, const Primitives::LabelView& b) { return a.first.size() < b.first.size(); }); for (auto& [ln, lv] : conflicting_exposed_labels) { while (true) { @@ -1241,18 +1164,18 @@ class PerGoroutineRelabeler { } } - PROMPP_ALWAYS_INLINE bool resolve_timestamps(PromPP::Primitives::Timestamp def_timestamp, - BareBones::Vector& samples, - const RelabelerOptions& options) { + PROMPP_ALWAYS_INLINE static bool resolve_timestamps(Primitives::Timestamp def_timestamp, + BareBones::Vector& samples, + const RelabelerOptions& options) { // skip resolve without stalenans - if (def_timestamp == PromPP::Primitives::kNullTimestamp) { + if (def_timestamp == Primitives::kNullTimestamp) { return false; } bool track_staleness{true}; for (auto& sample : samples) { // replace null timestamp on def timestamp - if (sample.timestamp() == PromPP::Primitives::kNullTimestamp) { + if (sample.timestamp() == Primitives::kNullTimestamp) { sample.timestamp() = def_timestamp; continue; } @@ -1292,11 +1215,11 @@ class PerGoroutineRelabeler { const RelabelerOptions& options, const StatelessRelabeler& stateless_relabeler, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, - PromPP::Primitives::Go::SliceView& shards_relabeled_series) { + Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_relabeled_series) { NoOpStaleNaNsState state{}; input_relabeling_internal(input_lss, target_lss, cache, hashdex, options, stateless_relabeler, stats, shards_inner_series, shards_relabeled_series, state, - PromPP::Primitives::kNullTimestamp); + Primitives::kNullTimestamp); } template @@ -1306,10 +1229,9 @@ class PerGoroutineRelabeler { const Hashdex& hashdex, const RelabelerOptions& options, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series) { + Primitives::Go::SliceView& shards_inner_series) { NoOpStaleNaNsState state{}; - return input_relabeling_from_cache_internal(input_lss, target_lss, cache, hashdex, options, stats, shards_inner_series, state, - PromPP::Primitives::kNullTimestamp); + return input_relabeling_from_cache_internal(input_lss, target_lss, cache, hashdex, options, stats, shards_inner_series, state, Primitives::kNullTimestamp); } template @@ -1320,10 +1242,10 @@ class PerGoroutineRelabeler { const RelabelerOptions& options, const StatelessRelabeler& stateless_relabeler, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, - PromPP::Primitives::Go::SliceView& shards_relabeled_series, + Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_relabeled_series, StaleNaNsState& state, - PromPP::Primitives::Timestamp def_timestamp) { + Primitives::Timestamp def_timestamp) { input_relabeling_internal(input_lss, target_lss, cache, hashdex, options, stateless_relabeler, stats, shards_inner_series, shards_relabeled_series, state, def_timestamp); } @@ -1335,9 +1257,9 @@ class PerGoroutineRelabeler { const Hashdex& hashdex, const RelabelerOptions& options, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series, + Primitives::Go::SliceView& shards_inner_series, StaleNaNsState& state, - PromPP::Primitives::Timestamp def_timestamp) { + Primitives::Timestamp def_timestamp) { return input_relabeling_from_cache_internal(input_lss, target_lss, cache, hashdex, options, stats, shards_inner_series, state, def_timestamp); } @@ -1346,58 +1268,18 @@ class PerGoroutineRelabeler { PROMPP_ALWAYS_INLINE void input_transition_relabeling(TargetLSS& target_lss, const Hashdex& hashdex, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series) { - assert(number_of_shards_ > 0); - - size_t n = std::min(static_cast(hashdex.size()), static_cast((hashdex.size() * 1.1) / number_of_shards_)); - for (auto i = 0; i < number_of_shards_; ++i) { - if (shards_inner_series[i]->size() >= n) { - continue; - } - - shards_inner_series[i]->reserve(n); - } - - PromPP::Primitives::LabelsBuilder builder{builder_state_}; - - for (auto it = skip_shard_inner_series(hashdex, shards_inner_series[shard_id_]->size()); it != hashdex.end(); ++it) { - if ((it->hash() % number_of_shards_) != shard_id_) { - continue; - } - - timeseries_buf_.clear(); - it->read(timeseries_buf_); - - Cache::CheckResult check_result = check_target_lss(target_lss, timeseries_buf_.label_set(), it->hash()); - switch (check_result.status) { - case Cache::CheckResult::kNotFound: { - builder.reset(timeseries_buf_.label_set()); - auto ls_id = target_lss.find_or_emplace(timeseries_buf_.label_set(), it->hash()); - auto& samples = timeseries_buf_.samples(); - for (const PromPP::Primitives::Sample& sample : samples) { - shards_inner_series[shard_id_]->emplace_back(sample, ls_id); - } - - ++stats.series_added; - - break; - } - - case Cache::CheckResult::kKeep: { - auto& samples = timeseries_buf_.samples(); - for (const PromPP::Primitives::Sample& sample : samples) { - shards_inner_series[shard_id_]->emplace_back(sample, check_result.ls_id); - } - - break; - } - - default: - continue; + Primitives::Go::SliceView& shards_inner_series) { + fill_inner_series(hashdex, skip_shard_inner_series(hashdex, shards_inner_series[shard_id_]->size()), shards_inner_series, [&](auto& item) { + const auto previous_size = target_lss.size(); + auto ls_id = target_lss.find_or_emplace(timeseries_buf_.label_set(), item.hash()); + shards_inner_series[shard_id_]->emplace_back(timeseries_buf_.samples(), ls_id); + + if (target_lss.size() > previous_size) { + ++stats.series_added; } - - stats.samples_added += static_cast(timeseries_buf_.samples().size()); - } + stats.samples_added += timeseries_buf_.samples().size(); + return true; + }); } // input_transition_relabeling_from_cache transparent relabeling with only reading from the lss. @@ -1405,54 +1287,29 @@ class PerGoroutineRelabeler { PROMPP_ALWAYS_INLINE bool input_transition_relabeling_only_read(TargetLSS& target_lss, const Hashdex& hashdex, Stats& stats, - PromPP::Primitives::Go::SliceView& shards_inner_series) { - assert(number_of_shards_ > 0); - - size_t n = std::min(static_cast(hashdex.size()), static_cast((hashdex.size() * 1.1) / number_of_shards_)); - for (auto i = 0; i < number_of_shards_; ++i) { - shards_inner_series[i]->reserve(n); - } - - for (const auto& item : hashdex) { - if ((item.hash() % number_of_shards_) != shard_id_) { - continue; - } - - timeseries_buf_.clear(); - item.read(timeseries_buf_); - - Cache::CheckResult check_result = check_target_lss(target_lss, timeseries_buf_.label_set(), item.hash()); - switch (check_result.status) { - case Cache::CheckResult::kNotFound: { - return false; - }; - - case Cache::CheckResult::kKeep: { - auto& samples = timeseries_buf_.samples(); - for (const PromPP::Primitives::Sample& sample : samples) { - shards_inner_series[shard_id_]->emplace_back(sample, check_result.ls_id); - } - - break; - } - - default: - continue; + Primitives::Go::SliceView& shards_inner_series) { + bool result = true; + fill_inner_series(hashdex, hashdex.begin(), shards_inner_series, [&](auto& item) { + if (auto ls_id = target_lss.find(timeseries_buf_.label_set(), item.hash()); ls_id.has_value()) { + shards_inner_series[shard_id_]->emplace_back(timeseries_buf_.samples(), *ls_id); + stats.samples_added += timeseries_buf_.samples().size(); + return true; } - stats.samples_added += static_cast(timeseries_buf_.samples().size()); - } + result = false; + return false; + }); - return true; + return result; } // second stage // append_relabeler_series add relabeled ls to lss, add to result and add to cache update. template - PROMPP_ALWAYS_INLINE void append_relabeler_series(LSS& target_lss, - InnerSeries* inner_series, - const RelabeledSeries* relabeled_series, - RelabelerStateUpdate* relabeler_state_update) { + PROMPP_ALWAYS_INLINE static void append_relabeler_series(LSS& target_lss, + InnerSeries* inner_series, + const RelabeledSeries* relabeled_series, + RelabelerStateUpdate* relabeler_state_update) { relabeler_state_update->reserve(relabeler_state_update->size() + relabeled_series->size()); inner_series->reserve(inner_series->size() + relabeled_series->size()); if constexpr (BareBones::concepts::has_reserve) { @@ -1461,17 +1318,52 @@ class PerGoroutineRelabeler { for (const auto& relabeled_serie : relabeled_series->data()) { uint32_t ls_id = target_lss.find_or_emplace(relabeled_serie.ls, relabeled_serie.hash); + inner_series->emplace_back(relabeled_serie.samples, ls_id); + relabeler_state_update->emplace_back(relabeled_serie.ls_id, ls_id); + } + } - for (const Primitives::Sample& sample : relabeled_serie.samples) { - inner_series->emplace_back(sample, ls_id); + template + void fill_inner_series(const Hashdex& hashdex, auto hashdex_it, Primitives::Go::SliceView& shards_inner_series, Handler handler) { + assert(number_of_shards_ > 0); + + const size_t n = std::min(static_cast(hashdex.size()), static_cast((hashdex.size() * 1.1) / number_of_shards_)); + for (uint16_t i = 0; i < number_of_shards_; ++i) { + shards_inner_series[i]->reserve(n); + } + + for (; hashdex_it != hashdex.end(); ++hashdex_it) { + if ((hashdex_it->hash() % number_of_shards_) != shard_id_) { + continue; } - relabeler_state_update->emplace_back(relabeled_serie.ls_id, ls_id); + timeseries_buf_.clear(); + hashdex_it->read(timeseries_buf_); + + if (!handler(*hashdex_it)) { + break; + } } } - // destructor. - PROMPP_ALWAYS_INLINE ~PerGoroutineRelabeler() = default; + template + PROMPP_ALWAYS_INLINE void add_stale_nans(Cache& cache, + Primitives::Go::SliceView& shards_inner_series, + StNaNsState& stale_nan_state, + Primitives::Timestamp def_timestamp) { + const Primitives::Sample smpl{def_timestamp, kStaleNan}; + stale_nan_state.swap( + [&](uint32_t ls_id) { + if (const auto res = cache.check_input(ls_id); res.status == Cache::CheckResult::kRelabel) { + shards_inner_series[res.shard_id]->emplace_back(smpl, res.ls_id); + } + }, + [&](uint32_t ls_id) { + if (const auto res = cache.check_target(ls_id); res.status == Cache::CheckResult::kKeep) { + shards_inner_series[shard_id_]->emplace_back(smpl, res.ls_id); + } + }); + } }; } // namespace PromPP::Prometheus::Relabel diff --git a/pp/prometheus/stateless_relabeler.h b/pp/prometheus/stateless_relabeler.h index fa7bdb17c..885e01410 100644 --- a/pp/prometheus/stateless_relabeler.h +++ b/pp/prometheus/stateless_relabeler.h @@ -1,20 +1,14 @@ #pragma once -#include #include #include #include #include #include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#pragma GCC diagnostic ignored "-Wpedantic" -#pragma GCC diagnostic ignored "-Wswitch" -#include "re2/re2.h" -#pragma GCC diagnostic pop #include #include +#include "re2/re2.h" #include "bare_bones/bit.h" #include "bare_bones/exception.h" @@ -24,8 +18,8 @@ namespace PromPP::Prometheus::Relabel { // label_name_is_valid validate label name. -PROMPP_ALWAYS_INLINE bool label_name_is_valid(std::string_view name) { - if (name.size() == 0) { +PROMPP_ALWAYS_INLINE bool label_name_is_valid(const std::string_view& name) { + if (name.empty()) { return false; } @@ -41,13 +35,13 @@ PROMPP_ALWAYS_INLINE bool label_name_is_valid(std::string_view name) { } // label_value_is_valid validate label value. -PROMPP_ALWAYS_INLINE bool label_value_is_valid(std::string_view value) noexcept { +PROMPP_ALWAYS_INLINE bool label_value_is_valid(const std::string_view& value) noexcept { return simdutf::validate_utf8(value.data(), value.length()); } // metric_name_value_is_valid validate value for metric name(__name__). -PROMPP_ALWAYS_INLINE bool metric_name_value_is_valid(std::string_view value) { - if (value.size() == 0) { +PROMPP_ALWAYS_INLINE bool metric_name_value_is_valid(const std::string_view& value) { + if (value.empty()) { return false; } @@ -85,23 +79,13 @@ class PatternPart { PROMPP_ALWAYS_INLINE explicit PatternPart(int g) : type_(pGroup), data_{.group_ = g} {} // write - convert parts to out. - PROMPP_ALWAYS_INLINE void write(std::ostream& out, std::vector& groups) { - if (type_ == pGroup) { - out << groups[data_.group_]; - } else { - out << data_.string_; - } - } - - PROMPP_ALWAYS_INLINE void write(std::ostream& out, std::vector& groups) const { + PROMPP_ALWAYS_INLINE void write(std::string& out, const std::vector& groups) const { if (type_ == pGroup) { - out << groups[data_.group_]; + out += groups[data_.group_]; } else { - out << data_.string_; + out += data_.string_; } } - - ~PatternPart() = default; }; // Regexp - wrapper on re2. @@ -111,19 +95,14 @@ class Regexp { public: // Regexp - work without ("^(?:" + std::string(s) + ")$"). - PROMPP_ALWAYS_INLINE explicit Regexp(const std::string_view& s) noexcept : re_(std::make_unique(std::string(s))) {} - - PROMPP_ALWAYS_INLINE Regexp(Regexp&&) noexcept = default; - PROMPP_ALWAYS_INLINE ~Regexp() = default; + PROMPP_ALWAYS_INLINE explicit Regexp(const std::string_view& s) noexcept : re_(std::make_unique(s)) {} // number_of_capturing_groups - return the number of capturing sub-patterns, or -1 if the regexp wasn't valid on construction. The overall match ($0) does not // count. Use in test. - PROMPP_ALWAYS_INLINE int number_of_capturing_groups() { return re_->NumberOfCapturingGroups(); } - - PROMPP_ALWAYS_INLINE int number_of_capturing_groups() const { return re_->NumberOfCapturingGroups(); } + [[nodiscard]] PROMPP_ALWAYS_INLINE int number_of_capturing_groups() const { return re_->NumberOfCapturingGroups(); } // groups - get named capturing groups and number groups. - PROMPP_ALWAYS_INLINE std::map groups() { + [[nodiscard]] PROMPP_ALWAYS_INLINE std::map groups() const { // get named capturing groups std::map named_groups = re_->NamedCapturingGroups(); // add number groups to named capturing groups @@ -135,8 +114,8 @@ class Regexp { } // match_to_args - match expression and return result args. - PROMPP_ALWAYS_INLINE bool match_to_args(std::string_view src, std::vector& res) { - int n = number_of_capturing_groups(); + PROMPP_ALWAYS_INLINE bool match_to_args(std::string_view src, std::vector& res) const { + const int n = number_of_capturing_groups(); // search full match to args, where size - number of capturing groups res.resize(n + 1); @@ -149,108 +128,65 @@ class Regexp { re_args.emplace_back(&res[i]); re_args_ptr.emplace_back(&re_args[i - 1]); } - bool ok = RE2::FullMatchN(src, *re_.get(), &re_args_ptr[0], n); - return ok; - } - - PROMPP_ALWAYS_INLINE bool match_to_args(std::string_view src, std::vector& res) const { - int n = number_of_capturing_groups(); - // search full match to args, where size - number of capturing groups - res.resize(n + 1); - res[0] = src; - std::vector re_args; - re_args.reserve(n); - std::vector re_args_ptr; - re_args_ptr.reserve(n); - for (int i = 1; i <= n; ++i) { - re_args.emplace_back(&res[i]); - re_args_ptr.emplace_back(&re_args[i - 1]); + if (!RE2::FullMatchN(src, *re_, &re_args_ptr[0], n)) { + res.clear(); + return false; } - bool ok = RE2::FullMatchN(src, *re_.get(), &re_args_ptr[0], n); - return ok; + + return true; } // replace_with_args - replace in template with incoming args. - PROMPP_ALWAYS_INLINE std::string replace_with_args(std::stringstream& buf, std::vector& args, std::vector& tmpl) { - if (tmpl.size() == 0) [[unlikely]] { - // no template or source data - return ""; - } - - buf.str(""); - for (auto& val : tmpl) { - val.write(buf, args); - } - - return buf.str(); - } + PROMPP_ALWAYS_INLINE static void replace_with_args(std::string& buf, const std::vector& args, const std::vector& tmpl) { + buf.clear(); - PROMPP_ALWAYS_INLINE std::string replace_with_args(std::stringstream& buf, std::vector& args, const std::vector& tmpl) const { - if (tmpl.size() == 0) [[unlikely]] { + if (tmpl.empty()) [[unlikely]] { // no template or source data - return ""; + return; } - buf.str(""); for (auto& val : tmpl) { val.write(buf, args); } - - return buf.str(); } // replace_full - find match for source and replace in template. - PROMPP_ALWAYS_INLINE std::string replace_full(std::stringstream& out, std::string_view src, std::vector& tmpl) { - if (src.size() == 0 || tmpl.size() == 0) [[unlikely]] { - // no template or source data - return ""; - } - - std::vector res_args; - bool ok = match_to_args(src, res_args); - if (!ok) { - // no entries in regexp - return ""; - } + PROMPP_ALWAYS_INLINE void replace_full(std::string& out, std::string_view src, const std::vector& tmpl) const { + out.clear(); - return replace_with_args(out, res_args, tmpl); - } - - PROMPP_ALWAYS_INLINE std::string replace_full(std::stringstream& out, std::string_view src, const std::vector& tmpl) const { - if (src.size() == 0 || tmpl.size() == 0) [[unlikely]] { + if (src.empty() || tmpl.empty()) [[unlikely]] { // no template or source data - return ""; + return; } - std::vector res_args; - bool ok = match_to_args(src, res_args); - if (!ok) { + std::vector res_args; + if (!match_to_args(src, res_args)) { // no entries in regexp - return ""; + return; } - return replace_with_args(out, res_args, tmpl); + replace_with_args(out, res_args, tmpl); } // full_match - check text for full match regexp. - PROMPP_ALWAYS_INLINE bool full_match(std::string_view str) const { return RE2::FullMatch(str, *re_.get()); } + [[nodiscard]] PROMPP_ALWAYS_INLINE bool full_match(std::string_view str) const { return RE2::FullMatch(str, *re_); } }; struct GORelabelConfig { // source_labels - a list of labels from which values are taken and concatenated with the configured separator in order. - PromPP::Primitives::Go::SliceView source_labels; + Primitives::Go::SliceView source_labels; // separator - is the string between concatenated values from the source labels. - PromPP::Primitives::Go::String separator; + Primitives::Go::String separator; // regex - against which the concatenation is matched. - PromPP::Primitives::Go::String regex; + Primitives::Go::String regex; // modulus - to take of the hash of concatenated values from the source labels. uint64_t modulus; // target_label - is the label to which the resulting string is written in a replacement. // Regexp interpolation is allowed for the replace action. - PromPP::Primitives::Go::String target_label; + Primitives::Go::String target_label; // replacement - is the regex replacement pattern to be used. - PromPP::Primitives::Go::String replacement; + Primitives::Go::String replacement; // action - is the action to be performed for the relabeling. uint8_t action; }; @@ -318,27 +254,26 @@ class RelabelConfig { std::vector replacement_parts_; // extract - extract from source letter or digit value. - PROMPP_ALWAYS_INLINE std::string extract(re2::RE2& rgx_validate, std::string_view src) { + PROMPP_ALWAYS_INLINE static std::string extract(const re2::RE2& rgx_validate, const std::string_view& src) { std::string name; RE2::PartialMatch(src, rgx_validate, &name); return name; } // is_valid_name - validate source on letter or digit value. - PROMPP_ALWAYS_INLINE bool is_valid_name(re2::RE2& rgx_validate, std::string_view src) { return RE2::FullMatch(src, rgx_validate); } + PROMPP_ALWAYS_INLINE static bool is_valid_name(const re2::RE2& rgx_validate, std::string_view src) { return RE2::FullMatch(src, rgx_validate); } // parse - parse template on parts. - PROMPP_ALWAYS_INLINE void parse(Regexp& regexp, re2::RE2& rgx_validate, std::string_view tmpl, std::vector& src_parts) { + PROMPP_ALWAYS_INLINE static void parse(const Regexp& regexp, const re2::RE2& rgx_validate, std::string_view tmpl, std::vector& src_parts) { std::map groups = regexp.groups(); - std::string_view p = std::string_view(tmpl); + auto p = std::string_view(tmpl); while (true) { - if (p.size() == 0) { + if (p.empty()) { break; } // search '$' and cut before - size_t i = p.find('$'); - std::string_view substr_p = p.substr(0, i); - if (substr_p.size() != 0) { + const size_t i = p.find('$'); + if (std::string_view substr_p = p.substr(0, i); !substr_p.empty()) { src_parts.emplace_back(substr_p); } if (i == std::string_view::npos) { @@ -356,7 +291,7 @@ class RelabelConfig { // if contains '{...}' case '{': { p.remove_prefix(1); - size_t j = p.find('}'); + const size_t j = p.find('}'); if (j == std::string_view::npos) { // if '}' not found cut - "${" src_parts.emplace_back(tmpl.substr(tmpl.size() - p.size() - 2, 2)); @@ -364,16 +299,14 @@ class RelabelConfig { } std::string_view g_name = p.substr(0, j); - auto rec = groups.find(std::string(g_name)); - if (rec != groups.end()) { + if (auto rec = groups.find(std::string(g_name)); rec != groups.end()) { // if g_name found in map add as group(int) src_parts.emplace_back(rec->second); p.remove_prefix(g_name.size() + 1); continue; } - bool ok = is_valid_name(rgx_validate, g_name); - if (!ok) { + if (!is_valid_name(rgx_validate, g_name)) { // if g_name invalid add as is - "${" + std::string{g_name} + "}" src_parts.emplace_back(tmpl.substr(tmpl.size() - p.size() - 2, g_name.size() + 3)); p.remove_prefix(g_name.size() + 1); @@ -388,10 +321,10 @@ class RelabelConfig { default: { // search '$' and extract g_name - int j = p.find('$'); + const auto j = p.find('$'); std::string_view g_name = p.substr(0, j); std::string name = extract(rgx_validate, g_name); - if (name.size() == 0) { + if (name.empty()) { // if name invalid add as is - "$" src_parts.emplace_back(tmpl.substr(tmpl.size() - p.size() - 1, 1)); continue; @@ -401,7 +334,7 @@ class RelabelConfig { if (rec != groups.end()) { // if g_name found in map add as group(int) src_parts.emplace_back(rec->second); - if (substr_g_name.size() != 0) { + if (!substr_g_name.empty()) { src_parts.emplace_back(substr_g_name); } p.remove_prefix(g_name.size()); @@ -409,7 +342,7 @@ class RelabelConfig { } // if g_name not found in map cut g_name - if (substr_g_name.size() != 0) { + if (!substr_g_name.empty()) { src_parts.emplace_back(substr_g_name); } p.remove_prefix(g_name.size()); @@ -430,12 +363,28 @@ class RelabelConfig { return BareBones::Bit::be(*reinterpret_cast(&digest[shift])); } + template + PROMPP_ALWAYS_INLINE std::string get_value(LabelsBuilder& builder) const { + std::string value; + + for (size_t i = 0; i < source_labels_.size(); ++i) { + const auto lv = builder.get(source_labels_[i]); + if (i == 0) [[unlikely]] { + value += lv; + continue; + } + value += separator_; + value += lv; + } + + return value; + } + public: // RelabelConfig - constructor for RelabelConfig from go-config. template PROMPP_ALWAYS_INLINE explicit RelabelConfig(GORelabelConfig* go_rc) noexcept - : source_labels_{}, - separator_{static_cast(go_rc->separator)}, + : separator_{static_cast(go_rc->separator)}, regexp_(static_cast(go_rc->regex)), modulus_{go_rc->modulus}, target_label_{static_cast(go_rc->target_label)}, @@ -451,302 +400,168 @@ class RelabelConfig { parse(regexp_, rgx_validate, replacement_, replacement_parts_); } - PROMPP_ALWAYS_INLINE RelabelConfig(RelabelConfig&&) noexcept = default; - // source_labels - a list of labels from which values are taken and concatenated with the configured separator in order. - PROMPP_ALWAYS_INLINE const std::vector& source_labels() const noexcept { return source_labels_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE const std::vector& source_labels() const noexcept { return source_labels_; } // separator - is the string between concatenated values from the source labels. - PROMPP_ALWAYS_INLINE const std::string_view& separator() const noexcept { return separator_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE const std::string_view& separator() const noexcept { return separator_; } // regexp - against which the concatenation is matched. - PROMPP_ALWAYS_INLINE const Regexp& regexp() const noexcept { return regexp_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE const Regexp& regexp() const noexcept { return regexp_; } // modulus - to take of the hash of concatenated values from the source labels. - PROMPP_ALWAYS_INLINE const uint64_t& modulus() const noexcept { return modulus_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE uint64_t modulus() const noexcept { return modulus_; } // target_label - is the label to which the resulting string is written in a replacement. // Regexp interpolation is allowed for the replace action. - PROMPP_ALWAYS_INLINE const std::string_view& target_label() const noexcept { return target_label_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE const std::string_view& target_label() const noexcept { return target_label_; } // replacement - is the regex replacement pattern to be used. - PROMPP_ALWAYS_INLINE const std::string_view& replacement() const noexcept { return replacement_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE const std::string_view& replacement() const noexcept { return replacement_; } // action - is the action to be performed for the relabeling. - PROMPP_ALWAYS_INLINE const rAction& action() const noexcept { return action_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE rAction action() const noexcept { return action_; } // target_label_parts - dismantled target_label. - PROMPP_ALWAYS_INLINE const std::vector& target_label_parts() const noexcept { return target_label_parts_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE const std::vector& target_label_parts() const noexcept { return target_label_parts_; } // replacement_parts - dismantled replacement. - PROMPP_ALWAYS_INLINE const std::vector& replacement_parts() const noexcept { return replacement_parts_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE const std::vector& replacement_parts() const noexcept { return replacement_parts_; } // relabel - building relabeling labels. template - PROMPP_ALWAYS_INLINE relabelStatus relabel(std::stringstream& buf, LabelsBuilder& builder) { - std::string value; - for (size_t i = 0; i < source_labels_.size(); ++i) { - std::string_view lv = builder.get(source_labels_[i]); - if (i == 0) { - value += std::string(lv); - continue; - } - value += separator_; - value += lv; - } - + PROMPP_ALWAYS_INLINE relabelStatus relabel(std::string& buf, LabelsBuilder& builder) const { switch (action_) { case rDrop: { - if (regexp_.full_match(value)) { + if (regexp_.full_match(get_value(builder))) { return rsDrop; } break; } case rKeep: { - if (!regexp_.full_match(value)) { + if (!regexp_.full_match(get_value(builder))) { return rsDrop; } break; } case rDropEqual: { - if (builder.get(target_label_) == value) { + if (builder.get(target_label_) == get_value(builder)) { return rsDrop; } break; } case rKeepEqual: { - if (builder.get(target_label_) != value) { + if (builder.get(target_label_) != get_value(builder)) { return rsDrop; } break; } case rReplace: { - std::vector res_args; - bool ok = regexp_.match_to_args(value, res_args); - if (!ok) { + const auto value = get_value(builder); + std::vector res_args; + if (!regexp_.match_to_args(value, res_args)) { break; } - std::string lname = regexp_.replace_with_args(buf, res_args, target_label_parts_); - if (!label_name_is_valid(lname)) { + Regexp::replace_with_args(buf, res_args, target_label_parts_); + if (!label_name_is_valid(buf)) { break; } - std::string lvalue = regexp_.replace_with_args(buf, res_args, replacement_parts_); - if (lvalue.size() == 0) { + std::string lname = buf; + + Regexp::replace_with_args(buf, res_args, replacement_parts_); + if (buf.empty()) { if (builder.contains(lname)) { builder.del(lname); return rsRelabel; } break; } - builder.set(lname, lvalue); + builder.set(lname, buf); return rsRelabel; } case rLowercase: { - std::string lvalue{value}; - std::ranges::transform(lvalue, lvalue.begin(), [](unsigned char c) { return std::tolower(c); }); - builder.set(target_label_, lvalue); + auto value = get_value(builder); + std::ranges::transform(value, value.begin(), [](unsigned char c) { return std::tolower(c); }); + builder.set(target_label_, value); return rsRelabel; } case rUppercase: { - std::string lvalue{value}; - std::ranges::transform(lvalue, lvalue.begin(), [](unsigned char c) { return std::toupper(c); }); - builder.set(target_label_, lvalue); + auto value = get_value(builder); + std::ranges::transform(value, value.begin(), [](unsigned char c) { return std::toupper(c); }); + builder.set(target_label_, value); return rsRelabel; } case rHashMod: { - std::string lvalue{std::to_string(make_hash_uint64(value) % modulus_)}; + std::string lvalue{std::to_string(make_hash_uint64(get_value(builder)) % modulus_)}; builder.set(target_label_, lvalue); return rsRelabel; } case rLabelMap: { - bool change{false}; - builder.range([&](LNameType& lname, LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { + std::vector labels_for_set; + builder.range([&](const auto& lname, const auto& lvalue) PROMPP_LAMBDA_INLINE -> bool { if (regexp_.full_match(lname)) { - std::string rlname = regexp_.replace_full(buf, lname, replacement_parts_); - builder.set(rlname, lvalue); - change = true; + regexp_.replace_full(buf, lname, replacement_parts_); + labels_for_set.emplace_back(buf, lvalue); } return true; }); - if (change) { - return rsRelabel; - } - break; - } - case rLabelDrop: { - bool change{false}; - builder.range([&](LNameType& lname, [[maybe_unused]] LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { - if (regexp_.full_match(lname)) { - builder.del(lname); - change = true; + if (!labels_for_set.empty()) { + for (const auto& label : labels_for_set) { + builder.set(label.first, label.second); } - return true; - }); - if (change) { - return rsRelabel; - } - break; - } - case rLabelKeep: { - bool change{false}; - builder.range([&](LNameType& lname, [[maybe_unused]] LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { - if (!regexp_.full_match(lname)) { - builder.del(lname); - change = true; - } - return true; - }); - if (change) { - return rsRelabel; - } - break; - } - - default: { - throw BareBones::Exception(0x481dea53751b85c3, "unknown relabel action"); - } - } - - return rsKeep; - } - - template - PROMPP_ALWAYS_INLINE relabelStatus relabel(std::stringstream& buf, LabelsBuilder& builder) const { - std::string value; - for (size_t i = 0; i < source_labels_.size(); ++i) { - std::string_view lv = builder.get(source_labels_[i]); - if (i == 0) { - value += std::string(lv); - continue; - } - value += separator_; - value += lv; - } - - switch (action_) { - case rDrop: { - if (regexp_.full_match(value)) { - return rsDrop; - } - break; - } - case rKeep: { - if (!regexp_.full_match(value)) { - return rsDrop; - } - break; - } - - case rDropEqual: { - if (builder.get(target_label_) == value) { - return rsDrop; + return rsRelabel; } - break; - } - case rKeepEqual: { - if (builder.get(target_label_) != value) { - return rsDrop; - } break; } - case rReplace: { - std::vector res_args; - bool ok = regexp_.match_to_args(value, res_args); - if (!ok) { - break; - } - - std::string lname = regexp_.replace_with_args(buf, res_args, target_label_parts_); - if (!label_name_is_valid(lname)) { - break; - } - std::string lvalue = regexp_.replace_with_args(buf, res_args, replacement_parts_); - if (lvalue.size() == 0) { - if (builder.contains(lname)) { - builder.del(lname); - return rsRelabel; - } - break; - } - builder.set(lname, lvalue); - return rsRelabel; - } - - case rLowercase: { - std::string lvalue{value}; - std::ranges::transform(lvalue, lvalue.begin(), [](unsigned char c) { return std::tolower(c); }); - builder.set(target_label_, lvalue); - return rsRelabel; - } - - case rUppercase: { - std::string lvalue{value}; - std::ranges::transform(lvalue, lvalue.begin(), [](unsigned char c) { return std::toupper(c); }); - builder.set(target_label_, lvalue); - return rsRelabel; - } - - case rHashMod: { - std::string lvalue{std::to_string(make_hash_uint64(value) % modulus_)}; - builder.set(target_label_, lvalue); - return rsRelabel; - } - - case rLabelMap: { - bool change{false}; - builder.range([&](LNameType& lname, LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { + case rLabelDrop: { + std::vector labels_for_del; + builder.range([&](const auto& lname, const auto&) PROMPP_LAMBDA_INLINE -> bool { if (regexp_.full_match(lname)) { - std::string rlname = regexp_.replace_full(buf, lname, replacement_parts_); - builder.set(rlname, lvalue); - change = true; + labels_for_del.emplace_back(lname); } return true; }); - if (change) { - return rsRelabel; - } - break; - } - case rLabelDrop: { - bool change{false}; - builder.range([&](LNameType& lname, [[maybe_unused]] LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { - if (regexp_.full_match(lname)) { - builder.del(lname); - change = true; + if (!labels_for_del.empty()) { + for (const auto& name : labels_for_del) { + builder.del(name); } - return true; - }); - if (change) { + return rsRelabel; } + break; } case rLabelKeep: { - bool change{false}; - builder.range([&](LNameType& lname, [[maybe_unused]] LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { + std::vector labels_for_del; + builder.range([&](const auto& lname, const auto&) PROMPP_LAMBDA_INLINE -> bool { if (!regexp_.full_match(lname)) { - builder.del(lname); - change = true; + labels_for_del.emplace_back(lname); } return true; }); - if (change) { + + if (!labels_for_del.empty()) { + for (const auto& name : labels_for_del) { + builder.del(name); + } + return rsRelabel; } + break; } @@ -757,9 +572,6 @@ class RelabelConfig { return rsKeep; } - - // ~RelabelConfig - destructor for RelabelConfig from go-config. - PROMPP_ALWAYS_INLINE ~RelabelConfig() = default; }; // StatelessRelabeler - stateless relabeler with relabel configs. @@ -772,34 +584,15 @@ class StatelessRelabeler { // StatelessRelabeler - constructor for StatelessRelabeler, converting go-config. template PROMPP_ALWAYS_INLINE explicit StatelessRelabeler(const GORelabelConfigs& go_rcfgs) noexcept { - configs_.reserve(go_rcfgs.size()); - for (const auto go_rcfg : go_rcfgs) { - configs_.emplace_back(go_rcfg); - } + reset_to(go_rcfgs); } // relabeling_process caller passes a LabelsBuilder containing the initial set of labels, which is mutated by the rules. template - PROMPP_ALWAYS_INLINE relabelStatus relabeling_process(std::stringstream& buf, LabelsBuilder& builder) { - relabelStatus rstatus{rsKeep}; - for (auto& rcfg : configs_) { - relabelStatus status = rcfg.relabel(buf, builder); - if (status == rsDrop) { - return rsDrop; - } - if (status == rsRelabel) { - rstatus = rsRelabel; - } - } - - return rstatus; - } - - template - PROMPP_ALWAYS_INLINE relabelStatus relabeling_process(std::stringstream& buf, LabelsBuilder& builder) const { + PROMPP_ALWAYS_INLINE relabelStatus relabeling_process(std::string& buf, LabelsBuilder& builder) const { relabelStatus rstatus{rsKeep}; for (auto& rcfg : configs_) { - relabelStatus status = rcfg.relabel(buf, builder); + const relabelStatus status = rcfg.relabel(buf, builder); if (status == rsDrop) { return rsDrop; } @@ -814,9 +607,8 @@ class StatelessRelabeler { // relabeling_process_with_soft_validate caller passes a LabelsBuilder containing the initial set of labels, which is mutated by the rules with soft(on empty) // validate label set. template - PROMPP_ALWAYS_INLINE relabelStatus relabeling_process_with_soft_validate(std::stringstream& buf, LabelsBuilder& builder) { - relabelStatus rstatus = relabeling_process(buf, builder); - + PROMPP_ALWAYS_INLINE relabelStatus relabeling_process_with_soft_validate(std::ostringstream& buf, LabelsBuilder& builder) { + const relabelStatus rstatus = relabeling_process(buf, builder); if (rstatus == rsDrop) { return rsDrop; } @@ -837,23 +629,21 @@ class StatelessRelabeler { configs_.emplace_back(go_rcfg); } } - - PROMPP_ALWAYS_INLINE ~StatelessRelabeler() = default; }; // processExternalLabels merges externalLabels into ls. If ls contains // a label in externalLabels, the value in ls wins. template -PROMPP_ALWAYS_INLINE void process_external_labels(LabelsBuilder& builder, ExternalLabels& external_labels) { +PROMPP_ALWAYS_INLINE void process_external_labels(LabelsBuilder& builder, const ExternalLabels& external_labels) { if (external_labels.size() == 0) { return; } std::size_t j{0}; - builder.range([&](LNameType& lname, [[maybe_unused]] LValueType& lvalue) PROMPP_LAMBDA_INLINE -> bool { - for (; j < external_labels.size() && lname > external_labels[j].first;) { - builder.set(external_labels[j].first, external_labels[j].second); - ++j; + std::vector indexes_for_set; + builder.range([&](const auto& lname, const auto&) PROMPP_LAMBDA_INLINE -> bool { + for (; j < external_labels.size() && lname > external_labels[j].first; ++j) { + indexes_for_set.emplace_back(j); } if (j < external_labels.size() && lname == external_labels[j].first) { @@ -862,6 +652,10 @@ PROMPP_ALWAYS_INLINE void process_external_labels(LabelsBuilder& builder, Extern return true; }); + for (auto index : indexes_for_set) { + builder.set(external_labels[index].first, external_labels[index].second); + } + for (; j < external_labels.size(); j++) { builder.set(external_labels[j].first, external_labels[j].second); } @@ -876,7 +670,6 @@ PROMPP_ALWAYS_INLINE void soft_validate(relabelStatus& rstatus, LabelsBuilder& b if (builder.is_empty()) [[unlikely]] { rstatus = rsDrop; - return; } }; diff --git a/pp/prometheus/tests/relabeler_tests.cpp b/pp/prometheus/tests/relabeler_tests.cpp index d34a1b4dd..591fd4dff 100644 --- a/pp/prometheus/tests/relabeler_tests.cpp +++ b/pp/prometheus/tests/relabeler_tests.cpp @@ -1,7 +1,3 @@ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunknown-pragmas" -#pragma GCC optimize("no-var-tracking") // to speed up compilation - #include #include #include @@ -15,326 +11,36 @@ namespace { -using namespace PromPP::Prometheus; // NOLINT -using namespace PromPP::Primitives; // NOLINT - -using LabelViewForTest = std::pair; -using LabelForTest = std::pair; - -class SampleForTest { - private: - int64_t timestamp_; - double value_; - - public: - PROMPP_ALWAYS_INLINE SampleForTest(int64_t timestamp, double value) noexcept : timestamp_(timestamp), value_(value) {} - - PROMPP_ALWAYS_INLINE SampleForTest() noexcept = default; - - int64_t timestamp() const noexcept { return timestamp_; } - int64_t& timestamp() noexcept { return timestamp_; } - - double value() const noexcept { return value_; } - double& value() noexcept { return value_; } -}; - -class NamesSetForTest : public std::vector { - using Base = std::vector; - - public: - using Base::Base; - [[maybe_unused]] friend size_t hash_value(const NamesSetForTest& lns) { return PromPP::Primitives::hash::hash_of_string_list(lns); } -}; - -class LabelViewSetForTest : public std::vector { - using Base = std::vector; - - public: - using Base::Base; - - PROMPP_ALWAYS_INLINE void add(const LabelViewForTest& label) noexcept { - if (__builtin_expect(Base::empty() || std::get<0>(label) > std::get<0>(Base::back()), true)) { - Base::emplace_back(label); - } else if (__builtin_expect(std::get<0>(label) == std::get<0>(Base::back()), false)) { - std::get<1>(Base::back()) = std::get<1>(label); - } else { - auto i = std::lower_bound(Base::begin(), Base::end(), std::get<0>(label), - [](const LabelViewForTest& a, const std::string_view& b) { return std::get<0>(a) < b; }); - if (__builtin_expect(std::get<0>(*i) == std::get<0>(label), false)) { - std::get<1>(*i) = std::get<1>(label); - } else { - Base::insert(i, label); - } - } - } - - template - PROMPP_ALWAYS_INLINE SymbolType get(const SymbolType& label_name) noexcept { - for (const auto& [ln, lv] : *this) { - if (ln == label_name) [[unlikely]] { - return lv; - } - } - - return ""; - } - - NamesSetForTest names() const { - NamesSetForTest tns; - - for (auto [label_name, _] : *this) { - tns.push_back(label_name); - } - - return tns; - } - - [[maybe_unused]] friend size_t hash_value(const LabelViewSetForTest& tls) { return PromPP::Primitives::hash::hash_of_label_set(tls); } -}; - -class TimeseriesForTest { - LabelViewSetForTest label_set_; - std::vector samples_; - - public: - TimeseriesForTest() noexcept = default; - TimeseriesForTest(const TimeseriesForTest&) noexcept = default; - TimeseriesForTest& operator=(const TimeseriesForTest&) noexcept = default; - TimeseriesForTest(TimeseriesForTest&&) noexcept = default; - TimeseriesForTest& operator=(TimeseriesForTest&&) noexcept = default; - - TimeseriesForTest(const LabelViewSetForTest& label_set, const std::vector& samples) noexcept : label_set_(label_set), samples_(samples) {} - - PROMPP_ALWAYS_INLINE auto& label_set() noexcept { return label_set_; } - - PROMPP_ALWAYS_INLINE const auto& label_set() const noexcept { return label_set_; } - - PROMPP_ALWAYS_INLINE const auto& samples() const noexcept { return samples_; } - - PROMPP_ALWAYS_INLINE auto& samples() noexcept { return samples_; } - - PROMPP_ALWAYS_INLINE void clear() noexcept { - label_set().clear(); - samples().clear(); - } -}; - -// LabelsBuilderForTest - builder for label set. -template -class LabelsBuilderForTest { - LabelSet* base_ = nullptr; - LabelViewSetForTest buf_; - std::vector add_; - std::vector del_; - - public: - PROMPP_ALWAYS_INLINE LabelsBuilderForTest() noexcept {} - - // del - add label name to remove from label set. - PROMPP_ALWAYS_INLINE void del(std::string& lname) { - std::erase_if(add_, [lname](LabelForTest& lv) { return lv.first == lname; }); - - if (auto i = std::ranges::find_if(del_.begin(), del_.end(), [lname](const std::string_view& ln) { return ln == lname; }); i != del_.end()) { - return; - } - - del_.emplace_back(lname); - } - - // del - add label name to remove from label set. - PROMPP_ALWAYS_INLINE void del(std::string_view lname) { - std::erase_if(add_, [lname](LabelForTest& lv) { return lv.first == lname; }); - - if (auto i = std::ranges::find_if(del_.begin(), del_.end(), [lname](const std::string_view& ln) { return ln == lname; }); i != del_.end()) { - return; - } - - del_.emplace_back(lname); - } - - // get - returns the value for the label with the given name. Returns an empty string if the label doesn't exist. - PROMPP_ALWAYS_INLINE std::string_view get(std::string_view lname) { - if (auto i = std::ranges::find_if(add_.begin(), add_.end(), [lname](const LabelForTest& l) { return l.first == lname; }); i != add_.end()) { - return (*i).second; - } - - if (auto i = std::ranges::find_if(del_.begin(), del_.end(), [lname](const std::string_view& ln) { return ln == lname; }); i != del_.end()) { - return ""; - } - - if (base_ != nullptr) [[likely]] { - for (const auto& [ln, lv] : *base_) { - if (ln == lname) { - return lv; - } - } - } - - return ""; - } - - PROMPP_ALWAYS_INLINE bool contains(std::string_view lname) { - if (auto i = std::ranges::find_if(add_, [&lname](const LabelForTest& l) { return l.first == lname; }); i != add_.end()) { - return true; - } - - if (base_ != nullptr) [[likely]] { - for (const auto& [ln, lv] : *base_) { - if (ln == lname) { - return true; - } - } - } - - return false; - } - - // returns size of building labels. - PROMPP_ALWAYS_INLINE size_t size() { - size_t count{0}; - if (base_ != nullptr) [[likely]] { - for (const auto& ls : *base_) { - if (auto i = std::ranges::find_if(add_.begin(), add_.end(), [ls](const LabelForTest& l) { return l.first == ls.first; }); i != add_.end()) { - continue; - } - - if (auto i = std::ranges::find_if(del_.begin(), del_.end(), [ls](const std::string_view& ln) { return ln == ls.first; }); i != del_.end()) { - continue; - } - - ++count; - } - } - - count += add_.size(); - return count; - } - - // returns true if ls represents an empty set of labels. - PROMPP_ALWAYS_INLINE bool is_empty() { return size() == 0; } - - // labels - returns the labels from the builder. If no modifications were made, the original labels are returned. - PROMPP_ALWAYS_INLINE LabelViewSetForTest labels() { - if (base_ != nullptr) [[likely]] { - for (const auto& ls : *base_) { - if (auto i = std::ranges::find_if(add_.begin(), add_.end(), [ls](const LabelForTest& l) { return l.first == ls.first; }); i != add_.end()) { - continue; - } - - if (auto i = std::ranges::find_if(del_.begin(), del_.end(), [ls](const std::string_view& ln) { return ln == ls.first; }); i != del_.end()) { - continue; - } - - buf_.add(ls); - } - } - - if (add_.size() != 0) { - std::ranges::for_each(add_.begin(), add_.end(), [&](const LabelForTest& l) { buf_.add(l); }); - std::ranges::sort(buf_.begin(), buf_.end(), [](const LabelViewForTest& a, const LabelViewForTest& b) { - if (a.first == b.first) { - return a.second < b.second; - } - return a.first < b.first; - }); - } - - return buf_; - } - - // range - calls f on each label in the builder. - template - PROMPP_ALWAYS_INLINE void range(Callback func) { - // take a copy of add and del, so they are unaffected by calls to set() or del(). - std::vector cadd; - cadd.reserve(add_.size()); - std::ranges::copy(add_.begin(), add_.end(), std::back_inserter(cadd)); - - std::vector cdel; - cdel.reserve(del_.size()); - std::ranges::copy(cdel.begin(), cdel.end(), std::back_inserter(cdel)); - - if (__builtin_expect(base_ != nullptr, true)) { - for (const auto& ls : *base_) { - if (auto i = std::ranges::find_if(cadd.begin(), cadd.end(), [ls](const LabelViewForTest& l) { return l.first == ls.first; }); i != cadd.end()) { - continue; - } - - if (auto i = std::ranges::find_if(cdel.begin(), cdel.end(), [ls](const std::string_view& ln) { return ln == ls.first; }); i != cdel.end()) { - continue; - } - - func(ls.first, ls.second); - } - } - - std::ranges::for_each(cadd.begin(), cadd.end(), [&](const LabelViewForTest& l) { func(l.first, l.second); }); - } - - // reset - clears all current state for the builder. - PROMPP_ALWAYS_INLINE void reset() { - buf_.clear(); - add_.clear(); - del_.clear(); - base_ = nullptr; - } - - // reset - clears all current state for the builder and init from LabelSet. - PROMPP_ALWAYS_INLINE void reset(LabelSet& ls) { - reset(); - base_ = &ls; - } - - // set - the name/value pair as a label. A value of "" means delete that label. - PROMPP_ALWAYS_INLINE void set(std::string& lname, std::string& lvalue) { - if (__builtin_expect(lvalue.size() == 0, false)) { - del(lname); - return; - } - - if (auto i = std::ranges::find_if(add_.begin(), add_.end(), [lname](const LabelForTest& l) { return l.first == lname; }); i != add_.end()) { - (*i).second = lvalue; - return; - } - - add_.emplace_back(lname, lvalue); - } - - // set - the name/value pair as a label. A value of "" means delete that label. - PROMPP_ALWAYS_INLINE void set(std::string_view lname, std::string& lvalue) { - if (__builtin_expect(lvalue.size() == 0, false)) { - del(lname); - return; - } - - if (auto i = std::ranges::find_if(add_.begin(), add_.end(), [lname](const LabelForTest& l) { return l.first == lname; }); i != add_.end()) { - (*i).second = lvalue; - return; - } - - add_.emplace_back(lname, lvalue); - } - - // set - the name/value pair as a label. A value of "" means delete that label. - PROMPP_ALWAYS_INLINE void set(std::string& lname, std::string_view lvalue) { - if (__builtin_expect(lvalue.size() == 0, false)) { - del(lname); - return; - } - - if (auto i = std::ranges::find_if(add_.begin(), add_.end(), [lname](const LabelForTest& l) { return l.first == lname; }); i != add_.end()) { - (*i).second = lvalue; - return; - } - - add_.emplace_back(lname, lvalue); - } - - PROMPP_ALWAYS_INLINE LabelsBuilderForTest(LabelsBuilderForTest&&) noexcept = default; - PROMPP_ALWAYS_INLINE ~LabelsBuilderForTest() = default; -}; - -struct RelabelConfigTest { +using PromPP::Primitives::Label; +using PromPP::Primitives::LabelsBuilder; +using PromPP::Primitives::LabelSet; +using PromPP::Primitives::LabelView; +using PromPP::Primitives::LabelViewSet; +using PromPP::Primitives::Sample; +using PromPP::Primitives::Timestamp; +using PromPP::Primitives::Go::SliceView; +using PromPP::Primitives::SnugComposites::LabelSet::EncodingBimap; +using PromPP::Primitives::SnugComposites::LabelSet::OrderedEncodingBimap; +using PromPP::Prometheus::Relabel::hard_validate; +using PromPP::Prometheus::Relabel::InnerSerie; +using PromPP::Prometheus::Relabel::InnerSeries; +using PromPP::Prometheus::Relabel::MetricLimits; +using PromPP::Prometheus::Relabel::PerGoroutineRelabeler; +using PromPP::Prometheus::Relabel::PerShardRelabeler; +using PromPP::Prometheus::Relabel::RelabelerStateUpdate; +using PromPP::Prometheus::Relabel::relabelStatus; +using PromPP::Prometheus::Relabel::StaleNaNsState; +using PromPP::Prometheus::Relabel::StatelessRelabeler; +using enum PromPP::Prometheus::Relabel::rAction; +using enum relabelStatus; + +using GoString = PromPP::Primitives::Go::String; +using PromPP::Primitives::kNullTimestamp; +using PromPP::Prometheus::kStaleNan; + +using GoLabel = std::pair; + +struct RelabelConfig { std::vector source_labels{}; std::string_view separator{}; std::string_view regex{}; @@ -344,43 +50,25 @@ struct RelabelConfigTest { uint8_t action{0}; }; -PROMPP_ALWAYS_INLINE LabelViewSetForTest make_label_set(std::initializer_list lvs) { - LabelViewSetForTest labels; - for (const LabelViewForTest& lv : lvs) { - labels.add(lv); - } - - return labels; -} - -PROMPP_ALWAYS_INLINE std::vector make_samples(std::initializer_list samples) { - std::vector sampleses; - for (const SampleForTest& s : samples) { - sampleses.push_back(s); - } - - return sampleses; -} - -struct ItemTest { - size_t hash_; - LabelViewSetForTest labelview_set_; - std::vector samples_; +class ItemTest { + public: + ItemTest(LabelViewSet&& label_set, std::vector&& samples) + : label_set_(std::move(label_set)), samples_(std::move(samples)), hash_(hash_value(label_set_)) {} - PROMPP_ALWAYS_INLINE explicit ItemTest(size_t hash, LabelViewSetForTest& labelview_set, std::vector& samples) - : hash_(hash), labelview_set_(labelview_set), samples_(samples) {} - PROMPP_ALWAYS_INLINE size_t hash() const { return hash_; } + [[nodiscard]] PROMPP_ALWAYS_INLINE size_t hash() const { return hash_; } template PROMPP_ALWAYS_INLINE void read(Timeseries& timeseries) const { - for (const auto& labelview : labelview_set_) { - timeseries.label_set().add({labelview.first, labelview.second}); - } - + timeseries.label_set().add(label_set_); for (const auto& sample : samples_) { timeseries.samples().emplace_back(sample.timestamp(), sample.value()); } } + + private: + LabelViewSet label_set_; + std::vector samples_; + size_t hash_; }; class HashdexTest : public std::vector { @@ -389,6 +77,8 @@ class HashdexTest : public std::vector { public: using Base::Base; + void emplace_back(LabelViewSet&& label_set, std::vector&& samples) { Base::emplace_back(std::move(label_set), std::move(samples)); } + [[nodiscard]] PROMPP_ALWAYS_INLINE const auto& metrics() const noexcept { return *this; } [[nodiscard]] static PROMPP_ALWAYS_INLINE auto metadata() noexcept { struct Stub {}; @@ -398,109 +88,76 @@ class HashdexTest : public std::vector { static_assert(PromPP::Prometheus::hashdex::HashdexInterface); -PROMPP_ALWAYS_INLINE void make_hashdex(HashdexTest& hx, LabelViewSetForTest label_set, std::vector samples) { - hx.emplace_back(hash_value(label_set), label_set, samples); -} - -// -// TestValidate -// - -struct TestValidate : public testing::Test { - PromPP::Primitives::LabelsBuilderStateMap builder_state_; +struct HardValidateCase { + LabelViewSet labels; + std::optional limits{std::nullopt}; + relabelStatus expected; }; -TEST_F(TestValidate, HardValid) { - LabelViewSetForTest incoming_labels = make_label_set({{"__name__", "value"}, {"job", "abc"}}); - PromPP::Primitives::LabelsBuilder builder{builder_state_}; - builder.reset(incoming_labels); - - Relabel::relabelStatus rstatus{Relabel::rsKeep}; - Relabel::hard_validate(rstatus, builder, nullptr); - EXPECT_EQ(Relabel::rsKeep, rstatus); -} - -TEST_F(TestValidate, HardInvalid) { - LabelViewSetForTest incoming_labels = make_label_set({{"__value__", "value"}, {"job", "abc"}}); - PromPP::Primitives::LabelsBuilder builder{builder_state_}; - builder.reset(incoming_labels); - - Relabel::relabelStatus rstatus{Relabel::rsKeep}; - Relabel::hard_validate(rstatus, builder, nullptr); - EXPECT_EQ(Relabel::rsInvalid, rstatus); -} - -TEST_F(TestValidate, InvalidLabelLimit) { - LabelViewSetForTest incoming_labels = make_label_set({{"__name__", "value"}, {"job", "abc"}, {"jub", "buj"}}); - PromPP::Primitives::LabelsBuilder builder{builder_state_}; - builder.reset(incoming_labels); - - // label_limit 0 - Relabel::MetricLimits ll{}; - Relabel::relabelStatus rstatus{Relabel::rsKeep}; - Relabel::hard_validate(rstatus, builder, &ll); - EXPECT_EQ(Relabel::rsKeep, rstatus); - - // label_limit 2 - ll.label_limit = 2; - Relabel::hard_validate(rstatus, builder, &ll); - EXPECT_EQ(Relabel::rsInvalid, rstatus); - - // label_name_length_limit 3 - rstatus = Relabel::rsKeep; - ll.label_limit = 3; - ll.label_name_length_limit = 3; - Relabel::hard_validate(rstatus, builder, &ll); - EXPECT_EQ(Relabel::rsInvalid, rstatus); - - // label_value_length_limit 3 - rstatus = Relabel::rsKeep; - ll.label_limit = 3; - ll.label_name_length_limit = 10; - ll.label_value_length_limit = 3; - Relabel::hard_validate(rstatus, builder, &ll); - EXPECT_EQ(Relabel::rsInvalid, rstatus); -} +class HardValidateFixture : public testing::TestWithParam { + protected: + LabelsBuilder builder_; + relabelStatus rstatus_{rsKeep}; +}; -// -// PerShardRelabeler -// +TEST_P(HardValidateFixture, Test) { + // Arrange + builder_.reset(GetParam().labels); + + // Act + hard_validate(rstatus_, builder_, GetParam().limits ? &GetParam().limits.value() : nullptr); + + // Assert + EXPECT_EQ(GetParam().expected, rstatus_); +} + +INSTANTIATE_TEST_SUITE_P(Valid, HardValidateFixture, testing::Values(HardValidateCase{.labels = {{"__name__", "value"}, {"job", "abc"}}, .expected = rsKeep})); +INSTANTIATE_TEST_SUITE_P(Invalid, + HardValidateFixture, + testing::Values(HardValidateCase{.labels = {{"__value__", "value"}, {"job", "abc"}}, .expected = rsInvalid})); +INSTANTIATE_TEST_SUITE_P( + NoLimit, + HardValidateFixture, + testing::Values(HardValidateCase{.labels = {{"__name__", "value"}, {"job", "abc"}, {"jub", "buj"}}, .limits = MetricLimits{}, .expected = rsKeep})); +INSTANTIATE_TEST_SUITE_P(LabelCountLimitExceeded, + HardValidateFixture, + testing::Values(HardValidateCase{.labels = {{"__name__", "value"}, {"job", "abc"}, {"jub", "buj"}}, + .limits = MetricLimits{.label_limit = 2}, + .expected = rsInvalid})); +INSTANTIATE_TEST_SUITE_P(LabelNameLengthLimitExceeded, + HardValidateFixture, + testing::Values(HardValidateCase{.labels = {{"__name__", "value"}, {"job", "abc"}, {"jub", "buj"}}, + .limits = MetricLimits{.label_name_length_limit = 3}, + .expected = rsInvalid})); +INSTANTIATE_TEST_SUITE_P(LabelValueLengthLimitExceeded, + HardValidateFixture, + testing::Values(HardValidateCase{.labels = {{"__name__", "value"}, {"job", "abc"}, {"jub", "buj"}}, + .limits = MetricLimits{.label_value_length_limit = 3}, + .expected = rsInvalid})); struct Stats { uint32_t samples_added{0}; uint32_t series_added{0}; uint32_t series_drop{0}; -}; -struct TestPerShardRelabeler : public testing::Test { - // shards_inner_series - std::vector> vector_shards_inner_series_; - PromPP::Primitives::Go::SliceView shards_inner_series_; + bool operator==(const Stats& other) const noexcept = default; +}; - // relabeled_results - std::vector> vector_relabeled_results_; - PromPP::Primitives::Go::SliceView relabeled_results_; +class PerGoroutineRelabelerFixture : public testing::Test { + protected: + static constexpr uint16_t kNumberOfShards = 2; - // external_labels - std::vector> vector_external_labels_; - PromPP::Primitives::Go::SliceView> external_labels_; + std::vector> vector_shards_inner_series_; + SliceView shards_inner_series_{}; - // target_labels - std::vector> vector_target_labels_{}; + std::vector> vector_relabeled_results_; + SliceView relabeled_results_{}; - // Options + std::vector vector_target_labels_{}; PromPP::Prometheus::Relabel::RelabelerOptions o_; - - // Stats Stats stats_; - - // Hashdex HashdexTest hx_; - - // LSS - PromPP::Primitives::SnugComposites::LabelSet::EncodingBimap lss_; - - // Cache + EncodingBimap lss_; PromPP::Prometheus::Relabel::Cache cache_{}; void reset() { @@ -508,526 +165,432 @@ struct TestPerShardRelabeler : public testing::Test { SetUp(); } - void add_target_labels(std::vector>& list_target_labels) { - vector_target_labels_.resize(list_target_labels.size()); + void add_target_labels(const LabelViewSet& target_labels) { + vector_target_labels_.resize(target_labels.size()); for (size_t i = 0; i < vector_target_labels_.size(); i++) { - vector_target_labels_[i].first.reset_to(list_target_labels[i].first.data(), list_target_labels[i].first.size()); - vector_target_labels_[i].second.reset_to(list_target_labels[i].second.data(), list_target_labels[i].second.size()); + vector_target_labels_[i].first.reset_to(target_labels[i].first.data(), target_labels[i].first.size()); + vector_target_labels_[i].second.reset_to(target_labels[i].second.data(), target_labels[i].second.size()); } o_.target_labels.reset_to(vector_target_labels_.data(), vector_target_labels_.size(), vector_target_labels_.size()); } void SetUp() final { - // shards_inner_series - vector_shards_inner_series_.emplace_back(std::make_unique()); - vector_shards_inner_series_.emplace_back(std::make_unique()); - shards_inner_series_.reset_to(reinterpret_cast(vector_shards_inner_series_.data()), - vector_shards_inner_series_.size(), vector_shards_inner_series_.size()); + vector_shards_inner_series_.emplace_back(std::make_unique()); + vector_shards_inner_series_.emplace_back(std::make_unique()); + shards_inner_series_.reset_to(reinterpret_cast(vector_shards_inner_series_.data()), vector_shards_inner_series_.size(), + vector_shards_inner_series_.size()); - // relabeled_results vector_relabeled_results_.emplace_back(std::make_unique()); vector_relabeled_results_.emplace_back(std::make_unique()); relabeled_results_.reset_to(reinterpret_cast(vector_relabeled_results_.data()), vector_shards_inner_series_.size(), vector_shards_inner_series_.size()); - // external_labels - external_labels_.reset_to(vector_external_labels_.data(), vector_external_labels_.size(), vector_external_labels_.size()); - - // target_labels o_.target_labels.reset_to(vector_target_labels_.data(), vector_target_labels_.size(), vector_target_labels_.size()); } void TearDown() final { - // clear memory vector_shards_inner_series_.clear(); vector_relabeled_results_.clear(); } }; -TEST_F(TestPerShardRelabeler, KeepEQ) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abc"}}), make_samples({{1712567046855, 0.1}})); +TEST_F(PerGoroutineRelabelerFixture, KeepOnNotFoundInCache) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{1000, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); + + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(update_data.size(), 0); - - vector_shards_inner_series_[1] = std::make_unique(); - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(stats_.samples_added, 2); - EXPECT_EQ(stats_.series_added, 1); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 0}}, shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); } -TEST_F(TestPerShardRelabeler, KeepEQ_OrderedEncodingBimap) { - PromPP::Primitives::SnugComposites::LabelSet::OrderedEncodingBimap lss; - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abc"}}), make_samples({{1712567046855, 0.1}})); +TEST_F(PerGoroutineRelabelerFixture, InnerSeriesAlreadyAdded) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{1000, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; + + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); - prs.input_relabeling(lss, lss, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(update_data.size(), 0); - - vector_shards_inner_series_[1] = std::make_unique(); - prs.input_relabeling(lss, lss, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(stats_.samples_added, 2); - EXPECT_EQ(stats_.series_added, 1); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 0}}, shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); } -TEST_F(TestPerShardRelabeler, KeepNE) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "no-match", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abc"}}), make_samples({{1712567046855, 0.1}})); +TEST_F(PerGoroutineRelabelerFixture, KeepOnFoundInCache) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{1000, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; + + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); + shards_inner_series_[1]->clear(); + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 0); - EXPECT_EQ(stats_.samples_added, 0); - EXPECT_EQ(stats_.series_added, 0); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 0}}, shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 2, .series_added = 1}), stats_); } -TEST_F(TestPerShardRelabeler, KeepEQNE) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abc"}}), make_samples({{1712567046855, 0.1}})); - - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 - EXPECT_EQ(relabeled_results_[0]->size(), 0); - EXPECT_EQ(relabeled_results_[1]->size(), 0); - EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); +TEST_F(PerGoroutineRelabelerFixture, KeepNotEqual) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "no-match", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{1000, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(update_data.size(), 0); + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); - reset(); - hx_.clear(); - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abcd"}}), make_samples({{1712567046855, 0.1}})); - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 skip + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); EXPECT_EQ(shards_inner_series_[1]->size(), 0); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); + EXPECT_EQ(Stats{.series_drop = 1}, stats_); } -TEST_F(TestPerShardRelabeler, ReplaceToNewLS2) { - RelabelConfigTest rct{.source_labels = std::vector{"__name__"}, - .separator = ";", - .regex = ".*(o).*", - .target_label = "replaced", - .replacement = "$1", - .action = 5}; // Replace - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}}), make_samples({{1712567046855, 0.1}})); - - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 - EXPECT_EQ(relabeled_results_[0]->size(), 0); - EXPECT_EQ(relabeled_results_[1]->size(), 1); - EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 0); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(update_data.size(), 1); - - prs.update_relabeler_state(cache_, &update_data, 1); +TEST_F(PerGoroutineRelabelerFixture, KeepEqualThenNotEqual) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{1000, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; - auto rlabels = lss_[update_data[0].relabeled_ls_id]; - LabelViewSetForTest expected_labels = make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}, {"replaced", "o"}}); - - EXPECT_EQ(rlabels, expected_labels); -} - -TEST_F(TestPerShardRelabeler, ReplaceToNewLS3) { - RelabelConfigTest rct{.separator = ";", .regex = ".*", .target_label = "replaced", .replacement = "blabla", .action = 5}; // Replace - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}}), make_samples({{1712567046855, 0.1}})); + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); + reset(); + hx_ = HashdexTest{{{{"__name__", "value"}, {"job", "abcd"}}, {{1000, 0.1}}}}; + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 - EXPECT_EQ(relabeled_results_[0]->size(), 1); + // Assert + EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); EXPECT_EQ(shards_inner_series_[1]->size(), 0); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[0], relabeled_results_[0], &update_data); - EXPECT_EQ(shards_inner_series_[0]->size(), 1); - EXPECT_EQ(update_data.size(), 1); - - prs.update_relabeler_state(cache_, &update_data, 1); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1, .series_drop = 1}), stats_); +} - auto rlabels = lss_[update_data[0].relabeled_ls_id]; - LabelViewSetForTest expected_labels = make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}, {"replaced", "blabla"}}); +TEST_F(PerGoroutineRelabelerFixture, ReplaceToNewLS2) { + // Arrange + const RelabelConfig config{ + .source_labels = {{"__name__"}}, .separator = ";", .regex = ".*(o).*", .target_label = "replaced", .replacement = "$1", .action = rReplace}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}}, {{1000, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; + RelabelerStateUpdate update_data; - EXPECT_EQ(rlabels, expected_labels); -} + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); -TEST_F(TestPerShardRelabeler, ReplaceToNewLS2_OrderedEncodingBimap) { - PromPP::Primitives::SnugComposites::LabelSet::OrderedEncodingBimap lss; - RelabelConfigTest rct{.source_labels = std::vector{"__name__"}, - .separator = ";", - .regex = ".*(o).*", - .target_label = "replaced", - .replacement = "$1", - .action = 5}; // Replace - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}}), make_samples({{1712567046855, 0.1}})); - - prs.input_relabeling(lss, lss, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 1); EXPECT_EQ(shards_inner_series_[0]->size(), 0); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 1}}, shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); + ASSERT_EQ(1U, update_data.size()); + EXPECT_EQ((LabelViewSet{{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}, {"replaced", "o"}}), lss_[update_data[0].relabeled_ls_id]); +} + +TEST_F(PerGoroutineRelabelerFixture, ReplaceToNewLS3) { + // Arrange + const RelabelConfig config{.separator = ";", .regex = ".*", .target_label = "replaced", .replacement = "blabla", .action = rReplace}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}}, {{1000, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; + RelabelerStateUpdate update_data; + + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[0], relabeled_results_[0], &update_data); + PerShardRelabeler::update_relabeler_state(cache_, &update_data, 1); + + // Assert + EXPECT_EQ(relabeled_results_[0]->size(), 1); + EXPECT_EQ(relabeled_results_[1]->size(), 0); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 1}}, shards_inner_series_[0]->data())); EXPECT_EQ(shards_inner_series_[1]->size(), 0); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); EXPECT_EQ(update_data.size(), 1); - - prs.update_relabeler_state(cache_, &update_data, 1); - - auto rlabels = lss[update_data[0].relabeled_ls_id]; - LabelViewSetForTest expected_labels = make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}, {"replaced", "o"}}); - - EXPECT_EQ(rlabels, expected_labels); + EXPECT_EQ((LabelViewSet{{"__name__", "booom"}, {"jab", "baj"}, {"job", "baj"}, {"replaced", "blabla"}}), lss_[update_data[0].relabeled_ls_id]); } -TEST_F(TestPerShardRelabeler, InputRelabelingWithStalenans_Default) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abc"}}), make_samples({{PromPP::Primitives::kNullTimestamp, 0.1}})); - PromPP::Prometheus::Relabel::StaleNaNsState state{}; - PromPP::Primitives::Timestamp def_timestamp{1712567046955}; +TEST_F(PerGoroutineRelabelerFixture, InputRelabelingWithStalenans_Default) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{kNullTimestamp, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; + StaleNaNsState state; + RelabelerStateUpdate update_data; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_, state, def_timestamp); + // Act + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, 1000); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, HashdexTest{}, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, + 2000); - // shard id 1 + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(shards_inner_series_[1]->data()[0].sample.timestamp(), def_timestamp); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); + EXPECT_EQ(shards_inner_series_[1]->size(), 2); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 0}, {.sample = Sample(2000, kStaleNan), .ls_id = 0}}, + shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); EXPECT_EQ(update_data.size(), 0); - - vector_shards_inner_series_[1] = std::make_unique(); - HashdexTest empty_hx; - PromPP::Primitives::Timestamp stale_ts = 1712567047055; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, empty_hx, o_, stats_, shards_inner_series_, relabeled_results_, state, stale_ts); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(PromPP::Primitives::Sample(stale_ts, PromPP::Prometheus::kStaleNan), shards_inner_series_[1]->data()[0].sample); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); } -TEST_F(TestPerShardRelabeler, InputRelabelingWithStalenans_DefaultHonorTimestamps) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abc"}}), make_samples({{PromPP::Primitives::kNullTimestamp, 0.1}})); - PromPP::Prometheus::Relabel::StaleNaNsState state{}; - PromPP::Primitives::Timestamp def_timestamp{1712567046955}; +TEST_F(PerGoroutineRelabelerFixture, InputRelabelingWithStalenans_DefaultHonorTimestamps) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{kNullTimestamp, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; + StaleNaNsState state; + RelabelerStateUpdate update_data; o_.honor_timestamps = true; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_, state, def_timestamp); + // Act + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, 1000); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, HashdexTest{}, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, + 2000); - // shard id 1 + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(shards_inner_series_[1]->data()[0].sample.timestamp(), def_timestamp); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); + EXPECT_EQ(shards_inner_series_[1]->size(), 2); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 0}, {.sample = Sample(2000, kStaleNan), .ls_id = 0}}, + shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); EXPECT_EQ(update_data.size(), 0); - - vector_shards_inner_series_[1] = std::make_unique(); - HashdexTest empty_hx; - PromPP::Primitives::Timestamp stale_ts = 1712567047055; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, empty_hx, o_, stats_, shards_inner_series_, relabeled_results_, state, stale_ts); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(PromPP::Primitives::Sample(stale_ts, PromPP::Prometheus::kStaleNan), shards_inner_series_[1]->data()[0].sample); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); } -TEST_F(TestPerShardRelabeler, InputRelabelingWithStalenans_WithMetricTimestamp) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abc"}}), make_samples({{1712567046855, 0.1}})); - PromPP::Prometheus::Relabel::StaleNaNsState state{}; - PromPP::Primitives::Timestamp def_timestamp{1712567046955}; +TEST_F(PerGoroutineRelabelerFixture, InputRelabelingWithStalenans_WithMetricTimestamp) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{1712567046855, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; + StaleNaNsState state; + RelabelerStateUpdate update_data; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_, state, def_timestamp); + // Act + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, 1000); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, HashdexTest{}, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, + 2000); - // shard id 1 + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(shards_inner_series_[1]->data()[0].sample.timestamp(), def_timestamp); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); + EXPECT_EQ(shards_inner_series_[1]->size(), 2); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 0}, {.sample = Sample(2000, kStaleNan), .ls_id = 0}}, + shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); EXPECT_EQ(update_data.size(), 0); - - vector_shards_inner_series_[1] = std::make_unique(); - HashdexTest empty_hx; - PromPP::Primitives::Timestamp stale_ts = 1712567047055; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, empty_hx, o_, stats_, shards_inner_series_, relabeled_results_, state, stale_ts); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(PromPP::Primitives::Sample(stale_ts, PromPP::Prometheus::kStaleNan), shards_inner_series_[1]->data()[0].sample); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); } -TEST_F(TestPerShardRelabeler, InputRelabelingWithStalenans_HonorTimestamps) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - PromPP::Primitives::Timestamp metric_timestamp{1712567046955}; - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abc"}}), make_samples({{metric_timestamp, 0.1}})); - PromPP::Prometheus::Relabel::StaleNaNsState state{}; - PromPP::Primitives::Timestamp def_timestamp{1712567046955}; +TEST_F(PerGoroutineRelabelerFixture, InputRelabelingWithStalenans_HonorTimestamps) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{1500, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; o_.honor_timestamps = true; + StaleNaNsState state; + RelabelerStateUpdate update_data; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_, state, def_timestamp); + // Act + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, 1000); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, HashdexTest{}, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, + 2000); - // shard id 1 + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(shards_inner_series_[1]->data()[0].sample.timestamp(), metric_timestamp); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1500, 0.1), .ls_id = 0}}, shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); EXPECT_EQ(update_data.size(), 0); - - vector_shards_inner_series_[1] = std::make_unique(); - HashdexTest empty_hx; - PromPP::Primitives::Timestamp stale_ts = 1712567047055; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, empty_hx, o_, stats_, shards_inner_series_, relabeled_results_, state, stale_ts); - EXPECT_EQ(shards_inner_series_[1]->size(), 0); } -TEST_F(TestPerShardRelabeler, InputRelabelingWithStalenans_HonorTimestampsAndTrackStaleness) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - PromPP::Primitives::Timestamp metric_timestamp{1712567046955}; - make_hashdex(hx_, make_label_set({{"__name__", "value"}, {"job", "abc"}}), make_samples({{metric_timestamp, 0.1}})); - PromPP::Prometheus::Relabel::StaleNaNsState state{}; - PromPP::Primitives::Timestamp def_timestamp{1712567046955}; +TEST_F(PerGoroutineRelabelerFixture, InputRelabelingWithStalenans_HonorTimestampsAndTrackStaleness) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "value"}, {"job", "abc"}}, {{1500, 0.1}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; o_.honor_timestamps = true; o_.track_timestamps_staleness = true; + StaleNaNsState state; + RelabelerStateUpdate update_data; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_, state, def_timestamp); + // Act + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, 1000); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); + relabeler.input_relabeling_with_stalenans(lss_, lss_, cache_, HashdexTest{}, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_, state, + 2000); - // shard id 1 + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 0); EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(shards_inner_series_[1]->data()[0].sample.timestamp(), metric_timestamp); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); + EXPECT_EQ(shards_inner_series_[1]->size(), 2); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1500, 0.1), .ls_id = 0}, {.sample = Sample(2000, kStaleNan), .ls_id = 0}}, + shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); EXPECT_EQ(update_data.size(), 0); - - vector_shards_inner_series_[1] = std::make_unique(); - HashdexTest empty_hx; - PromPP::Primitives::Timestamp stale_ts = 1712567047055; - prs.input_relabeling_with_stalenans(lss_, lss_, cache_, empty_hx, o_, stats_, shards_inner_series_, relabeled_results_, state, stale_ts); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); - EXPECT_EQ(PromPP::Primitives::Sample(stale_ts, PromPP::Prometheus::kStaleNan), shards_inner_series_[1]->data()[0].sample); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); } -TEST_F(TestPerShardRelabeler, TargetLabels_HappyPath) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 0); - make_hashdex(hx_, make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "abc"}}), make_samples({{1712567046855, 0.1}})); - std::vector> list_target_labels{{"a_name", "target_a_value"}, {"z_name", "target_z_value"}}; - add_target_labels(list_target_labels); +TEST_F(PerGoroutineRelabelerFixture, TargetLabels_HappyPath) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "booom"}, {"jab", "baj"}, {"job", "abc"}}, {{1000, 0.1}}); + add_target_labels(LabelViewSet{{"a_name", "target_a_value"}, {"z_name", "target_z_value"}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 0}; + RelabelerStateUpdate update_data; - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); + PerShardRelabeler::update_relabeler_state(cache_, &update_data, 1); + + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 0); EXPECT_EQ(relabeled_results_[1]->size(), 1); EXPECT_EQ(shards_inner_series_[0]->size(), 0); - EXPECT_EQ(shards_inner_series_[1]->size(), 0); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[1], relabeled_results_[1], &update_data); - EXPECT_EQ(shards_inner_series_[1]->size(), 1); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 1}}, shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); EXPECT_EQ(update_data.size(), 1); - - prs.update_relabeler_state(cache_, &update_data, 1); - - auto rlabels = lss_[update_data[0].relabeled_ls_id]; - LabelViewSetForTest expected_labels = - make_label_set({{"__name__", "booom"}, {"a_name", "target_a_value"}, {"jab", "baj"}, {"job", "abc"}, {"z_name", "target_z_value"}}); - - EXPECT_EQ(rlabels, expected_labels); + EXPECT_EQ((LabelViewSet{{"__name__", "booom"}, {"a_name", "target_a_value"}, {"jab", "baj"}, {"job", "abc"}, {"z_name", "target_z_value"}}), + lss_[update_data[0].relabeled_ls_id]); } -TEST_F(TestPerShardRelabeler, TargetLabels_ExportedLabel) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 0); - make_hashdex(hx_, make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "abc"}}), make_samples({{1712567046855, 0.1}})); - std::vector> list_target_labels{{"jab", "target_a_value"}, {"z_name", "target_z_value"}}; - add_target_labels(list_target_labels); +TEST_F(PerGoroutineRelabelerFixture, TargetLabels_ExportedLabel) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rsKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "booom"}, {"jab", "baj"}, {"job", "abc"}}, {{1000, 0.1}}); + add_target_labels(LabelViewSet{{"jab", "target_a_value"}, {"z_name", "target_z_value"}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 0}; + RelabelerStateUpdate update_data; - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[0], relabeled_results_[0], &update_data); + PerShardRelabeler::update_relabeler_state(cache_, &update_data, 1); + + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 1); EXPECT_EQ(relabeled_results_[1]->size(), 0); - EXPECT_EQ(shards_inner_series_[0]->size(), 0); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 1}}, shards_inner_series_[0]->data())); EXPECT_EQ(shards_inner_series_[1]->size(), 0); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[0], relabeled_results_[0], &update_data); - EXPECT_EQ(shards_inner_series_[0]->size(), 1); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); EXPECT_EQ(update_data.size(), 1); - - prs.update_relabeler_state(cache_, &update_data, 1); - - auto rlabels = lss_[update_data[0].relabeled_ls_id]; - LabelViewSetForTest expected_labels = - make_label_set({{"__name__", "booom"}, {"exported_jab", "baj"}, {"jab", "target_a_value"}, {"job", "abc"}, {"z_name", "target_z_value"}}); - - EXPECT_EQ(rlabels, expected_labels); + EXPECT_EQ((LabelViewSet{{"__name__", "booom"}, {"exported_jab", "baj"}, {"jab", "target_a_value"}, {"job", "abc"}, {"z_name", "target_z_value"}}), + lss_[update_data[0].relabeled_ls_id]); } -TEST_F(TestPerShardRelabeler, TargetLabels_ExportedLabel_Honor) { - RelabelConfigTest rct{.source_labels = std::vector{"job"}, .regex = "abc", .action = 2}; // Keep - Relabel::StatelessRelabeler sr(std::vector{&rct}); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 0); - make_hashdex(hx_, make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "abc"}}), make_samples({{1712567046855, 0.1}})); - std::vector> list_target_labels{{"jab", "target_a_value"}, {"z_name", "target_z_value"}}; - add_target_labels(list_target_labels); +TEST_F(PerGoroutineRelabelerFixture, TargetLabels_ExportedLabel_Honor) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rsKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_.emplace_back({{"__name__", "booom"}, {"jab", "baj"}, {"job", "abc"}}, {{1000, 0.1}}); + add_target_labels(LabelViewSet{{"jab", "target_a_value"}, {"z_name", "target_z_value"}}); + PerGoroutineRelabeler relabeler{kNumberOfShards, 0}; o_.honor_labels = true; + RelabelerStateUpdate update_data; - prs.input_relabeling(lss_, lss_, cache_, hx_, o_, stats_, shards_inner_series_, relabeled_results_); - // shard id 1 + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); + PerShardRelabeler::append_relabeler_series(lss_, shards_inner_series_[0], relabeled_results_[0], &update_data); + PerShardRelabeler::update_relabeler_state(cache_, &update_data, 1); + + // Assert EXPECT_EQ(relabeled_results_[0]->size(), 1); EXPECT_EQ(relabeled_results_[1]->size(), 0); - EXPECT_EQ(shards_inner_series_[0]->size(), 0); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, 0.1), .ls_id = 1}}, shards_inner_series_[0]->data())); EXPECT_EQ(shards_inner_series_[1]->size(), 0); - EXPECT_EQ(stats_.samples_added, 1); - EXPECT_EQ(stats_.series_added, 1); - - PromPP::Prometheus::Relabel::RelabelerStateUpdate update_data{}; - prs.append_relabeler_series(lss_, shards_inner_series_[0], relabeled_results_[0], &update_data); - EXPECT_EQ(shards_inner_series_[0]->size(), 1); + EXPECT_EQ((Stats{.samples_added = 1, .series_added = 1}), stats_); EXPECT_EQ(update_data.size(), 1); + EXPECT_EQ((LabelViewSet{{"__name__", "booom"}, {"jab", "baj"}, {"job", "abc"}, {"z_name", "target_z_value"}}), lss_[update_data[0].relabeled_ls_id]); +} - prs.update_relabeler_state(cache_, &update_data, 1); +TEST_F(PerGoroutineRelabelerFixture, SampleLimitExceeded) { + // Arrange + const RelabelConfig config{.source_labels = {{"job"}}, .regex = "abc", .action = rKeep}; + const StatelessRelabeler stateless_relabeler(std::initializer_list{&config}); + hx_ = HashdexTest{ + {{{"__name__", "value"}, {"job", "abc"}}, {{1000, kStaleNan}}}, + {{{"__name__", "value"}, {"job", "abc"}}, {{2000, 0.1}}}, + {{{"__name__", "value"}, {"job", "abc"}}, {{3000, 0.1}}}, + }; + MetricLimits limits{.sample_limit = 1}; + PerGoroutineRelabeler relabeler{kNumberOfShards, 1}; + o_.metric_limits = &limits; - auto rlabels = lss_[update_data[0].relabeled_ls_id]; - LabelViewSetForTest expected_labels = make_label_set({{"__name__", "booom"}, {"jab", "baj"}, {"job", "abc"}, {"z_name", "target_z_value"}}); + // Act + relabeler.input_relabeling(lss_, lss_, cache_, hx_, o_, stateless_relabeler, stats_, shards_inner_series_, relabeled_results_); - EXPECT_EQ(rlabels, expected_labels); + // Assert + EXPECT_EQ(relabeled_results_[0]->size(), 0); + EXPECT_EQ(relabeled_results_[1]->size(), 0); + EXPECT_EQ(shards_inner_series_[0]->size(), 0); + EXPECT_TRUE(std::ranges::equal(std::vector{{.sample = Sample(1000, kStaleNan), .ls_id = 0}, {.sample = Sample(2000, 0.1), .ls_id = 0}}, + shards_inner_series_[1]->data())); + EXPECT_EQ((Stats{.samples_added = 2, .series_added = 1}), stats_); } -struct TestTargetLabels : public testing::Test { - // target_labels - std::vector> vector_target_labels_; +class TargetLabelsFixture : public testing::Test { + protected: + static constexpr uint16_t kNumberOfShards = 2; + + std::vector vector_target_labels_; - // Options PromPP::Prometheus::Relabel::RelabelerOptions o_; + std::vector rcts_; + std::vector vector_external_labels_; + SliceView external_labels_{}; - // for init PerShardRelabeler - PromPP::Primitives::SnugComposites::LabelSet::EncodingBimap lss_; - std::vector rcts_; - std::vector> vector_external_labels_; - PromPP::Primitives::Go::SliceView> external_labels_; + LabelsBuilder builder_; + + StatelessRelabeler stateless_relabeler_{rcts_}; + PerShardRelabeler relabeler_{external_labels_, &stateless_relabeler_, kNumberOfShards, 1}; void SetUp() final { o_.target_labels.reset_to(vector_target_labels_.data(), vector_target_labels_.size(), vector_target_labels_.size()); external_labels_.reset_to(vector_external_labels_.data(), vector_external_labels_.size(), vector_external_labels_.size()); } - void add_target_labels(std::vector>& list_target_labels) { + void add_target_labels(const LabelViewSet& list_target_labels) { vector_target_labels_.resize(list_target_labels.size()); for (size_t i = 0; i < vector_target_labels_.size(); i++) { vector_target_labels_[i].first.reset_to(list_target_labels[i].first.data(), list_target_labels[i].first.size()); @@ -1037,226 +600,242 @@ struct TestTargetLabels : public testing::Test { } }; -TEST_F(TestTargetLabels, ResolveConflictingExposedLabels_EmptyConflictingLabels) { - auto labels = make_label_set({{"c_name", "c_value"}}); - PromPP::Primitives::LabelsBuilderStateMap builder_state; - PromPP::Primitives::LabelsBuilder builder{builder_state}; - builder.reset(labels); - Relabel::StatelessRelabeler sr(rcts_); - PromPP::Prometheus::Relabel::PerShardRelabeler prs(external_labels_, &sr, 2, 1); - std::vector conflicting_exposed_labels{}; - LabelViewSetForTest expected_labels = make_label_set({{"c_name", "c_value"}}); +TEST_F(TargetLabelsFixture, ResolveConflictingExposedLabels_EmptyConflictingLabels) { + // Arrange + const LabelViewSet labels{{"c_name", "c_value"}}; + + builder_.reset(labels); - prs.resolve_conflicting_exposed_labels(builder, conflicting_exposed_labels); + std::vector