From 2326597df418ec68db62a84ed0170b36190434ad Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 8 Apr 2025 10:05:22 +0000 Subject: [PATCH 01/90] for save --- .promu.yml | 2 +- model/labels/labels.go | 2 +- model/labels/labels_stringlabels.go | 44 +++++++++++++++++++++++++-- model/labels/sharding.go | 2 +- model/labels/sharding_stringlabels.go | 2 +- web/federate.go | 8 +++++ web/web.go | 2 +- 7 files changed, 55 insertions(+), 7 deletions(-) diff --git a/.promu.yml b/.promu.yml index 748e44a890..c7eca49130 100644 --- a/.promu.yml +++ b/.promu.yml @@ -19,7 +19,7 @@ build: - builtinassets - static - osusergo - - stringlabels + # - stringlabels windows: - builtinassets - stringlabels diff --git a/model/labels/labels.go b/model/labels/labels.go index 01514abf38..12dbefd270 100644 --- a/model/labels/labels.go +++ b/model/labels/labels.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !stringlabels && !dedupelabels +//go:build slicelabels package labels diff --git a/model/labels/labels_stringlabels.go b/model/labels/labels_stringlabels.go index 9ef764daec..879175c985 100644 --- a/model/labels/labels_stringlabels.go +++ b/model/labels/labels_stringlabels.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build stringlabels +//go:build !slicelabels && !dedupelabels package labels @@ -19,9 +19,12 @@ import ( "reflect" "slices" "strings" + "sync" "unsafe" "github.com/cespare/xxhash/v2" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" ) // Labels is implemented by a single flat string holding name/value pairs. @@ -478,6 +481,8 @@ func (b *Builder) Reset(base Labels) { // If no modifications were made, the original labels are returned. func (b *Builder) Labels() Labels { if len(b.del) == 0 && len(b.add) == 0 { + + ul.add(b.base.Hash()) return b.base } @@ -512,7 +517,10 @@ func (b *Builder) Labels() Labels { for ; a < len(b.add); a++ { buf = appendLabelTo(buf, &b.add[a]) } - return Labels{data: yoloString(buf)} + + ret := Labels{data: yoloString(buf)} + ul.add(ret.Hash()) + return ret } func marshalLabelsToSizedBuffer(lbls []Label, data []byte) int { @@ -663,6 +671,8 @@ func (b *ScratchBuilder) Labels() Labels { marshalLabelsToSizedBuffer(b.add, buf) b.output = Labels{data: yoloString(buf)} } + + ul.add(b.output.Hash()) return b.output } @@ -677,6 +687,8 @@ func (b *ScratchBuilder) Overwrite(ls *Labels) { } marshalLabelsToSizedBuffer(b.add, b.overwriteBuffer) ls.data = yoloString(b.overwriteBuffer) + + ul.add(ls.Hash()) } // Symbol-table is no-op, just for api parity with dedupelabels. @@ -699,3 +711,31 @@ func NewScratchBuilderWithSymbolTable(_ *SymbolTable, n int) ScratchBuilder { func (b *ScratchBuilder) SetSymbolTable(_ *SymbolTable) { // no-op } + +// +// uniqLables +// + +var ul = &uniqLables{ + counter: promauto.NewCounter( + prometheus.CounterOpts{ + Name: "prompp_labels_unique_total", + Help: "Current unique labels.", + }, + ), +} + +type uniqLables struct { + counter prometheus.Counter + sync.Map +} + +func (ul *uniqLables) add(hash uint64) { + _, loaded := ul.Map.LoadOrStore(hash, nil) + + if loaded { + return + } + + ul.counter.Inc() +} diff --git a/model/labels/sharding.go b/model/labels/sharding.go index 5e3e89fbbb..bdd7ca1fb1 100644 --- a/model/labels/sharding.go +++ b/model/labels/sharding.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !stringlabels && !dedupelabels +//go:build slicelabels package labels diff --git a/model/labels/sharding_stringlabels.go b/model/labels/sharding_stringlabels.go index 3ad2027d8c..c49e42c660 100644 --- a/model/labels/sharding_stringlabels.go +++ b/model/labels/sharding_stringlabels.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build stringlabels +//go:build !slicelabels && !dedupelabels package labels diff --git a/web/federate.go b/web/federate.go index 470255a00d..9a68c98a99 100644 --- a/web/federate.go +++ b/web/federate.go @@ -320,3 +320,11 @@ Loop: } } } + +func (h *Handler) federationMock(w http.ResponseWriter, req *http.Request) { + h.mtx.RLock() + defer h.mtx.RUnlock() + + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) +} diff --git a/web/web.go b/web/web.go index a2b4ed62bc..5e6a2ed3e9 100644 --- a/web/web.go +++ b/web/web.go @@ -389,7 +389,7 @@ func New(logger log.Logger, o *Options, receiver handler.Receiver) *Handler { // router.Get("/metrics", promhttp.Handler().ServeHTTP) router.Get("/federate", readyf(httputil.CompressionHandler{ - Handler: http.HandlerFunc(h.federation), + Handler: http.HandlerFunc(h.federationMock), }.ServeHTTP)) router.Get("/consoles/*filepath", readyf(h.consoles)) From 3609e1016be40945ace975f39fce4d9cac28a331 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Wed, 9 Apr 2025 18:29:51 +0300 Subject: [PATCH 02/90] don't rebuild sorting index on insert error --- pp/series_index/queryable_encoding_bimap.h | 8 +------- pp/series_index/sorting_index.h | 16 +++++++++++++--- pp/series_index/tests/sorting_index_tests.cpp | 4 ++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/pp/series_index/queryable_encoding_bimap.h b/pp/series_index/queryable_encoding_bimap.h index 246f6b44d3..8209d69a6b 100644 --- a/pp/series_index/queryable_encoding_bimap.h +++ b/pp/series_index/queryable_encoding_bimap.h @@ -29,10 +29,6 @@ class QueryableEncodingBimap final template PROMPP_ALWAYS_INLINE void sort_series_ids(Iterator begin, Iterator end) noexcept { - if (sorting_index_.empty()) [[unlikely]] { - sorting_index_.build(); - } - sorting_index_.sort(begin, end); } @@ -128,9 +124,7 @@ class QueryableEncodingBimap final trie_index_.insert((*label).first, label.name_id(), (*label).second, label.value_id()); } - if (!sorting_index_.empty()) { - sorting_index_.update(ls_id_set_iterator); - } + sorting_index_.update(ls_id_set_iterator); } PROMPP_ALWAYS_INLINE static bool is_valid_label(std::string_view value) noexcept { return !value.empty(); } diff --git a/pp/series_index/sorting_index.h b/pp/series_index/sorting_index.h index 13c5c83b24..e80a8a4d5f 100644 --- a/pp/series_index/sorting_index.h +++ b/pp/series_index/sorting_index.h @@ -29,17 +29,27 @@ class SortingIndex { } PROMPP_ALWAYS_INLINE void update(typename Set::const_iterator ls_id_iterator) { + if (empty()) { + return; + } + const uint64_t previous = get_previous(ls_id_iterator); const uint64_t next = get_next(ls_id_iterator); - if (uint32_t value = (previous + next) / 2; value > previous) { + if (uint32_t value = (previous + next) / 2; value > previous) [[likely]] { sorting_index_.emplace_back(value); } else { - build(); + // If we can't insert item we don't need to rebuild index, because it's very expensive operation for CPU. + // Index will be built on demand in sort method + sorting_index_.clear(); } } template - void sort(Iterator begin, Iterator end) const noexcept { + PROMPP_ALWAYS_INLINE void sort(Iterator begin, Iterator end) noexcept { + if (empty()) [[unlikely]] { + build(); + } + std::sort(begin, end, [this](uint32_t a, uint32_t b) PROMPP_LAMBDA_INLINE { return sorting_index_[a] < sorting_index_[b]; }); } diff --git a/pp/series_index/tests/sorting_index_tests.cpp b/pp/series_index/tests/sorting_index_tests.cpp index d83f1113b9..fbe189168f 100644 --- a/pp/series_index/tests/sorting_index_tests.cpp +++ b/pp/series_index/tests/sorting_index_tests.cpp @@ -13,7 +13,7 @@ class SortingIndexFixture : public testing::Test { protected: struct LessComparator { - PROMPP_ALWAYS_INLINE bool operator()(uint32_t a, uint32_t b) const noexcept { return SortingIndexFixture::kItems[a] < SortingIndexFixture::kItems[b]; } + PROMPP_ALWAYS_INLINE static bool operator()(uint32_t a, uint32_t b) noexcept { return kItems[a] < kItems[b]; } }; using Set = phmap::btree_set; @@ -57,7 +57,7 @@ TEST_F(SortingIndexFixture, UpdateAndSort) { EXPECT_THAT(series_ids, testing::ElementsAre("a"_idx, "b"_idx, "d"_idx)); } -TEST_F(SortingIndexFixture, RebuildIndex) { +TEST_F(SortingIndexFixture, BuildIndexInSort) { // Arrange index_.update(set_.emplace(0).first); index_.update(set_.emplace(1).first); From 55d57f32ed1f4c1d195597fac955f9209a176657 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Thu, 10 Apr 2025 09:09:33 +0300 Subject: [PATCH 03/90] added reserve method for QueryableEncodingBimap --- pp/series_index/queried_series.h | 6 ++++++ pp/series_index/queryable_encoding_bimap.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/pp/series_index/queried_series.h b/pp/series_index/queried_series.h index a6c9a73f48..3bbd52665d 100644 --- a/pp/series_index/queried_series.h +++ b/pp/series_index/queried_series.h @@ -21,6 +21,12 @@ class QueriedSeries { } } + PROMPP_ALWAYS_INLINE void reserve(uint32_t series_count) { + for (auto& queried_series : queried_series_) { + queried_series.reserve(series_count); + } + } + template void set(Source source, const SeriesIdContainer& ids) noexcept { for (auto id : ids) { diff --git a/pp/series_index/queryable_encoding_bimap.h b/pp/series_index/queryable_encoding_bimap.h index 8209d69a6b..4df64102e7 100644 --- a/pp/series_index/queryable_encoding_bimap.h +++ b/pp/series_index/queryable_encoding_bimap.h @@ -84,6 +84,12 @@ class QueryableEncodingBimap final [[nodiscard]] PROMPP_ALWAYS_INLINE uint32_t queried_series_count(QueriedSeries::Source source) const noexcept { return queried_series_.count(source); } + PROMPP_ALWAYS_INLINE void reserve(uint32_t count) { + Base::items_.reserve(count); + ls_id_hash_set_.reserve(count); + queried_series_.reserve(count); + } + private: using LabelSet = typename Base::value_type; From 1dff133f355f59d41e53d053f27830218f4be719 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Thu, 10 Apr 2025 09:10:54 +0300 Subject: [PATCH 04/90] created performance test for QueryableEncodingBimap --- pp/performance_tests/performance_tests.cpp | 2 + ...generate_queryable_encoding_bimap_test.cpp | 53 +++++++++++++++++++ .../generate_queryable_encoding_bimap_test.h | 14 +++++ 3 files changed, 69 insertions(+) create mode 100644 pp/performance_tests/series_index/generate_queryable_encoding_bimap_test.cpp create mode 100644 pp/performance_tests/series_index/generate_queryable_encoding_bimap_test.h diff --git a/pp/performance_tests/performance_tests.cpp b/pp/performance_tests/performance_tests.cpp index 96422fd4e0..c3955a31fa 100644 --- a/pp/performance_tests/performance_tests.cpp +++ b/pp/performance_tests/performance_tests.cpp @@ -19,6 +19,7 @@ #include "save_lss_to_wal_test.h" #include "series_data_encoder_test.h" #include "series_index/generate_cedarpp_series_index_test.h" +#include "series_index/generate_queryable_encoding_bimap_test.h" #include "series_index/generate_series_reverse_index_test.h" #include "tests_database.h" #include "write_protobuf_non_naned_wal_test.h" @@ -73,6 +74,7 @@ int main([[maybe_unused]] int argc, char* argv[]) { test_db.add(std::make_unique()); test_db.add(std::make_unique()); test_db.add(std::make_unique()); + test_db.add(std::make_unique()); test_db.add(std::make_unique()); test_db.add(std::make_unique()); test_db.add(std::make_unique()); diff --git a/pp/performance_tests/series_index/generate_queryable_encoding_bimap_test.cpp b/pp/performance_tests/series_index/generate_queryable_encoding_bimap_test.cpp new file mode 100644 index 0000000000..6f77adb87a --- /dev/null +++ b/pp/performance_tests/series_index/generate_queryable_encoding_bimap_test.cpp @@ -0,0 +1,53 @@ +#include "generate_queryable_encoding_bimap_test.h" + +#include + +#include "performance_tests/dummy_wal.h" +#include "series_index/queryable_encoding_bimap.h" +#include "series_index/trie/cedarpp_tree.h" +#include "wal/wal.h" + +namespace performance_tests::series_index { + +using TrieIndex = ::series_index::TrieIndex<::series_index::trie::CedarTrie, ::series_index::trie::CedarMatchesList>; +using QueryableEncodingBimap = + ::series_index::QueryableEncodingBimap; + +void GenerateQueryableEncodingBimap::execute([[maybe_unused]] const Config& config, [[maybe_unused]] Metrics& metrics) const { + DummyWal::Timeseries tmsr; + DummyWal dummy_wal(input_file_full_name(config)); + + QueryableEncodingBimap lss; + //lss.reserve(1200000); + + uint32_t find_or_emplace_call_count = 0; + uint32_t max_ls_id = std::numeric_limits::min(); + std::chrono::nanoseconds find_or_emplace_time{}; + std::chrono::nanoseconds emplace_time{}; + while (dummy_wal.read_next_segment()) { + while (dummy_wal.read_next(tmsr)) { + auto start_tm = std::chrono::steady_clock::now(); + const auto ls_id = lss.find_or_emplace(tmsr.label_set()); + const auto elapsed_time = std::chrono::steady_clock::now() - start_tm; + find_or_emplace_time += elapsed_time; + ++find_or_emplace_call_count; + if (ls_id > max_ls_id) { + max_ls_id = ls_id; + emplace_time += elapsed_time; + } + BareBones::compiler::do_not_optimize(start_tm); + BareBones::compiler::do_not_optimize(find_or_emplace_call_count); + } + } + + std::vector gg; + const auto start_tm = std::chrono::steady_clock::now(); + lss.sort_series_ids(gg); + + std::cout << "lss_allocated_memory_kb: " << lss.allocated_memory() / 1024 << std::endl; + std::cout << "build_sort_index_time_nanoseconds: " << (std::chrono::steady_clock::now() - start_tm).count() << std::endl; + std::cout << "find_or_emplace_time_nanoseconds: " << find_or_emplace_time.count() / find_or_emplace_call_count << std::endl; + std::cout << "emplace_time_nanoseconds: " << emplace_time.count() / (max_ls_id + 1) << std::endl; +} + +} // namespace performance_tests::series_index \ No newline at end of file diff --git a/pp/performance_tests/series_index/generate_queryable_encoding_bimap_test.h b/pp/performance_tests/series_index/generate_queryable_encoding_bimap_test.h new file mode 100644 index 0000000000..8917472e3c --- /dev/null +++ b/pp/performance_tests/series_index/generate_queryable_encoding_bimap_test.h @@ -0,0 +1,14 @@ +#pragma once + +#include "performance_tests/test_with_input_only.h" + +namespace performance_tests::series_index { + +struct GenerateQueryableEncodingBimap : TestWithInputOnly { + std::string input_file_base_name() const final { return "dummy_wal"; } + bool has_output() const final { return false; } + std::string output_file_base_name() const final { return ""; } + void execute(const Config& config, Metrics& metrics) const final; +}; + +} // namespace performance_tests::series_index From 94c65b46d1d36e18f88d4f0b37dbc3c857b6a49e Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Thu, 10 Apr 2025 10:41:19 +0300 Subject: [PATCH 05/90] added reserving memory for containers in append_relabeler_series method --- pp/entrypoint/prometheus_relabeler.cpp | 4 +--- pp/prometheus/relabeler.h | 28 +++++++++----------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/pp/entrypoint/prometheus_relabeler.cpp b/pp/entrypoint/prometheus_relabeler.cpp index e1ee712ef7..3433aea4d7 100644 --- a/pp/entrypoint/prometheus_relabeler.cpp +++ b/pp/entrypoint/prometheus_relabeler.cpp @@ -125,9 +125,7 @@ extern "C" void prompp_prometheus_relabeler_state_update_dtor(void* args) { PromPP::Prometheus::Relabel::RelabelerStateUpdate* relabeler_state_update; }; - Arguments* in = reinterpret_cast(args); - - in->relabeler_state_update->~RelabelerStateUpdate(); + static_cast(args)->relabeler_state_update->~vector(); } // diff --git a/pp/prometheus/relabeler.h b/pp/prometheus/relabeler.h index 581fa9885e..bfe9d69907 100644 --- a/pp/prometheus/relabeler.h +++ b/pp/prometheus/relabeler.h @@ -189,23 +189,7 @@ struct IncomingAndRelabeledLsID { }; // RelabelerStateUpdate - container for update states. -class RelabelerStateUpdate { - std::vector data_; - - public: - PROMPP_ALWAYS_INLINE explicit RelabelerStateUpdate() {} - - PROMPP_ALWAYS_INLINE const std::vector& data() const { return data_; } - - PROMPP_ALWAYS_INLINE void emplace_back(const uint32_t incoming_ls_id, uint32_t relabeled_ls_id) { data_.emplace_back(incoming_ls_id, relabeled_ls_id); } - - PROMPP_ALWAYS_INLINE size_t size() const { return data_.size(); } - - PROMPP_ALWAYS_INLINE const IncomingAndRelabeledLsID& operator[](uint32_t i) const { - assert(i < data_.size()); - return data_[i]; - } -}; +using RelabelerStateUpdate = std::vector; class NoOpStaleNaNsState { public: @@ -695,10 +679,16 @@ class PerShardRelabeler { 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) { + lss.reserve(lss.size() + relabeled_series->size()); + } + for (const auto& relabeled_serie : relabeled_series->data()) { uint32_t ls_id = lss.find_or_emplace(relabeled_serie.ls, relabeled_serie.hash); - for (const PromPP::Primitives::Sample& sample : relabeled_serie.samples) { + for (const Primitives::Sample& sample : relabeled_serie.samples) { inner_series->emplace_back(sample, ls_id); } relabeler_state_update->emplace_back(relabeled_serie.ls_id, ls_id); @@ -707,7 +697,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) { - for (const auto& update : relabeler_state_update->data()) { + for (const auto& update : *relabeler_state_update) { cache.add_relabel(update.incoming_ls_id, update.relabeled_ls_id, relabeled_shard_id); } } From 14d15781afa321bc8294418340c7f62b686721e2 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Thu, 10 Apr 2025 13:00:20 +0000 Subject: [PATCH 06/90] add write timeout --- cmd/prometheus/main.go | 5 ++++- pp-pkg/receiver/receiver.go | 2 ++ pp/go/relabeler/appender/storage.go | 27 +++++++++++++++++---------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 4e36b90e4c..6c8a63997f 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -18,7 +18,6 @@ import ( "context" "errors" "fmt" - pptsdb "github.com/prometheus/prometheus/pp-pkg/tsdb" "math" "math/bits" "net" @@ -36,6 +35,8 @@ import ( "syscall" "time" + pptsdb "github.com/prometheus/prometheus/pp-pkg/tsdb" + "github.com/KimMachineGun/automemlimit/memlimit" "github.com/alecthomas/kingpin/v2" "github.com/alecthomas/units" @@ -715,6 +716,8 @@ func main() { time.Duration(cfg.WalCommitInterval), time.Duration(cfg.tsdb.RetentionDuration), time.Duration(cfg.HeadRetentionTimeout), + // x3 ScrapeInterval timeout for write block + time.Duration(cfgFile.GlobalConfig.ScrapeInterval*3), ) if err != nil { level.Error(logger).Log("msg", "failed to create a receiver", "err", err) diff --git a/pp-pkg/receiver/receiver.go b/pp-pkg/receiver/receiver.go index b14a9db933..058c5fa78c 100644 --- a/pp-pkg/receiver/receiver.go +++ b/pp-pkg/receiver/receiver.go @@ -129,6 +129,7 @@ func NewReceiver( commitInterval time.Duration, maxRetentionDuration time.Duration, headRetentionTimeout time.Duration, + writeTimeout time.Duration, ) (*Receiver, error) { if logger == nil { logger = log.NewNopLogger() @@ -201,6 +202,7 @@ func NewReceiver( clock, maxRetentionDuration, headRetentionTimeout, + writeTimeout, rotatedHeads..., ) diff --git a/pp/go/relabeler/appender/storage.go b/pp/go/relabeler/appender/storage.go index 204b513783..a98543ead9 100644 --- a/pp/go/relabeler/appender/storage.go +++ b/pp/go/relabeler/appender/storage.go @@ -3,6 +3,7 @@ package appender import ( "fmt" "sync" + "sync/atomic" "time" "github.com/jonboulle/clockwork" @@ -17,8 +18,8 @@ import ( ) const ( - PersistedHeadValue = -(1 << 30) - writeRetryTimeout = 5 * time.Minute + writeRetryTimeout = 5 * time.Minute + maxAddIter uint32 = 5 ) type WriteNotifier interface { @@ -39,8 +40,10 @@ type QueryableStorage struct { heads []relabeler.Head headRetentionTimeout time.Duration - signal chan struct{} - closer *util.Closer + writeTimer clockwork.Timer + writeTimeout time.Duration + addCount uint32 + closer *util.Closer clock clockwork.Clock maxRetentionDuration time.Duration @@ -57,6 +60,7 @@ func NewQueryableStorageWithWriteNotifier( clock clockwork.Clock, maxRetentionDuration time.Duration, headRetentionTimeout time.Duration, + writeTimeout time.Duration, heads ...relabeler.Head, ) *QueryableStorage { factory := util.NewUnconflictRegisterer(registerer) @@ -64,7 +68,8 @@ func NewQueryableStorageWithWriteNotifier( blockWriter: blockWriter, writeNotifier: writeNotifier, heads: heads, - signal: make(chan struct{}, 1), + writeTimer: clock.NewTimer(0), + writeTimeout: writeTimeout, closer: util.NewCloser(), clock: clock, maxRetentionDuration: maxRetentionDuration, @@ -79,6 +84,9 @@ func NewQueryableStorageWithWriteNotifier( querierMetrics: querierMetrics, } + // skip 0 start + <-qs.writeTimer.Chan() + return qs } @@ -109,7 +117,8 @@ func (qs *QueryableStorage) loop() { } select { - case <-qs.signal: + case <-qs.writeTimer.Chan(): + atomic.StoreUint32(&qs.addCount, 0) case <-retryTimer.Chan(): case <-qs.closer.Signal(): logger.Infof("QUERYABLE STORAGE: done") @@ -194,10 +203,8 @@ func (qs *QueryableStorage) Add(head relabeler.Head) { logger.Infof("QUERYABLE STORAGE: head %s added", head.String()) qs.mtx.Unlock() - select { - case qs.signal <- struct{}{}: - case <-qs.closer.Signal(): - default: + if atomic.AddUint32(&qs.addCount, 1) < maxAddIter { + qs.writeTimer.Reset(qs.writeTimeout) } } From 41e446136cce5a2752199b628178643cb43e89a6 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Thu, 10 Apr 2025 13:48:13 +0000 Subject: [PATCH 07/90] for save --- cmd/prometheus/main.go | 5 ++-- pp/go/cppbridge/entrypoint.go | 49 +++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 6c8a63997f..5f3ffe6f44 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -707,8 +707,9 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), - Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), + BlockDuration: 10 * time.Minute, + // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), + Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), }, headCatalog, reloadBlocksTriggerNotifier, diff --git a/pp/go/cppbridge/entrypoint.go b/pp/go/cppbridge/entrypoint.go index ee06d87d03..fc3909159b 100644 --- a/pp/go/cppbridge/entrypoint.go +++ b/pp/go/cppbridge/entrypoint.go @@ -27,10 +27,15 @@ import ( "github.com/prometheus/prometheus/pp/go/model" ) -var unsafeCall = promauto.NewCounterVec( - prometheus.CounterOpts{ +var unsafeCall = promauto.NewHistogramVec( + prometheus.HistogramOpts{ Name: "prompp_cppbridge_unsafecall_nanoseconds", Help: "The time duration cpp call.", + Buckets: []float64{ + 100, 500, + 1000, 5000, 10000, 50000, 100000, 500000, + 1000000, 5000000, 10000000, 50000000, 100000000, 500000000, + }, }, []string{"object", "method"}, ) @@ -1264,7 +1269,7 @@ func prometheusPerShardRelabelerInputRelabeling( ) unsafeCall.With( prometheus.Labels{"object": "input_relabeler", "method": "input_relabeling"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) return res.samplesAdded, res.seriesAdded, res.exception } @@ -1314,7 +1319,7 @@ func prometheusPerShardRelabelerInputRelabelingWithStalenans( ) unsafeCall.With( prometheus.Labels{"object": "input_relabeler", "method": "relabeling_with_stalenans"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) return res.samplesAdded, res.seriesAdded, res.exception } @@ -1345,7 +1350,7 @@ func prometheusPerShardRelabelerAppendRelabelerSeries( ) unsafeCall.With( prometheus.Labels{"object": "input_relabeler", "method": "append_relabeler_series"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) return res.exception } @@ -1373,7 +1378,7 @@ func prometheusPerShardRelabelerUpdateRelabelerState( ) unsafeCall.With( prometheus.Labels{"object": "input_relabeler", "method": "update_relabeler_state"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) return res.exception } @@ -1423,7 +1428,7 @@ func prometheusPerShardRelabelerResetTo( ) unsafeCall.With( prometheus.Labels{"object": "input_relabeler", "method": "reset_to"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) } func seriesDataDataStorageCtor() uintptr { @@ -1450,7 +1455,7 @@ func seriesDataDataStorageReset(dataStorage uintptr) { ) unsafeCall.With( prometheus.Labels{"object": "head_data_storage", "method": "reset"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) } func seriesDataDataStorageAllocatedMemory(dataStorage uintptr) uint64 { @@ -1468,7 +1473,7 @@ func seriesDataDataStorageAllocatedMemory(dataStorage uintptr) uint64 { ) unsafeCall.With( prometheus.Labels{"object": "head_data_storage", "method": "allocated_memory"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) return res.allocatedMemory } @@ -1489,7 +1494,7 @@ func seriesDataDataStorageQuery(dataStorage uintptr, query HeadDataStorageQuery) ) unsafeCall.With( prometheus.Labels{"object": "head_data_storage", "method": "query"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) return res.serializedChunks } @@ -1553,7 +1558,7 @@ func seriesDataEncoderEncode(encoder uintptr, seriesID uint32, timestamp int64, ) unsafeCall.With( prometheus.Labels{"object": "head_data_storage", "method": "encode"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) } func seriesDataEncoderEncodeInnerSeriesSlice(encoder uintptr, innerSeriesSlice []*InnerSeries) { @@ -1568,7 +1573,7 @@ func seriesDataEncoderEncodeInnerSeriesSlice(encoder uintptr, innerSeriesSlice [ ) unsafeCall.With( prometheus.Labels{"object": "head_data_storage", "method": "encode_inner_series_slice"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) } func seriesDataEncoderMergeOutOfOrderChunks(encoder uintptr) { @@ -1582,7 +1587,7 @@ func seriesDataEncoderMergeOutOfOrderChunks(encoder uintptr) { ) unsafeCall.With( prometheus.Labels{"object": "head_data_storage", "method": "merge_out_of_order_chunks"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) } func seriesDataEncoderDtor(encoder uintptr) { @@ -1719,7 +1724,7 @@ func seriesDataChunkRecoderRecodeNextChunk(chunkRecoder uintptr, recodedChunk *R ) unsafeCall.With( prometheus.Labels{"object": "chunk_recoder", "method": "recode_next_chunk"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) } func seriesDataChunkRecoderDtor(chunkRecoder uintptr) { @@ -1966,7 +1971,7 @@ func walPrometheusScraperHashdexParse(hashdex uintptr, buffer []byte, default_ti ) unsafeCall.With( prometheus.Labels{"object": "hashdex", "method": "parse"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) return res.scraped, res.error } @@ -2023,7 +2028,7 @@ func walOpenMetricsScraperHashdexParse(hashdex uintptr, buffer []byte, default_t ) unsafeCall.With( prometheus.Labels{"object": "hashdex", "method": "parse"}, - ).Add(float64(time.Since(start).Nanoseconds())) + ).Observe(float64(time.Since(start).Nanoseconds())) return res.scraped, res.error } @@ -2135,12 +2140,18 @@ func headWalEncoderAddInnerSeries(encoder uintptr, innerSeries []*InnerSeries) ( exception []byte } + start := time.Now() + fastcgo.UnsafeCall2( C.prompp_head_wal_encoder_add_inner_series, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) + unsafeCall.With( + prometheus.Labels{"object": "head_wal_encoder", "method": "add_inner_series"}, + ).Observe(float64(time.Since(start).Nanoseconds())) + return res.WALEncoderStats, handleException(res.exception) } @@ -2155,12 +2166,18 @@ func headWalEncoderFinalize(encoder uintptr) (stats WALEncoderStats, segment []b exception []byte } + start := time.Now() + fastcgo.UnsafeCall2( C.prompp_head_wal_encoder_finalize, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) + unsafeCall.With( + prometheus.Labels{"object": "head_wal_encoder", "method": "finalize"}, + ).Observe(float64(time.Since(start).Nanoseconds())) + return res.WALEncoderStats, res.segment, handleException(res.exception) } From 3e983f6f2c975fa59b54f4df5accf682699ce52e Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Thu, 10 Apr 2025 15:27:35 +0000 Subject: [PATCH 08/90] move merge --- pp/go/relabeler/appender/appender.go | 4 ++++ pp/go/relabeler/appender/head.go | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pp/go/relabeler/appender/appender.go b/pp/go/relabeler/appender/appender.go index e27905983f..fdbe732f2c 100644 --- a/pp/go/relabeler/appender/appender.go +++ b/pp/go/relabeler/appender/appender.go @@ -101,6 +101,8 @@ func (qa *QueryableAppender) Rotate() error { qa.lock.Lock() defer qa.lock.Unlock() + qa.head.MergeOutOfOrderChunks() + if err := qa.head.Rotate(); err != nil { return fmt.Errorf("failed to rotate head: %w", err) } @@ -119,6 +121,8 @@ func (qa *QueryableAppender) Reconfigure( qa.lock.Lock() defer qa.lock.Unlock() + qa.head.MergeOutOfOrderChunks() + if err := headConfigurator.Configure(qa.head); err != nil { return fmt.Errorf("failed to reconfigure head: %w", err) } diff --git a/pp/go/relabeler/appender/head.go b/pp/go/relabeler/appender/head.go index a60056aec5..9f07f6c6e5 100644 --- a/pp/go/relabeler/appender/head.go +++ b/pp/go/relabeler/appender/head.go @@ -142,7 +142,6 @@ func (h *RotatableHead) Rotate() error { return err } - h.head.MergeOutOfOrderChunks() if err = h.head.CommitToWal(); err != nil { logger.Errorf("failed to commit wal on rotation: %v", err) } @@ -163,7 +162,6 @@ func (h *RotatableHead) RotateWithConfig(inputRelabelerConfigs []*config.InputRe return err } - h.head.MergeOutOfOrderChunks() if err = h.head.CommitToWal(); err != nil { logger.Errorf("failed to commit wal on rotation: %v", err) } From f160e73d01173bdbec966d0486721329960788bb Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Fri, 11 Apr 2025 10:38:19 +0000 Subject: [PATCH 09/90] fix memory --- pp/bare_bones/memory.h | 1 + pp/series_data/data_storage.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pp/bare_bones/memory.h b/pp/bare_bones/memory.h index fad46dcbcb..1794ffa050 100644 --- a/pp/bare_bones/memory.h +++ b/pp/bare_bones/memory.h @@ -382,6 +382,7 @@ class SharedMemory : public GenericMemory, uint32_t, T> { data_.non_atomic_reallocate(new_size); } else { SharedPtr new_data(new_size); + new_data.set_constructed_item_count(data_.constructed_item_count()); PRAGMA_DIAGNOSTIC(push) PRAGMA_DIAGNOSTIC(ignored DIAGNOSTIC_CLASS_MEMACCESS) std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T)); diff --git a/pp/series_data/data_storage.h b/pp/series_data/data_storage.h index b2fb51a2a3..1028621bea 100644 --- a/pp/series_data/data_storage.h +++ b/pp/series_data/data_storage.h @@ -275,7 +275,9 @@ struct DataStorage { size_t encoders_memory = variant_encoders.allocated_memory() + gorilla_encoders.allocated_memory(); for (const auto& chunk : open_chunks) { - encoders_memory += variant_encoders[chunk.encoder.external_index].allocated_memory(chunk.encoding_state.encoding_type); + if (is_variant_encoder(chunk.encoding_state.encoding_type)) { + encoders_memory += variant_encoders[chunk.encoder.external_index].allocated_memory(chunk.encoding_state.encoding_type); + } } return open_chunks.allocated_memory() + encoders_memory + timestamp_encoder.allocated_memory() + finalized_timestamp_streams.allocated_memory() + From cf46711d8ff0993f4513ec76f932a6085d3f5ac3 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Fri, 11 Apr 2025 14:19:55 +0300 Subject: [PATCH 10/90] fixed bug with reallocation SharedMemory on non-unique ShredPtr --- pp/bare_bones/memory.h | 7 +++++-- pp/bare_bones/tests/memory_tests.cpp | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pp/bare_bones/memory.h b/pp/bare_bones/memory.h index fad46dcbcb..726ae16cab 100644 --- a/pp/bare_bones/memory.h +++ b/pp/bare_bones/memory.h @@ -227,7 +227,10 @@ class SharedPtr { static constexpr uint32_t kControlBlockSize = sizeof(ControlBlock); SharedPtr() = default; - explicit PROMPP_ALWAYS_INLINE SharedPtr(uint32_t size) : data_(nullptr) { non_atomic_reallocate(size); } + explicit PROMPP_ALWAYS_INLINE SharedPtr(uint32_t size, ItemCounter constructed_item_count = 0) : data_(nullptr) { + non_atomic_reallocate(size); + set_constructed_item_count(constructed_item_count); + } PROMPP_ALWAYS_INLINE SharedPtr(const SharedPtr& other) noexcept : data_(other.data_) { inc_atomic_ref_counter(); } SharedPtr(SharedPtr&& other) noexcept : data_(std::exchange(other.data_, nullptr)) {} @@ -381,7 +384,7 @@ class SharedMemory : public GenericMemory, uint32_t, T> { if (data_.non_atomic_is_unique()) [[likely]] { data_.non_atomic_reallocate(new_size); } else { - SharedPtr new_data(new_size); + SharedPtr new_data(new_size, constructed_item_count()); PRAGMA_DIAGNOSTIC(push) PRAGMA_DIAGNOSTIC(ignored DIAGNOSTIC_CLASS_MEMACCESS) std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T)); diff --git a/pp/bare_bones/tests/memory_tests.cpp b/pp/bare_bones/tests/memory_tests.cpp index 582676fa1d..c213cb7c43 100644 --- a/pp/bare_bones/tests/memory_tests.cpp +++ b/pp/bare_bones/tests/memory_tests.cpp @@ -335,6 +335,7 @@ TEST_F(SharedMemoryFixture, MoveOperator) { TEST_F(SharedMemoryFixture, ResizeOnNonUniqueOwner) { // Arrange memory_.resize_to_fit_at_least(1); + memory_.set_constructed_item_count(1); auto memory2 = memory_; // Act @@ -343,6 +344,7 @@ TEST_F(SharedMemoryFixture, ResizeOnNonUniqueOwner) { // Assert EXPECT_NE(memory_.size(), memory2.size()); EXPECT_NE(memory_.begin(), memory2.begin()); + EXPECT_EQ(1U, memory2.constructed_item_count()); } } // namespace \ No newline at end of file From 5ed1bb9495b1f8fa703a7953c4538e70338143f7 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Fri, 11 Apr 2025 14:34:58 +0300 Subject: [PATCH 11/90] fixed DataStorage::allocation_memory bug in debug build --- pp/series_data/data_storage.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pp/series_data/data_storage.h b/pp/series_data/data_storage.h index b2fb51a2a3..1028621bea 100644 --- a/pp/series_data/data_storage.h +++ b/pp/series_data/data_storage.h @@ -275,7 +275,9 @@ struct DataStorage { size_t encoders_memory = variant_encoders.allocated_memory() + gorilla_encoders.allocated_memory(); for (const auto& chunk : open_chunks) { - encoders_memory += variant_encoders[chunk.encoder.external_index].allocated_memory(chunk.encoding_state.encoding_type); + if (is_variant_encoder(chunk.encoding_state.encoding_type)) { + encoders_memory += variant_encoders[chunk.encoder.external_index].allocated_memory(chunk.encoding_state.encoding_type); + } } return open_chunks.allocated_memory() + encoders_memory + timestamp_encoder.allocated_memory() + finalized_timestamp_streams.allocated_memory() + From 45e314cbbd2760fa5837673dc91bb14f555056df Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Mon, 14 Apr 2025 14:26:10 +0300 Subject: [PATCH 12/90] created performance test for chunk recoder --- pp/BUILD | 1 + pp/performance_tests/chunk_recoder_test.cpp | 66 +++++++++++++++++++++ pp/performance_tests/chunk_recoder_test.h | 14 +++++ pp/performance_tests/performance_tests.cpp | 2 + 4 files changed, 83 insertions(+) create mode 100644 pp/performance_tests/chunk_recoder_test.cpp create mode 100644 pp/performance_tests/chunk_recoder_test.h diff --git a/pp/BUILD b/pp/BUILD index ec2d23047a..6b6497bcc2 100644 --- a/pp/BUILD +++ b/pp/BUILD @@ -214,6 +214,7 @@ cc_library( name = "performance_tests_headers", hdrs = glob(["performance_tests/**/*.h"]), deps = [ + ":head", ":primitives", ":prometheus", ":series_data", diff --git a/pp/performance_tests/chunk_recoder_test.cpp b/pp/performance_tests/chunk_recoder_test.cpp new file mode 100644 index 0000000000..7d269eb498 --- /dev/null +++ b/pp/performance_tests/chunk_recoder_test.cpp @@ -0,0 +1,66 @@ +#include "chunk_recoder_test.h" + +#include +#include + +#include "bare_bones/gorilla.h" +#include "head/chunk_recoder.h" +#include "performance_tests/dummy_wal.h" +#include "primitives/snug_composites.h" +#include "series_data/encoder.h" +#include "series_data/outdated_sample_encoder.h" + +namespace performance_tests { + +using BareBones::Encoding::Gorilla::StreamEncoder; +using BareBones::Encoding::Gorilla::TimestampDecoder; +using BareBones::Encoding::Gorilla::TimestampEncoder; +using BareBones::Encoding::Gorilla::ValuesEncoder; +using PromPP::Primitives::TimeInterval; +using series_data::Decoder; +using series_data::encoder::Sample; +using series_data::encoder::SampleList; + +void ChunkRecoder::execute(const Config& config, [[maybe_unused]] Metrics& metrics) const { + DummyWal::Timeseries tmsr; + DummyWal dummy_wal(input_file_full_name(config)); + + PromPP::Primitives::SnugComposites::LabelSet::EncodingBimap label_set_bitmap; + series_data::DataStorage storage; + series_data::Encoder encoder{storage}; + TimeInterval time_interval{.min = TimeInterval::kMax, .max = TimeInterval::kMin}; + + while (dummy_wal.read_next_segment()) { + while (dummy_wal.read_next(tmsr)) { + const auto ls_id = label_set_bitmap.find_or_emplace(tmsr.label_set()); + auto& sample = tmsr.samples()[0]; + encoder.encode(ls_id, sample.timestamp(), sample.value()); + + time_interval.min = std::min(time_interval.min, sample.timestamp()); + time_interval.max = std::max(time_interval.max, sample.timestamp()); + } + } + + // const std::ranges::iota_view ls_id_set(0, label_set_bitmap.size() - 1); + std::vector ls_id_set; + ls_id_set.resize(label_set_bitmap.size()); + std::iota(ls_id_set.begin(), ls_id_set.end(), 0); + + head::ChunkRecoder recoder(ls_id_set, &storage, time_interval); + + struct { + TimeInterval interval; + uint32_t series_id{}; + uint8_t samples_count{}; + BareBones::Vector buffer; + } chunk_data; + + const auto start_tm = std::chrono::steady_clock::now(); + while (recoder.has_more_data()) { + recoder.recode_next_chunk(chunk_data); + } + + std::cout << "recode time: " << (std::chrono::steady_clock::now() - start_tm).count() << std::endl; +} + +} // namespace performance_tests \ No newline at end of file diff --git a/pp/performance_tests/chunk_recoder_test.h b/pp/performance_tests/chunk_recoder_test.h new file mode 100644 index 0000000000..486a9c50fe --- /dev/null +++ b/pp/performance_tests/chunk_recoder_test.h @@ -0,0 +1,14 @@ +#pragma once + +#include "performance_tests/test_with_input_only.h" + +namespace performance_tests { + +struct ChunkRecoder : TestWithInputOnly { + std::string input_file_base_name() const final { return "dummy_wal"; } + bool has_output() const final { return false; } + std::string output_file_base_name() const final { return ""; } + void execute(const Config& config, Metrics& metrics) const final; +}; + +} // namespace performance_tests diff --git a/pp/performance_tests/performance_tests.cpp b/pp/performance_tests/performance_tests.cpp index 96422fd4e0..81458e5616 100644 --- a/pp/performance_tests/performance_tests.cpp +++ b/pp/performance_tests/performance_tests.cpp @@ -1,3 +1,4 @@ +#include "chunk_recoder_test.h" #include "full_load_lss_test.h" #include "full_save_lss_test.h" #include "load_gorilla_from_wal_and_calculate_hash_over_label_set_names_test.h" @@ -69,6 +70,7 @@ int main([[maybe_unused]] int argc, char* argv[]) { test_db.add(std::make_unique()); test_db.add(std::make_unique()); test_db.add(std::make_unique()); + test_db.add(std::make_unique()); test_db.add(std::make_unique()); test_db.add(std::make_unique()); test_db.add(std::make_unique()); From e9fe596a2342702849c5e6d6932f481b5ed81242 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Mon, 14 Apr 2025 15:19:21 +0300 Subject: [PATCH 13/90] optimized chunk recoder for constant values chunks --- pp/bare_bones/gorilla.h | 14 ++++++++++++++ pp/head/chunk_recoder.h | 26 ++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/pp/bare_bones/gorilla.h b/pp/bare_bones/gorilla.h index 65ee67f584..8830279bfa 100644 --- a/pp/bare_bones/gorilla.h +++ b/pp/bare_bones/gorilla.h @@ -662,6 +662,20 @@ class PROMPP_ATTRIBUTE_PACKED StreamEncoder { state_.values_encoder.encode(v, v_bitseq); } } + + template + PROMPP_ALWAYS_INLINE void encode_constant_value(int64_t ts, BitSequence& ts_bitseq, BitSequence& v_bitseq) noexcept { + if (state_.state == GorillaState::kFirstPoint) [[unlikely]] { + assert(false); + } else if (state_.state == GorillaState::kSecondPoint) [[unlikely]] { + state_.timestamp_encoder.encode_delta(ts, ts_bitseq); + v_bitseq.write_zero_bit(); + state_.state = GorillaState::kOtherPoint; + } else { + state_.timestamp_encoder.encode_delta_of_delta(ts, ts_bitseq); + v_bitseq.write_zero_bit(); + } + } }; static_assert(sizeof(StreamEncoder, ValuesEncoder>) == 29); diff --git a/pp/head/chunk_recoder.h b/pp/head/chunk_recoder.h index 3f77b52bc4..53494351a1 100644 --- a/pp/head/chunk_recoder.h +++ b/pp/head/chunk_recoder.h @@ -119,20 +119,26 @@ class ChunkRecoder { void recode_chunk(ChunkInfoInterface auto& info) { Encoder encoder; - Decoder::create_decode_iterator(*iterator_, [&](auto&& begin, auto&& end) PROMPP_LAMBDA_INLINE { + Decoder::create_decode_iterator(*iterator_, [&](Iterator&& begin, auto&& end) { for (; begin != end; ++begin) { const auto& sample = *begin; - if (sample.timestamp > time_interval_.max) { + if (sample.timestamp > time_interval_.max) [[unlikely]] { return; } - if (sample.timestamp < time_interval_.min) { + if (sample.timestamp < time_interval_.min) [[unlikely]] { continue; } if (encoder.state().state == BareBones::Encoding::Gorilla::GorillaState::kFirstPoint) [[unlikely]] { info.interval.min = sample.timestamp; } - encoder.encode(sample.timestamp, sample.value, stream_, stream_); + + if constexpr (std::is_same_v) { + recode_constant_chunk_sample(sample, encoder); + } else { + recode_chunk_sample(sample, encoder); + } + ++info.samples_count; } }); @@ -143,6 +149,18 @@ class ChunkRecoder { } } + PROMPP_ALWAYS_INLINE void recode_constant_chunk_sample(const series_data::encoder::Sample& sample, Encoder& encoder) noexcept { + if (encoder.state().state == BareBones::Encoding::Gorilla::GorillaState::kFirstPoint) [[unlikely]] { + encoder.encode(sample.timestamp, sample.value, stream_, stream_); + } else { + encoder.encode_constant_value(sample.timestamp, stream_, stream_); + } + } + + PROMPP_ALWAYS_INLINE void recode_chunk_sample(const series_data::encoder::Sample& sample, Encoder& encoder) noexcept { + encoder.encode(sample.timestamp, sample.value, stream_, stream_); + } + void advance_to_non_empty_chunk() noexcept { const auto chunk_is_empty = [this] PROMPP_LAMBDA_INLINE { if (iterator_.chunk_is_empty()) { From 7900f05f2422bc55302f6ab5c76d69d48e8906e4 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Mon, 14 Apr 2025 18:25:51 +0300 Subject: [PATCH 14/90] optimized BStream::write_bits --- pp/prometheus/tsdb/chunkenc/bstream.h | 38 ++++++++++----------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/pp/prometheus/tsdb/chunkenc/bstream.h b/pp/prometheus/tsdb/chunkenc/bstream.h index 26acaba5b9..aa85722ffc 100644 --- a/pp/prometheus/tsdb/chunkenc/bstream.h +++ b/pp/prometheus/tsdb/chunkenc/bstream.h @@ -6,22 +6,20 @@ namespace PromPP::Prometheus::tsdb::chunkenc { template requires std::is_same_v -class BStream : public BareBones::CompactBitSequenceBase { +class BStream : public BareBones::CompactBitSequenceBase { public: - void write_bits(uint64_t u, uint8_t nbits) noexcept { - u <<= kUint64Bits - nbits; - while (nbits >= kByteBits) { - const auto byt = static_cast(u >> (kUint64Bits - kByteBits)); - write_byte(byt); - u <<= kByteBits; - nbits -= kByteBits; - } - - while (nbits > 0) { - write_bit(u >> (kUint64Bits - 1) == 1); - u <<= 1; - --nbits; - } + void write_bits(uint64_t value, uint8_t nbits) noexcept { + reserve_enough_memory_if_needed(nbits); + + auto memory = Base::template unfilled_memory(); + const auto rest = rest_of_bits_in_byte(); + + value <<= kUint64Bits - nbits; + *memory++ |= static_cast(value >> (kUint64Bits - rest)); + value <<= rest; + *reinterpret_cast(memory) |= BareBones::Bit::be(value); + + size_in_bits_ += nbits; } PROMPP_ALWAYS_INLINE void write_byte(uint8_t byt) noexcept { @@ -45,16 +43,8 @@ class BStream : public BareBones::CompactBitSequenceBase; + using Base = BareBones::CompactBitSequenceBase; static constexpr auto kUint64Bits = BareBones::Bit::kUint64Bits; static constexpr auto kByteBits = BareBones::Bit::kByteBits; From 1c23b1b69f3db260c0070bab14f0617d1fbdcff5 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Mon, 14 Apr 2025 19:27:38 +0300 Subject: [PATCH 15/90] refactored ChunkRecoder --- pp/entrypoint/series_data_data_storage.cpp | 3 ++- pp/head/chunk_recoder.h | 20 ++++++++++++-------- pp/head/chunk_recoder_tests.cpp | 6 +++--- pp/performance_tests/chunk_recoder_test.cpp | 8 ++------ 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/pp/entrypoint/series_data_data_storage.cpp b/pp/entrypoint/series_data_data_storage.cpp index 2d15599778..20377a8c17 100644 --- a/pp/entrypoint/series_data_data_storage.cpp +++ b/pp/entrypoint/series_data_data_storage.cpp @@ -104,8 +104,9 @@ extern "C" void prompp_series_data_chunk_recoder_ctor(void* args, void* res) { }; const auto in = static_cast(args); + const auto& ls_id_set = std::get(*in->lss).ls_id_set(); new (res) Result{ - .chunk_recoder = std::make_unique(std::get(*in->lss).ls_id_set(), in->data_storage.get(), in->time_interval), + .chunk_recoder = std::make_unique(ls_id_set.begin(), ls_id_set.end(), in->data_storage.get(), in->time_interval), }; } diff --git a/pp/head/chunk_recoder.h b/pp/head/chunk_recoder.h index 53494351a1..438a390ada 100644 --- a/pp/head/chunk_recoder.h +++ b/pp/head/chunk_recoder.h @@ -16,11 +16,15 @@ concept ChunkInfoInterface = requires(ChunkInfo& info) { { info.samples_count } -> std::same_as; }; -template +template class ChunkRecoder { public: - explicit ChunkRecoder(const LsIdSet& ls_id_id_set, const series_data::DataStorage* data_storage, const PromPP::Primitives::TimeInterval& time_interval) - : iterator_(ls_id_id_set, data_storage), time_interval_{.min = time_interval.min, .max = time_interval.max - 1} { + explicit ChunkRecoder(LsIdSetIterator&& ls_id_set_iterator_begin, + LsIdSetIteratorSentinel&& ls_id_set_iterator_end, + const series_data::DataStorage* data_storage, + const PromPP::Primitives::TimeInterval& time_interval) + : iterator_(std::move(ls_id_set_iterator_begin), std::move(ls_id_set_iterator_end), data_storage), + time_interval_{.min = time_interval.min, .max = time_interval.max - 1} { advance_to_non_empty_chunk(); } @@ -65,9 +69,9 @@ class ChunkRecoder { using LabelSetID = PromPP::Primitives::LabelSetID; - ChunkIterator(const LsIdSet& series_id_set, const series_data::DataStorage* data_storage) - : ls_id_iterator_(series_id_set.begin()), - ls_id_end_iterator_(series_id_set.end()), + ChunkIterator(LsIdSetIterator&& ls_id_iterator_, LsIdSetIteratorSentinel&& ls_id_end_iterator, const series_data::DataStorage* data_storage) + : ls_id_iterator_(std::move(ls_id_iterator_)), + ls_id_end_iterator_(std::move(ls_id_end_iterator)), chunk_iterator_(data_storage, ls_id_iterator_ != ls_id_end_iterator_ ? static_cast(*ls_id_iterator_) : PromPP::Primitives::kInvalidLabelSetID) {} @@ -97,8 +101,8 @@ class ChunkRecoder { } private: - typename LsIdSet::const_iterator ls_id_iterator_; - typename LsIdSet::const_iterator ls_id_end_iterator_; + LsIdSetIterator ls_id_iterator_; + [[no_unique_address]] LsIdSetIteratorSentinel ls_id_end_iterator_; series_data::DataStorage::SeriesChunkIterator chunk_iterator_; }; diff --git a/pp/head/chunk_recoder_tests.cpp b/pp/head/chunk_recoder_tests.cpp index 7a118ec0e7..589866e50e 100644 --- a/pp/head/chunk_recoder_tests.cpp +++ b/pp/head/chunk_recoder_tests.cpp @@ -17,7 +17,7 @@ using std::operator""s; class ChunkRecoderFixture : public ::testing::Test { protected: using LsIdSet = std::vector; - using ChunkRecoder = head::ChunkRecoder; + using ChunkRecoder = head::ChunkRecoder; struct RecodeInfo { TimeInterval interval{.min = 0, .max = 0}; @@ -34,7 +34,7 @@ class ChunkRecoderFixture : public ::testing::Test { ChunkRecoder create_recoder(const LsIdSet& ls_id_set, const TimeInterval& time_interval) { ls_id_set_ = ls_id_set; - return ChunkRecoder{ls_id_set_, &storage_, time_interval}; + return ChunkRecoder{ls_id_set_.begin(), ls_id_set_.end(), &storage_, time_interval}; } static RecodeInfo recode(ChunkRecoder& recoder) noexcept { @@ -48,7 +48,7 @@ class ChunkRecoderFixture : public ::testing::Test { TEST_F(ChunkRecoderFixture, EmptyStorage) { // Arrange - ChunkRecoder recoder({}, &storage_, {}); + ChunkRecoder recoder({}, {}, &storage_, {}); // Act const auto info1 = recode(recoder); diff --git a/pp/performance_tests/chunk_recoder_test.cpp b/pp/performance_tests/chunk_recoder_test.cpp index 7d269eb498..539c5f4877 100644 --- a/pp/performance_tests/chunk_recoder_test.cpp +++ b/pp/performance_tests/chunk_recoder_test.cpp @@ -41,12 +41,8 @@ void ChunkRecoder::execute(const Config& config, [[maybe_unused]] Metrics& metri } } - // const std::ranges::iota_view ls_id_set(0, label_set_bitmap.size() - 1); - std::vector ls_id_set; - ls_id_set.resize(label_set_bitmap.size()); - std::iota(ls_id_set.begin(), ls_id_set.end(), 0); - - head::ChunkRecoder recoder(ls_id_set, &storage, time_interval); + const std::ranges::iota_view ls_id_set(0, label_set_bitmap.size() - 1); + head::ChunkRecoder recoder(ls_id_set.begin(), ls_id_set.end(), &storage, time_interval); struct { TimeInterval interval; From 97cb6bbc10c81b12fee5a38cfac66033d86a7581 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Mon, 14 Apr 2025 19:37:20 +0300 Subject: [PATCH 16/90] removed redundant usings --- pp/performance_tests/chunk_recoder_test.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pp/performance_tests/chunk_recoder_test.cpp b/pp/performance_tests/chunk_recoder_test.cpp index 539c5f4877..f0da49b48d 100644 --- a/pp/performance_tests/chunk_recoder_test.cpp +++ b/pp/performance_tests/chunk_recoder_test.cpp @@ -12,14 +12,7 @@ namespace performance_tests { -using BareBones::Encoding::Gorilla::StreamEncoder; -using BareBones::Encoding::Gorilla::TimestampDecoder; -using BareBones::Encoding::Gorilla::TimestampEncoder; -using BareBones::Encoding::Gorilla::ValuesEncoder; using PromPP::Primitives::TimeInterval; -using series_data::Decoder; -using series_data::encoder::Sample; -using series_data::encoder::SampleList; void ChunkRecoder::execute(const Config& config, [[maybe_unused]] Metrics& metrics) const { DummyWal::Timeseries tmsr; @@ -48,7 +41,6 @@ void ChunkRecoder::execute(const Config& config, [[maybe_unused]] Metrics& metri TimeInterval interval; uint32_t series_id{}; uint8_t samples_count{}; - BareBones::Vector buffer; } chunk_data; const auto start_tm = std::chrono::steady_clock::now(); From ae679f2e83d0fa8c55cb67b76d5589d04847bcc1 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Mon, 14 Apr 2025 19:41:58 +0300 Subject: [PATCH 17/90] fixed chunks size in performance test --- pp/performance_tests/chunk_recoder_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pp/performance_tests/chunk_recoder_test.cpp b/pp/performance_tests/chunk_recoder_test.cpp index f0da49b48d..0f63344950 100644 --- a/pp/performance_tests/chunk_recoder_test.cpp +++ b/pp/performance_tests/chunk_recoder_test.cpp @@ -34,7 +34,7 @@ void ChunkRecoder::execute(const Config& config, [[maybe_unused]] Metrics& metri } } - const std::ranges::iota_view ls_id_set(0, label_set_bitmap.size() - 1); + const std::ranges::iota_view ls_id_set(0, label_set_bitmap.size()); head::ChunkRecoder recoder(ls_id_set.begin(), ls_id_set.end(), &storage, time_interval); struct { From 52d03580000d6a50162352c5ed083d2970fd1ea2 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Mon, 14 Apr 2025 19:57:14 +0300 Subject: [PATCH 18/90] fixed compilation error --- pp/entrypoint/series_data_data_storage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pp/entrypoint/series_data_data_storage.cpp b/pp/entrypoint/series_data_data_storage.cpp index 20377a8c17..9b73a7ea6b 100644 --- a/pp/entrypoint/series_data_data_storage.cpp +++ b/pp/entrypoint/series_data_data_storage.cpp @@ -10,7 +10,7 @@ using entrypoint::head::DataStoragePtr; using entrypoint::head::QueryableEncodingBimap; -using ChunkRecoder = head::ChunkRecoder; +using ChunkRecoder = head::ChunkRecoder; using ChunkRecoderPtr = std::unique_ptr; extern "C" void prompp_series_data_data_storage_ctor(void* res) { From eecdbcc08f915c8e4513c72659242e8800d19627 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 15 Apr 2025 05:52:23 +0000 Subject: [PATCH 19/90] for save --- cmd/prometheus/main.go | 6 +++--- pp/go/cppbridge/entrypoint.go | 6 ++++++ pp/go/relabeler/appender/rotator.go | 8 ++++++++ pp/go/relabeler/appender/storage.go | 1 + tsdb/db.go | 5 ++++- web/federate.go | 8 ++++++++ web/web.go | 2 +- 7 files changed, 31 insertions(+), 5 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 17ef459a79..324f51aece 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -707,9 +707,9 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - BlockDuration: 10 * time.Minute, // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), - Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), + BlockDuration: 15 * time.Minute, + Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), }, headCatalog, reloadBlocksTriggerNotifier, @@ -718,7 +718,7 @@ func main() { time.Duration(cfg.tsdb.RetentionDuration), time.Duration(cfg.HeadRetentionTimeout), // x3 ScrapeInterval timeout for write block - time.Duration(cfgFile.GlobalConfig.ScrapeInterval*3), + time.Duration(cfgFile.GlobalConfig.ScrapeInterval*6), ) if err != nil { level.Error(logger).Log("msg", "failed to create a receiver", "err", err) diff --git a/pp/go/cppbridge/entrypoint.go b/pp/go/cppbridge/entrypoint.go index fc3909159b..f94a1adb73 100644 --- a/pp/go/cppbridge/entrypoint.go +++ b/pp/go/cppbridge/entrypoint.go @@ -1662,12 +1662,18 @@ func seriesDataDecodeIteratorSample(decodeIterator uintptr) (int64, float64) { value float64 } + // start := time.Now() + fastcgo.UnsafeCall2( C.prompp_series_data_decode_iterator_sample, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) + // unsafeCall.With( + // prometheus.Labels{"object": "head_data_storage_decode_iterator", "method": "sample"}, + // ).Observe(float64(time.Since(start).Nanoseconds())) + return res.timestamp, res.value } diff --git a/pp/go/relabeler/appender/rotator.go b/pp/go/relabeler/appender/rotator.go index 6c7dc307ff..9ce408729d 100644 --- a/pp/go/relabeler/appender/rotator.go +++ b/pp/go/relabeler/appender/rotator.go @@ -39,6 +39,7 @@ type RotateCommiter struct { run chan struct{} closer *util.Closer rotateCounter prometheus.Counter + rotateTimestamp prometheus.Gauge } // NewRotateCommiter - Rotator constructor. @@ -63,6 +64,12 @@ func NewRotateCommiter( Help: "Total counter of rotate rotatable object.", }, ), + rotateTimestamp: factory.NewGauge( + prometheus.GaugeOpts{ + Name: "prompp_rotator_rotate_timestamp", + Help: "Timestamp in seconds of rotate rotatable object.", + }, + ), } go r.loop() @@ -108,6 +115,7 @@ func (r *RotateCommiter) loop() { logger.Errorf("rotation failed: %v", err) } r.rotateCounter.Inc() + r.rotateTimestamp.Set(float64(time.Now().UnixMilli())) r.rotateTimer.Reset() r.commitTimer.Reset() diff --git a/pp/go/relabeler/appender/storage.go b/pp/go/relabeler/appender/storage.go index a98543ead9..e222ebafa7 100644 --- a/pp/go/relabeler/appender/storage.go +++ b/pp/go/relabeler/appender/storage.go @@ -143,6 +143,7 @@ func (qs *QueryableStorage) write() bool { shouldNotify := false persisted := make([]string, 0, lenHeads) for _, head := range heads { + logger.Infof("QUERYABLE STORAGE: head %s start write", head.String()) start := qs.clock.Now() if qs.headIsOutdated(head) { persisted = append(persisted, head.ID()) diff --git a/tsdb/db.go b/tsdb/db.go index bc74f8e9d7..e36680d790 100644 --- a/tsdb/db.go +++ b/tsdb/db.go @@ -1049,7 +1049,8 @@ func (db *DB) run(ctx context.Context) { default: } // PP_CHANGES.md: rebuild on cpp end - case <-time.After(1 * time.Minute): + case <-time.After(5 * time.Minute): + level.Info(db.logger).Log("msg", "reloadBlocks start") db.cmtx.Lock() if err := db.reloadBlocks(); err != nil { level.Error(db.logger).Log("msg", "reloadBlocks", "err", err) @@ -1188,6 +1189,8 @@ func (a dbAppender) Commit() error { // Old blocks are only deleted on reloadBlocks based on the new block's parent information. // See DB.reloadBlocks documentation for further information. func (db *DB) Compact(ctx context.Context) (returnErr error) { + time.Sleep(2 * time.Minute) + level.Info(db.logger).Log("msg", "Compact start") db.cmtx.Lock() defer db.cmtx.Unlock() defer func() { diff --git a/web/federate.go b/web/federate.go index 470255a00d..9a68c98a99 100644 --- a/web/federate.go +++ b/web/federate.go @@ -320,3 +320,11 @@ Loop: } } } + +func (h *Handler) federationMock(w http.ResponseWriter, req *http.Request) { + h.mtx.RLock() + defer h.mtx.RUnlock() + + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) +} diff --git a/web/web.go b/web/web.go index a2b4ed62bc..5e6a2ed3e9 100644 --- a/web/web.go +++ b/web/web.go @@ -389,7 +389,7 @@ func New(logger log.Logger, o *Options, receiver handler.Receiver) *Handler { // router.Get("/metrics", promhttp.Handler().ServeHTTP) router.Get("/federate", readyf(httputil.CompressionHandler{ - Handler: http.HandlerFunc(h.federation), + Handler: http.HandlerFunc(h.federationMock), }.ServeHTTP)) router.Get("/consoles/*filepath", readyf(h.consoles)) From a9b91c8affd1350c8ae01f3192fbc247a0c6b1a7 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Tue, 15 Apr 2025 10:02:03 +0300 Subject: [PATCH 20/90] created FixedSizeBStream and used it in ChunkRecoder --- pp/head/chunk_recoder.h | 10 ++-- pp/prometheus/tsdb/chunkenc/bstream.h | 75 ++++++++++++++++++++------- pp/prometheus/tsdb/chunkenc/xor.h | 7 ++- 3 files changed, 70 insertions(+), 22 deletions(-) diff --git a/pp/head/chunk_recoder.h b/pp/head/chunk_recoder.h index 438a390ada..4d2bf05f7a 100644 --- a/pp/head/chunk_recoder.h +++ b/pp/head/chunk_recoder.h @@ -4,6 +4,7 @@ #include "primitives/primitives.h" #include "prometheus/tsdb/chunkenc/bstream.h" #include "prometheus/tsdb/chunkenc/xor.h" +#include "series_data/concepts.h" #include "series_data/decoder.h" #include "series_data/encoder/bit_sequence.h" @@ -54,8 +55,9 @@ class ChunkRecoder { private: using Sample = series_data::encoder::Sample; using Decoder = series_data::Decoder; - using Encoder = - BareBones::Encoding::Gorilla::StreamEncoder; + using TimestampEncoder = PromPP::Prometheus::tsdb::chunkenc::TimestampEncoder; + using ValuesEncoder = PromPP::Prometheus::tsdb::chunkenc::ValuesEncoder; + using Encoder = BareBones::Encoding::Gorilla::StreamEncoder; class IteratorSentinel {}; @@ -106,8 +108,10 @@ class ChunkRecoder { series_data::DataStorage::SeriesChunkIterator chunk_iterator_; }; + static constexpr auto kStreamSize = series_data::kSamplesPerChunkDefault * (TimestampEncoder::kMaxItemSizeInBits + ValuesEncoder::kMaxItemSizeInBits); + ChunkIterator iterator_; - PromPP::Prometheus::tsdb::chunkenc::BStream stream_; + PromPP::Prometheus::tsdb::chunkenc::FixedSizeBStream stream_{kStreamSize}; const PromPP::Primitives::TimeInterval time_interval_; PROMPP_ALWAYS_INLINE static void reset_info(ChunkInfoInterface auto& info) noexcept { diff --git a/pp/prometheus/tsdb/chunkenc/bstream.h b/pp/prometheus/tsdb/chunkenc/bstream.h index aa85722ffc..ca7bafb559 100644 --- a/pp/prometheus/tsdb/chunkenc/bstream.h +++ b/pp/prometheus/tsdb/chunkenc/bstream.h @@ -4,32 +4,36 @@ namespace PromPP::Prometheus::tsdb::chunkenc { +PROMPP_ALWAYS_INLINE void write_bits(uint8_t* memory, uint64_t value, uint8_t nbits, uint8_t rest_of_bits_in_byte) noexcept { + value <<= BareBones::Bit::kUint64Bits - nbits; + *memory++ |= static_cast(value >> (BareBones::Bit::kUint64Bits - rest_of_bits_in_byte)); + value <<= rest_of_bits_in_byte; + *reinterpret_cast(memory) |= BareBones::Bit::be(value); +} + +PROMPP_ALWAYS_INLINE void write_byte(uint8_t* memory, uint8_t byt, uint8_t unfilled_bits_in_byte, uint8_t rest_of_bits_in_byte) noexcept { + *memory++ |= byt >> unfilled_bits_in_byte; + *memory |= byt << rest_of_bits_in_byte; +} + +PROMPP_ALWAYS_INLINE void write_single_bit(uint8_t* memory, uint8_t rest_of_bits_in_byte) noexcept { + *memory |= 0b1u << (rest_of_bits_in_byte - 1); +} + template requires std::is_same_v class BStream : public BareBones::CompactBitSequenceBase { public: void write_bits(uint64_t value, uint8_t nbits) noexcept { reserve_enough_memory_if_needed(nbits); - - auto memory = Base::template unfilled_memory(); - const auto rest = rest_of_bits_in_byte(); - - value <<= kUint64Bits - nbits; - *memory++ |= static_cast(value >> (kUint64Bits - rest)); - value <<= rest; - *reinterpret_cast(memory) |= BareBones::Bit::be(value); - + chunkenc::write_bits(Base::template unfilled_memory(), value, nbits, rest_of_bits_in_byte()); size_in_bits_ += nbits; } PROMPP_ALWAYS_INLINE void write_byte(uint8_t byt) noexcept { reserve_enough_memory_if_needed(); - - auto memory = Base::template unfilled_memory(); - *memory++ |= byt >> unfilled_bits_in_byte(); - *memory |= byt << rest_of_bits_in_byte(); - - size_in_bits_ += kByteBits; + chunkenc::write_byte(Base::template unfilled_memory(), byt, unfilled_bits_in_byte(), rest_of_bits_in_byte()); + size_in_bits_ += BareBones::Bit::kByteBits; } PROMPP_ALWAYS_INLINE void write_zero_bit() noexcept { @@ -39,14 +43,49 @@ class BStream : public BareBones::CompactBitSequenceBase() |= 0b1u << (rest_of_bits_in_byte() - 1); + chunkenc::write_single_bit(Base::template unfilled_memory(), rest_of_bits_in_byte()); + ++size_in_bits_; + } + + private: + using Base = BareBones::CompactBitSequenceBase; + + static constexpr auto kByteBits = BareBones::Bit::kByteBits; + + using Base::reserve_enough_memory_if_needed; + using Base::size_in_bits_; + using Base::unfilled_bits_in_byte; + using Base::unfilled_memory; + + [[nodiscard]] PROMPP_ALWAYS_INLINE uint8_t rest_of_bits_in_byte() const noexcept { return kByteBits - unfilled_bits_in_byte(); } +}; + +template + requires std::is_same_v +class FixedSizeBStream : public BareBones::CompactBitSequenceBase { + public: + explicit FixedSizeBStream(uint32_t bits) { reserve_enough_memory_if_needed(bits); } + + void write_bits(uint64_t value, uint8_t nbits) noexcept { + chunkenc::write_bits(Base::template unfilled_memory(), value, nbits, rest_of_bits_in_byte()); + size_in_bits_ += nbits; + } + + PROMPP_ALWAYS_INLINE void write_byte(uint8_t byt) noexcept { + chunkenc::write_byte(Base::template unfilled_memory(), byt, unfilled_bits_in_byte(), rest_of_bits_in_byte()); + size_in_bits_ += BareBones::Bit::kByteBits; + } + + PROMPP_ALWAYS_INLINE void write_zero_bit() noexcept { ++size_in_bits_; } + + PROMPP_ALWAYS_INLINE void write_single_bit() noexcept { + chunkenc::write_single_bit(Base::template unfilled_memory(), rest_of_bits_in_byte()); ++size_in_bits_; } private: using Base = BareBones::CompactBitSequenceBase; - static constexpr auto kUint64Bits = BareBones::Bit::kUint64Bits; static constexpr auto kByteBits = BareBones::Bit::kByteBits; using Base::reserve_enough_memory_if_needed; @@ -54,7 +93,7 @@ class BStream : public BareBones::CompactBitSequenceBase state{}; [[nodiscard]] PROMPP_ALWAYS_INLINE int64_t timestamp() const noexcept { return state.last_ts; } @@ -73,6 +75,9 @@ class PROMPP_ATTRIBUTE_PACKED TimestampEncoder { class PROMPP_ATTRIBUTE_PACKED ValuesEncoder { public: + static constexpr uint8_t kXorLengthMaskBits = 1 + 1 + 5 + 6; + static constexpr uint8_t kMaxItemSizeInBits = kXorLengthMaskBits + BareBones::Bit::kUint64Bits; + [[nodiscard]] PROMPP_ALWAYS_INLINE double value() const noexcept { return state_.last_v; } [[nodiscard]] PROMPP_ALWAYS_INLINE const BareBones::Encoding::Gorilla::ValuesEncoderState& state() const noexcept { return state_; } @@ -141,7 +146,7 @@ class PROMPP_ATTRIBUTE_PACKED ValuesEncoder { state_.last_v_xor_trailing_z = v_xor_trailing_z; assert(state_.last_v_xor_length + state_.last_v_xor_trailing_z <= BareBones::Bit::to_bits(sizeof(uint64_t))); - stream.write_bits((0b11 << (5 + 6)) | (v_xor_leading_z << 6) | v_xor_length, 1 + 1 + 5 + 6); + stream.write_bits((0b11 << (5 + 6)) | (v_xor_leading_z << 6) | v_xor_length, kXorLengthMaskBits); stream.write_bits(v_xor >> state_.last_v_xor_trailing_z, state_.last_v_xor_length); } From 4ea54d6e95fa62e524cbf00c6fbc524292c8575f Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 15 Apr 2025 07:15:39 +0000 Subject: [PATCH 21/90] for save --- cmd/prometheus/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 324f51aece..2616dbbaec 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -707,9 +707,9 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), BlockDuration: 15 * time.Minute, - Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), + // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), + Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), }, headCatalog, reloadBlocksTriggerNotifier, From ae37e8e7caa4de3c2826a9aa30032e67f68660d7 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 15 Apr 2025 08:49:21 +0000 Subject: [PATCH 22/90] for save --- cmd/prometheus/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 2616dbbaec..0338d09d24 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -708,8 +708,8 @@ func main() { localStoragePath, receiver.RotationInfo{ BlockDuration: 15 * time.Minute, + Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), - Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), }, headCatalog, reloadBlocksTriggerNotifier, From f4cd2d6dbdf310865a5990916e40b4b42a301ce8 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 15 Apr 2025 11:12:23 +0000 Subject: [PATCH 23/90] for save --- cmd/prometheus/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 0338d09d24..7e1c1ccce4 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -707,9 +707,9 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - BlockDuration: 15 * time.Minute, + // BlockDuration: 15 * time.Minute, Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), - // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), + BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), }, headCatalog, reloadBlocksTriggerNotifier, From bb7d8cb82bda046dc0e29a268b8be0a37e44aa87 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 15 Apr 2025 13:58:56 +0000 Subject: [PATCH 24/90] for save --- cmd/prometheus/main.go | 4 ++-- pp/go/cppbridge/entrypoint.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 7e1c1ccce4..0338d09d24 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -707,9 +707,9 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - // BlockDuration: 15 * time.Minute, + BlockDuration: 15 * time.Minute, Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), - BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), + // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), }, headCatalog, reloadBlocksTriggerNotifier, diff --git a/pp/go/cppbridge/entrypoint.go b/pp/go/cppbridge/entrypoint.go index f94a1adb73..44dd021d4d 100644 --- a/pp/go/cppbridge/entrypoint.go +++ b/pp/go/cppbridge/entrypoint.go @@ -1976,7 +1976,7 @@ func walPrometheusScraperHashdexParse(hashdex uintptr, buffer []byte, default_ti uintptr(unsafe.Pointer(&res)), ) unsafeCall.With( - prometheus.Labels{"object": "hashdex", "method": "parse"}, + prometheus.Labels{"object": "prometheus_hashdex", "method": "parse"}, ).Observe(float64(time.Since(start).Nanoseconds())) return res.scraped, res.error @@ -2033,7 +2033,7 @@ func walOpenMetricsScraperHashdexParse(hashdex uintptr, buffer []byte, default_t uintptr(unsafe.Pointer(&res)), ) unsafeCall.With( - prometheus.Labels{"object": "hashdex", "method": "parse"}, + prometheus.Labels{"object": "open_metrics_hashdex", "method": "parse"}, ).Observe(float64(time.Since(start).Nanoseconds())) return res.scraped, res.error From 192fe3e0e0f4bfea68e4418c33ed1212f2d6dd61 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Wed, 16 Apr 2025 07:08:10 +0000 Subject: [PATCH 25/90] for save --- cmd/prometheus/main.go | 4 ++-- pp/entrypoint/common.cpp | 2 ++ pp/entrypoint/common.h | 2 ++ pp/go/cppbridge/c_garbage_collector.go | 6 ++++-- pp/go/cppbridge/entrypoint.go | 17 +++++++++++++++++ pp/go/cppbridge/entrypoint.h | 2 ++ pp/go/relabeler/querier/metrics.go | 22 ++++++++++++++++------ pp/go/relabeler/querier/querier.go | 4 ++-- 8 files changed, 47 insertions(+), 12 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 0338d09d24..7e1c1ccce4 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -707,9 +707,9 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - BlockDuration: 15 * time.Minute, + // BlockDuration: 15 * time.Minute, Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), - // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), + BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), }, headCatalog, reloadBlocksTriggerNotifier, diff --git a/pp/entrypoint/common.cpp b/pp/entrypoint/common.cpp index cbe4392f97..dd68c9ea7a 100644 --- a/pp/entrypoint/common.cpp +++ b/pp/entrypoint/common.cpp @@ -69,3 +69,5 @@ extern "C" void prompp_dump_memory_profile([[maybe_unused]] void* args, void* re out->error = ENODATA; #endif } + +extern "C" void unsafe_call_2(void* args [[maybe_unused]], void* res [[maybe_unused]]) {} diff --git a/pp/entrypoint/common.h b/pp/entrypoint/common.h index 6ae64723c6..3a95295c05 100644 --- a/pp/entrypoint/common.h +++ b/pp/entrypoint/common.h @@ -30,6 +30,8 @@ void prompp_mem_info(void* res); */ void prompp_dump_memory_profile(void* args, void* res); +void unsafe_call_2(void* args, void* res); + #ifdef __cplusplus } #endif diff --git a/pp/go/cppbridge/c_garbage_collector.go b/pp/go/cppbridge/c_garbage_collector.go index bd92f09afe..06984bcf92 100644 --- a/pp/go/cppbridge/c_garbage_collector.go +++ b/pp/go/cppbridge/c_garbage_collector.go @@ -5,8 +5,8 @@ import ( "runtime" "time" - "github.com/prometheus/prometheus/pp/go/util" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/prometheus/pp/go/util" ) // garbage collector for objects initiated in GO but filled in C/C++, @@ -15,7 +15,7 @@ import ( const ( defaultGCDecay float64 = 1.0 / 3.0 defaultGCWarmupValue float64 = 0 - gcDelayThreshold time.Duration = 2 * time.Second + gcDelayThreshold time.Duration = 5 * time.Second ) // CGOGC - implement wise garbage collector for c/c++ objects. @@ -133,6 +133,8 @@ func (cgc *CGOGC) run() { func (cgc *CGOGC) gc() { memInfo := GetMemInfo() + unsafeCall2() + if !cgc.isOverThreshold(memInfo) { return } diff --git a/pp/go/cppbridge/entrypoint.go b/pp/go/cppbridge/entrypoint.go index 44dd021d4d..468d30cdbf 100644 --- a/pp/go/cppbridge/entrypoint.go +++ b/pp/go/cppbridge/entrypoint.go @@ -87,6 +87,23 @@ func dumpMemoryProfile(filename string) int { return res.error } +func unsafeCall2() { + var args struct{} + var res struct{} + + start := time.Now() + + fastcgo.UnsafeCall2( + C.unsafe_call_2, + uintptr(unsafe.Pointer(&args)), + uintptr(unsafe.Pointer(&res)), + ) + + unsafeCall.With( + prometheus.Labels{"object": "unsafe", "method": "call_2"}, + ).Observe(float64(time.Since(start).Nanoseconds())) +} + // // ProtobufHashdex // diff --git a/pp/go/cppbridge/entrypoint.h b/pp/go/cppbridge/entrypoint.h index 08b0c9ec5d..9ef61e02f7 100755 --- a/pp/go/cppbridge/entrypoint.h +++ b/pp/go/cppbridge/entrypoint.h @@ -30,6 +30,8 @@ void prompp_mem_info(void* res); */ void prompp_dump_memory_profile(void* args, void* res); +void unsafe_call_2(void* args, void* res); + #ifdef __cplusplus } #endif diff --git a/pp/go/relabeler/querier/metrics.go b/pp/go/relabeler/querier/metrics.go index 3759e27079..83cb07f987 100644 --- a/pp/go/relabeler/querier/metrics.go +++ b/pp/go/relabeler/querier/metrics.go @@ -17,17 +17,27 @@ func NewMetrics(registerer prometheus.Registerer) *Metrics { return &Metrics{ LabelNamesDuration: factory.NewHistogramVec( prometheus.HistogramOpts{ - Name: "prompp_head_query_label_names_duration", - Help: "Label names query from head duration in milliseconds", - Buckets: []float64{1, 5, 10, 20, 50, 100}, + Name: "prompp_head_query_label_names_duration", + Help: "Label names query from head duration in microseconds", + Buckets: []float64{ + 50, 100, 250, 500, 750, + 1000, 2500, 5000, 7500, + 10000, 25000, 50000, 75000, + 100000, 500000, + }, }, []string{"generation"}, ), LabelValuesDuration: factory.NewHistogramVec( prometheus.HistogramOpts{ - Name: "prompp_head_query_label_values_duration", - Help: "Label values query from head duration in milliseconds", - Buckets: []float64{1, 5, 10, 20, 50, 100}, + Name: "prompp_head_query_label_values_duration", + Help: "Label values query from head duration in microseconds", + Buckets: []float64{ + 50, 100, 250, 500, 750, + 1000, 2500, 5000, 7500, + 10000, 25000, 50000, 75000, + 100000, 500000, + }, }, []string{"generation"}, ), diff --git a/pp/go/relabeler/querier/querier.go b/pp/go/relabeler/querier/querier.go index eff9e8a9c4..b3988402d6 100644 --- a/pp/go/relabeler/querier/querier.go +++ b/pp/go/relabeler/querier/querier.go @@ -54,7 +54,7 @@ func (q *Querier) LabelValues(ctx context.Context, name string, matchers ...*lab if q.metrics != nil { q.metrics.LabelValuesDuration.With( prometheus.Labels{"generation": fmt.Sprintf("%d", q.head.Generation())}, - ).Observe(float64(time.Since(start).Milliseconds())) + ).Observe(float64(time.Since(start).Microseconds())) } }() @@ -95,7 +95,7 @@ func (q *Querier) LabelNames(ctx context.Context, matchers ...*labels.Matcher) ( if q.metrics != nil { q.metrics.LabelNamesDuration.With( prometheus.Labels{"generation": fmt.Sprintf("%d", q.head.Generation())}, - ).Observe(float64(time.Since(start).Milliseconds())) + ).Observe(float64(time.Since(start).Microseconds())) } }() From eaafa5965168acb722d9c18746898102b7f802e7 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Wed, 16 Apr 2025 14:44:14 +0000 Subject: [PATCH 26/90] for save --- cmd/prometheus/main.go | 4 +- pp/go/cppbridge/entrypoint.go | 358 +++++++++++++++++++++++++--------- 2 files changed, 271 insertions(+), 91 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 7e1c1ccce4..0338d09d24 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -707,9 +707,9 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - // BlockDuration: 15 * time.Minute, + BlockDuration: 15 * time.Minute, Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), - BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), + // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), }, headCatalog, reloadBlocksTriggerNotifier, diff --git a/pp/go/cppbridge/entrypoint.go b/pp/go/cppbridge/entrypoint.go index 468d30cdbf..0c84bbb0a9 100644 --- a/pp/go/cppbridge/entrypoint.go +++ b/pp/go/cppbridge/entrypoint.go @@ -22,22 +22,235 @@ import ( "unsafe" //nolint:gocritic // because otherwise it won't work "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/prometheus/pp/go/cppbridge/fastcgo" "github.com/prometheus/prometheus/pp/go/model" + "github.com/prometheus/prometheus/pp/go/util" ) -var unsafeCall = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "prompp_cppbridge_unsafecall_nanoseconds", - Help: "The time duration cpp call.", - Buckets: []float64{ - 100, 500, - 1000, 5000, 10000, 50000, 100000, 500000, - 1000000, 5000000, 10000000, 50000000, 100000000, 500000000, +var ( + // UnsafeCall2 + unsafeCall2Sum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "unsafe", "method": "call_2"}, }, - }, - []string{"object", "method"}, + ) + unsafeCall2Count = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "unsafe", "method": "call_2"}, + }, + ) + + // input_relabeler input_relabeling + inputRelabelerInputRelabelingSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "input_relabeler", "method": "input_relabeling"}, + }, + ) + inputRelabelerInputRelabelingCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "input_relabeler", "method": "input_relabeling"}, + }, + ) + + // input_relabeler relabeling_with_stalenans + inputRelabelerRelabelingWithStalenansSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "input_relabeler", "method": "relabeling_with_stalenans"}, + }, + ) + inputRelabelerRelabelingWithStalenansCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "input_relabeler", "method": "relabeling_with_stalenans"}, + }, + ) + + // input_relabeler append_relabeler_series + inputRelabelerAppendRelabelerSeriesSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "input_relabeler", "method": "append_relabeler_series"}, + }, + ) + inputRelabelerAppendRelabelerSeriesCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "input_relabeler", "method": "append_relabeler_series"}, + }, + ) + + // input_relabeler update_relabeler_state + inputRelabelerUpdateRelabelerStateSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "input_relabeler", "method": "update_relabeler_state"}, + }, + ) + inputRelabelerUpdateRelabelerStateCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "input_relabeler", "method": "update_relabeler_state"}, + }, + ) + + // head_data_storage allocated_memory + headDataStorageAllocatedMemorySum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_data_storage", "method": "allocated_memory"}, + }, + ) + headDataStorageAllocatedMemoryCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_data_storage", "method": "allocated_memory"}, + }, + ) + + // head_data_storage query + headDataStorageQuerySum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_data_storage", "method": "query"}, + }, + ) + headDataStorageQueryCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_data_storage", "method": "query"}, + }, + ) + + // head_data_storage encode_inner_series_slice + headDataStorageEncodeInnerSeriesSliceSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_data_storage", "method": "encode_inner_series_slice"}, + }, + ) + headDataStorageEncodeInnerSeriesSliceCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_data_storage", "method": "encode_inner_series_slice"}, + }, + ) + + // head_data_storage merge_out_of_order_chunks + headDataStorageMergeOutOfOrderChunksSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_data_storage", "method": "merge_out_of_order_chunks"}, + }, + ) + headDataStorageMergeOutOfOrderChunksCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_data_storage", "method": "merge_out_of_order_chunks"}, + }, + ) + + // prometheus_hashdex parse + prometheusHashdexParseSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "prometheus_hashdex", "method": "parse"}, + }, + ) + prometheusHashdexParseCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "prometheus_hashdex", "method": "parse"}, + }, + ) + + // open_metrics_hashdex parse + openMetricsHashdexParseSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "open_metrics_hashdex", "method": "parse"}, + }, + ) + openMetricsHashdexParseCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "open_metrics_hashdex", "method": "parse"}, + }, + ) + + // head_wal_encoder add_inner_series + headWalEncoderAddInnerSeriesSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_wal_encoder", "method": "add_inner_series"}, + }, + ) + headWalEncoderAddInnerSeriesCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_wal_encoder", "method": "add_inner_series"}, + }, + ) + + // head_wal_encoder finalize + headWalEncoderFinalizeSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_wal_encoder", "method": "finalize"}, + }, + ) + headWalEncoderFinalizeCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "head_wal_encoder", "method": "finalize"}, + }, + ) + + // chunk_recoder recode_next_chunk + chunkRecoderRecodeNextChunkSum = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_sum", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "chunk_recoder", "method": "recode_next_chunk"}, + }, + ) + chunkRecoderRecodeNextChunkCount = util.NewUnconflictRegisterer(prometheus.DefaultRegisterer).NewCounter( + prometheus.CounterOpts{ + Name: "prompp_cppbridge_unsafecall_nanoseconds_count", + Help: "The time duration cpp call.", + ConstLabels: prometheus.Labels{"object": "chunk_recoder", "method": "recode_next_chunk"}, + }, + ) ) func freeBytes(b []byte) { @@ -91,7 +304,7 @@ func unsafeCall2() { var args struct{} var res struct{} - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.unsafe_call_2, @@ -99,9 +312,8 @@ func unsafeCall2() { uintptr(unsafe.Pointer(&res)), ) - unsafeCall.With( - prometheus.Labels{"object": "unsafe", "method": "call_2"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + unsafeCall2Sum.Add(float64(time.Now().UnixNano() - start)) + unsafeCall2Count.Inc() } // @@ -1278,15 +1490,14 @@ func prometheusPerShardRelabelerInputRelabeling( seriesAdded uint32 exception []byte } - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_prometheus_per_shard_relabeler_input_relabeling, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - unsafeCall.With( - prometheus.Labels{"object": "input_relabeler", "method": "input_relabeling"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + inputRelabelerInputRelabelingSum.Add(float64(time.Now().UnixNano() - start)) + inputRelabelerInputRelabelingCount.Inc() return res.samplesAdded, res.seriesAdded, res.exception } @@ -1328,15 +1539,14 @@ func prometheusPerShardRelabelerInputRelabelingWithStalenans( seriesAdded uint32 exception []byte } - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_prometheus_per_shard_relabeler_input_relabeling_with_stalenans, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - unsafeCall.With( - prometheus.Labels{"object": "input_relabeler", "method": "relabeling_with_stalenans"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + inputRelabelerRelabelingWithStalenansSum.Add(float64(time.Now().UnixNano() - start)) + inputRelabelerRelabelingWithStalenansCount.Inc() return res.samplesAdded, res.seriesAdded, res.exception } @@ -1359,15 +1569,14 @@ func prometheusPerShardRelabelerAppendRelabelerSeries( var res struct { exception []byte } - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_prometheus_per_shard_relabeler_append_relabeler_series, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - unsafeCall.With( - prometheus.Labels{"object": "input_relabeler", "method": "append_relabeler_series"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + inputRelabelerAppendRelabelerSeriesSum.Add(float64(time.Now().UnixNano() - start)) + inputRelabelerAppendRelabelerSeriesCount.Inc() return res.exception } @@ -1387,15 +1596,14 @@ func prometheusPerShardRelabelerUpdateRelabelerState( var res struct { exception []byte } - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_prometheus_per_shard_relabeler_update_relabeler_state, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - unsafeCall.With( - prometheus.Labels{"object": "input_relabeler", "method": "update_relabeler_state"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + inputRelabelerUpdateRelabelerStateSum.Add(float64(time.Now().UnixNano() - start)) + inputRelabelerUpdateRelabelerStateCount.Inc() return res.exception } @@ -1438,14 +1646,11 @@ func prometheusPerShardRelabelerResetTo( perShardRelabeler uintptr numberOfShards uint16 }{externalLabels, perShardRelabeler, numberOfShards} - start := time.Now() + fastcgo.UnsafeCall1( C.prompp_prometheus_per_shard_relabeler_reset_to, uintptr(unsafe.Pointer(&args)), ) - unsafeCall.With( - prometheus.Labels{"object": "input_relabeler", "method": "reset_to"}, - ).Observe(float64(time.Since(start).Nanoseconds())) } func seriesDataDataStorageCtor() uintptr { @@ -1465,14 +1670,11 @@ func seriesDataDataStorageReset(dataStorage uintptr) { args := struct { dataStorage uintptr }{dataStorage} - start := time.Now() + fastcgo.UnsafeCall1( C.prompp_series_data_data_storage_reset, uintptr(unsafe.Pointer(&args)), ) - unsafeCall.With( - prometheus.Labels{"object": "head_data_storage", "method": "reset"}, - ).Observe(float64(time.Since(start).Nanoseconds())) } func seriesDataDataStorageAllocatedMemory(dataStorage uintptr) uint64 { @@ -1482,15 +1684,14 @@ func seriesDataDataStorageAllocatedMemory(dataStorage uintptr) uint64 { var res struct { allocatedMemory uint64 } - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_series_data_data_storage_allocated_memory, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - unsafeCall.With( - prometheus.Labels{"object": "head_data_storage", "method": "allocated_memory"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + headDataStorageAllocatedMemorySum.Add(float64(time.Now().UnixNano() - start)) + headDataStorageAllocatedMemoryCount.Inc() return res.allocatedMemory } @@ -1503,15 +1704,14 @@ func seriesDataDataStorageQuery(dataStorage uintptr, query HeadDataStorageQuery) var res struct { serializedChunks []byte } - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_series_data_data_storage_query, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - unsafeCall.With( - prometheus.Labels{"object": "head_data_storage", "method": "query"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + headDataStorageQuerySum.Add(float64(time.Now().UnixNano() - start)) + headDataStorageQueryCount.Inc() return res.serializedChunks } @@ -1568,14 +1768,11 @@ func seriesDataEncoderEncode(encoder uintptr, seriesID uint32, timestamp int64, timestamp int64 value float64 }{encoder, seriesID, timestamp, value} - start := time.Now() + fastcgo.UnsafeCall1( C.prompp_series_data_encoder_encode, uintptr(unsafe.Pointer(&args)), ) - unsafeCall.With( - prometheus.Labels{"object": "head_data_storage", "method": "encode"}, - ).Observe(float64(time.Since(start).Nanoseconds())) } func seriesDataEncoderEncodeInnerSeriesSlice(encoder uintptr, innerSeriesSlice []*InnerSeries) { @@ -1583,28 +1780,26 @@ func seriesDataEncoderEncodeInnerSeriesSlice(encoder uintptr, innerSeriesSlice [ encoder uintptr innerSeriesSlice []*InnerSeries }{encoder, innerSeriesSlice} - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall1( C.prompp_series_data_encoder_encode_inner_series_slice, uintptr(unsafe.Pointer(&args)), ) - unsafeCall.With( - prometheus.Labels{"object": "head_data_storage", "method": "encode_inner_series_slice"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + headDataStorageEncodeInnerSeriesSliceSum.Add(float64(time.Now().UnixNano() - start)) + headDataStorageEncodeInnerSeriesSliceCount.Inc() } func seriesDataEncoderMergeOutOfOrderChunks(encoder uintptr) { args := struct { encoder uintptr }{encoder} - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall1( C.prompp_series_data_encoder_merge_out_of_order_chunks, uintptr(unsafe.Pointer(&args)), ) - unsafeCall.With( - prometheus.Labels{"object": "head_data_storage", "method": "merge_out_of_order_chunks"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + headDataStorageMergeOutOfOrderChunksSum.Add(float64(time.Now().UnixNano() - start)) + headDataStorageMergeOutOfOrderChunksCount.Inc() } func seriesDataEncoderDtor(encoder uintptr) { @@ -1679,18 +1874,12 @@ func seriesDataDecodeIteratorSample(decodeIterator uintptr) (int64, float64) { value float64 } - // start := time.Now() - fastcgo.UnsafeCall2( C.prompp_series_data_decode_iterator_sample, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - // unsafeCall.With( - // prometheus.Labels{"object": "head_data_storage_decode_iterator", "method": "sample"}, - // ).Observe(float64(time.Since(start).Nanoseconds())) - return res.timestamp, res.value } @@ -1739,15 +1928,14 @@ func seriesDataChunkRecoderRecodeNextChunk(chunkRecoder uintptr, recodedChunk *R args := struct { chunkRecoder uintptr }{chunkRecoder} - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_series_data_chunk_recoder_recode_next_chunk, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(recodedChunk)), ) - unsafeCall.With( - prometheus.Labels{"object": "chunk_recoder", "method": "recode_next_chunk"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + chunkRecoderRecodeNextChunkSum.Add(float64(time.Now().UnixNano() - start)) + chunkRecoderRecodeNextChunkCount.Inc() } func seriesDataChunkRecoderDtor(chunkRecoder uintptr) { @@ -1986,15 +2174,14 @@ func walPrometheusScraperHashdexParse(hashdex uintptr, buffer []byte, default_ti error uint32 scraped uint32 } - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_wal_prometheus_scraper_hashdex_parse, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - unsafeCall.With( - prometheus.Labels{"object": "prometheus_hashdex", "method": "parse"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + prometheusHashdexParseSum.Add(float64(time.Now().UnixNano() - start)) + prometheusHashdexParseCount.Inc() return res.scraped, res.error } @@ -2043,15 +2230,14 @@ func walOpenMetricsScraperHashdexParse(hashdex uintptr, buffer []byte, default_t error uint32 scraped uint32 } - start := time.Now() + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_wal_open_metrics_scraper_hashdex_parse, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - unsafeCall.With( - prometheus.Labels{"object": "open_metrics_hashdex", "method": "parse"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + openMetricsHashdexParseSum.Add(float64(time.Now().UnixNano() - start)) + openMetricsHashdexParseCount.Inc() return res.scraped, res.error } @@ -2163,17 +2349,14 @@ func headWalEncoderAddInnerSeries(encoder uintptr, innerSeries []*InnerSeries) ( exception []byte } - start := time.Now() - + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_head_wal_encoder_add_inner_series, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - - unsafeCall.With( - prometheus.Labels{"object": "head_wal_encoder", "method": "add_inner_series"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + headWalEncoderAddInnerSeriesSum.Add(float64(time.Now().UnixNano() - start)) + headWalEncoderAddInnerSeriesCount.Inc() return res.WALEncoderStats, handleException(res.exception) } @@ -2189,17 +2372,14 @@ func headWalEncoderFinalize(encoder uintptr) (stats WALEncoderStats, segment []b exception []byte } - start := time.Now() - + start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.prompp_head_wal_encoder_finalize, uintptr(unsafe.Pointer(&args)), uintptr(unsafe.Pointer(&res)), ) - - unsafeCall.With( - prometheus.Labels{"object": "head_wal_encoder", "method": "finalize"}, - ).Observe(float64(time.Since(start).Nanoseconds())) + headWalEncoderFinalizeSum.Add(float64(time.Now().UnixNano() - start)) + headWalEncoderFinalizeCount.Inc() return res.WALEncoderStats, res.segment, handleException(res.exception) } From 9e222227b4534e74634a811fe1986684f8f49ad5 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Fri, 18 Apr 2025 15:22:41 +0300 Subject: [PATCH 27/90] created QueryableEncodingBimap::copy_added_series for copy label_sets added through find_or_emplace --- pp/bare_bones/bitset.h | 4 +- pp/series_index/queryable_encoding_bimap.h | 20 ++++ .../tests/queryable_encoding_bimap_tests.cpp | 109 ++++++++++++++---- 3 files changed, 106 insertions(+), 27 deletions(-) diff --git a/pp/bare_bones/bitset.h b/pp/bare_bones/bitset.h index 41d266f11f..fcc335a324 100644 --- a/pp/bare_bones/bitset.h +++ b/pp/bare_bones/bitset.h @@ -63,12 +63,12 @@ class Bitset { [[nodiscard]] PROMPP_ALWAYS_INLINE bool is_set(uint32_t v) const noexcept { return v < size() && (data_[v >> 6] & (1ull << (v & 0x3F))) != 0; } - void set(uint32_t v) noexcept { + PROMPP_ALWAYS_INLINE void set(uint32_t v) noexcept { assert(v < size()); data_[v >> 6] |= (1ull << (v & 0x3F)); } - void reset(uint32_t v) noexcept { + PROMPP_ALWAYS_INLINE void reset(uint32_t v) noexcept { assert(v < size()); data_[v >> 6] &= ~(1ull << (v & 0x3F)); } diff --git a/pp/series_index/queryable_encoding_bimap.h b/pp/series_index/queryable_encoding_bimap.h index 4df64102e7..68efea76be 100644 --- a/pp/series_index/queryable_encoding_bimap.h +++ b/pp/series_index/queryable_encoding_bimap.h @@ -58,6 +58,7 @@ class QueryableEncodingBimap final auto composite_label_set = Base::items_.emplace_back(Base::data_, label_set).composite(Base::data()); update_indexes(ls_id, composite_label_set, hash); queried_series_.set_series_count(Base::items_.size()); + mark_series_as_added(ls_id); return ls_id; } @@ -88,6 +89,15 @@ class QueryableEncodingBimap final Base::items_.reserve(count); ls_id_hash_set_.reserve(count); queried_series_.reserve(count); + added_series_.reserve(count); + } + + void copy_added_series(QueryableEncodingBimap& copy_to) const { + const auto size_before = copy_to.size(); + for (auto ls_id : added_series_) { + copy_to.items_.emplace_back(copy_to.data_, this->operator[](ls_id)); + } + copy_to.after_items_load_impl(size_before); } private: @@ -106,6 +116,8 @@ class QueryableEncodingBimap final QueriedSeries queried_series_; + BareBones::Bitset added_series_; + PROMPP_ALWAYS_INLINE void after_items_load_impl(uint32_t first_loaded_id) noexcept { ls_id_hash_set_.reserve(Base::items_.size()); queried_series_.set_series_count(Base::items_.size()); @@ -133,6 +145,14 @@ class QueryableEncodingBimap final sorting_index_.update(ls_id_set_iterator); } + PROMPP_ALWAYS_INLINE void mark_series_as_added(uint32_t ls_id) noexcept { + if (added_series_.size() <= ls_id) [[unlikely]] { + added_series_.resize(ls_id + 1); + } + + added_series_.set(ls_id); + } + PROMPP_ALWAYS_INLINE static bool is_valid_label(std::string_view value) noexcept { return !value.empty(); } PROMPP_ALWAYS_INLINE static size_t phmap_hash(size_t hash) noexcept { return phmap::phmap_mix()(hash); } diff --git a/pp/series_index/tests/queryable_encoding_bimap_tests.cpp b/pp/series_index/tests/queryable_encoding_bimap_tests.cpp index 83b86d8e9d..bc6adaac64 100644 --- a/pp/series_index/tests/queryable_encoding_bimap_tests.cpp +++ b/pp/series_index/tests/queryable_encoding_bimap_tests.cpp @@ -15,23 +15,23 @@ using series_index::trie::CedarTrie; class QueryableEncodingBimapFixture : public testing::Test { protected: using TrieIndex = series_index::TrieIndex; - using Index = QueryableEncodingBimap; + using Lss = QueryableEncodingBimap; - Index index_; + Lss lss_; }; TEST_F(QueryableEncodingBimapFixture, EmplaceLabelSet) { // Arrange // Act - index_.find_or_emplace(LabelViewSet{{"job", "cron"}}); + lss_.find_or_emplace(LabelViewSet{{"job", "cron"}}); // Assert - const auto name_id = index_.trie_index().names_trie().lookup("job"); + const auto name_id = lss_.trie_index().names_trie().lookup("job"); EXPECT_TRUE(name_id); - EXPECT_NE(nullptr, index_.reverse_index().get(*name_id)); + EXPECT_NE(nullptr, lss_.reverse_index().get(*name_id)); - const auto values_trie = index_.trie_index().values_trie(*name_id); + const auto values_trie = lss_.trie_index().values_trie(*name_id); ASSERT_NE(nullptr, values_trie); EXPECT_TRUE(values_trie->lookup("cron")); } @@ -45,13 +45,13 @@ TEST_F(QueryableEncodingBimapFixture, EmplaceInvalidLabel) { label.second = ""; break; } - const auto ls_id = index_.find_or_emplace(ls); + const auto ls_id = lss_.find_or_emplace(ls); // Assert - const auto label = index_[ls_id].begin(); - EXPECT_FALSE(index_.trie_index().names_trie().lookup("key")); - EXPECT_EQ(nullptr, index_.reverse_index().get(label.name_id())); - EXPECT_EQ(nullptr, index_.trie_index().values_trie(label.name_id())); + const auto label = lss_[ls_id].begin(); + EXPECT_FALSE(lss_.trie_index().names_trie().lookup("key")); + EXPECT_EQ(nullptr, lss_.reverse_index().get(label.name_id())); + EXPECT_EQ(nullptr, lss_.trie_index().values_trie(label.name_id())); } TEST_F(QueryableEncodingBimapFixture, EmplaceLabelSetWithInvalidLabel) { @@ -65,35 +65,35 @@ TEST_F(QueryableEncodingBimapFixture, EmplaceLabelSetWithInvalidLabel) { break; } } - auto ls_id = index_.find_or_emplace(ls); + auto ls_id = lss_.find_or_emplace(ls); // Assert { - auto name_id = index_.trie_index().names_trie().lookup("job"); + auto name_id = lss_.trie_index().names_trie().lookup("job"); EXPECT_TRUE(name_id); - EXPECT_NE(nullptr, index_.reverse_index().get(*name_id)); + EXPECT_NE(nullptr, lss_.reverse_index().get(*name_id)); - auto values_trie = index_.trie_index().values_trie(*name_id); + auto values_trie = lss_.trie_index().values_trie(*name_id); ASSERT_NE(nullptr, values_trie); EXPECT_TRUE(values_trie->lookup("cron")); } { - auto second_label = std::next(index_[ls_id].begin()); - auto series_ids = index_.reverse_index().get(second_label.name_id()); + auto second_label = std::next(lss_[ls_id].begin()); + auto series_ids = lss_.reverse_index().get(second_label.name_id()); - EXPECT_FALSE(index_.trie_index().names_trie().lookup("key")); + EXPECT_FALSE(lss_.trie_index().names_trie().lookup("key")); ASSERT_NE(nullptr, series_ids); EXPECT_TRUE(series_ids->is_empty()); - EXPECT_EQ(nullptr, index_.trie_index().values_trie(second_label.name_id())); + EXPECT_EQ(nullptr, lss_.trie_index().values_trie(second_label.name_id())); } { - auto name_id = index_.trie_index().names_trie().lookup("process"); + auto name_id = lss_.trie_index().names_trie().lookup("process"); EXPECT_TRUE(name_id); - EXPECT_NE(nullptr, index_.reverse_index().get(*name_id)); + EXPECT_NE(nullptr, lss_.reverse_index().get(*name_id)); - auto values_trie = index_.trie_index().values_trie(*name_id); + auto values_trie = lss_.trie_index().values_trie(*name_id); ASSERT_NE(nullptr, values_trie); EXPECT_TRUE(values_trie->lookup("php")); } @@ -105,13 +105,72 @@ TEST_F(QueryableEncodingBimapFixture, EmplaceDuplicatedLabelSet) { const auto label_set2 = LabelViewSet{{"job", "cron"}, {"key", ""}, {"process", "php1"}}; // Act - const auto ls_id1 = index_.find_or_emplace(label_set); - const auto existing_ls_id = index_.find_or_emplace(label_set); - const auto ls_id2 = index_.find_or_emplace(label_set2); + const auto ls_id1 = lss_.find_or_emplace(label_set); + const auto existing_ls_id = lss_.find_or_emplace(label_set); + const auto ls_id2 = lss_.find_or_emplace(label_set2); // Assert EXPECT_EQ(ls_id1, existing_ls_id); EXPECT_NE(ls_id1, ls_id2); } +class QueryableEncodingBimapCopyAddedSeriesFixture : public QueryableEncodingBimapFixture {}; + +TEST_F(QueryableEncodingBimapCopyAddedSeriesFixture, EmptyLss) { + // Arrange + Lss lss_copy; + + // Act + lss_.copy_added_series(lss_copy); + + // Assert + EXPECT_EQ(0U, lss_copy.size()); +} + +TEST_F(QueryableEncodingBimapCopyAddedSeriesFixture, NonEmptyLss) { + // Arrange + Lss lss_copy; + + const auto label_set = LabelViewSet{{"job", "cron"}, {"key", ""}, {"process", "php"}}; + const auto label_set2 = LabelViewSet{{"job", "cron"}, {"key", ""}, {"process", "php1"}}; + + lss_.find_or_emplace(label_set); + lss_.find_or_emplace(label_set2); + + // Act + lss_.copy_added_series(lss_copy); + + // Assert + EXPECT_EQ(2U, lss_copy.size()); + EXPECT_EQ(0U, lss_copy.find(label_set)); + EXPECT_EQ(1U, lss_copy.find(label_set2)); + EXPECT_EQ(2U, lss_copy.reverse_index().names_count()); + EXPECT_FALSE(lss_copy.ls_id_set().empty()); + EXPECT_TRUE(lss_copy.trie_index().names_trie().lookup("job")); +} + +TEST_F(QueryableEncodingBimapCopyAddedSeriesFixture, CopyOfCopy) { + // Arrange + Lss lss_copy; + Lss lss_copy2; + + const auto label_set = LabelViewSet{{"job", "cron"}, {"key", ""}, {"process", "php"}}; + const auto label_set2 = LabelViewSet{{"job", "cron"}, {"key", ""}, {"process", "php1"}}; + + lss_.find_or_emplace(label_set); + lss_.find_or_emplace(label_set2); + + // Act + lss_.copy_added_series(lss_copy); + lss_copy.copy_added_series(lss_copy2); + + // Assert + EXPECT_EQ(0U, lss_copy2.size()); + EXPECT_FALSE(lss_copy2.find(label_set)); + EXPECT_FALSE(lss_copy2.find(label_set2)); + EXPECT_EQ(0U, lss_copy2.reverse_index().names_count()); + EXPECT_TRUE(lss_copy2.ls_id_set().empty()); + EXPECT_FALSE(lss_copy2.trie_index().names_trie().lookup("job")); +} + } // namespace From 6d6235ec2e9ccf52de159f4516f40ef45259259c Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Fri, 18 Apr 2025 12:25:31 +0000 Subject: [PATCH 28/90] for save --- pp/entrypoint/entrypoint.cpp.template | 2 ++ pp/entrypoint/entrypoint.h.template | 2 ++ pp/go/cppbridge/c_garbage_collector.go | 2 +- pp/go/cppbridge/entrypoint.go | 19 +++++++++++++++---- pp/go/cppbridge/entrypoint.h | 2 ++ pp/go/cppbridge/entrypoint_test.go | 19 +++++++++++++++++++ 6 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 pp/go/cppbridge/entrypoint_test.go diff --git a/pp/entrypoint/entrypoint.cpp.template b/pp/entrypoint/entrypoint.cpp.template index a769af642b..f6cf6d575f 100644 --- a/pp/entrypoint/entrypoint.cpp.template +++ b/pp/entrypoint/entrypoint.cpp.template @@ -56,6 +56,8 @@ extern "C" void prompp_get_flavor(void* res) { } } +extern "C" void iunsafe_call_2(void* args [[maybe_unused]], void* res [[maybe_unused]]) {} + #define EXTERN_FUNCTION(return_type, name, args) \ return_type amd64_haswell_##name args; \ return_type amd64_nehalem_##name args; \ diff --git a/pp/entrypoint/entrypoint.h.template b/pp/entrypoint/entrypoint.h.template index fc0223074d..029fee62cd 100644 --- a/pp/entrypoint/entrypoint.h.template +++ b/pp/entrypoint/entrypoint.h.template @@ -11,6 +11,8 @@ extern "C" { */ void prompp_get_flavor(void* res); +void iunsafe_call_2(void* args, void* res); + #ifdef __cplusplus } #endif diff --git a/pp/go/cppbridge/c_garbage_collector.go b/pp/go/cppbridge/c_garbage_collector.go index 06984bcf92..ca4f274e24 100644 --- a/pp/go/cppbridge/c_garbage_collector.go +++ b/pp/go/cppbridge/c_garbage_collector.go @@ -133,7 +133,7 @@ func (cgc *CGOGC) run() { func (cgc *CGOGC) gc() { memInfo := GetMemInfo() - unsafeCall2() + EmptyUnsafeCall2() if !cgc.isOverThreshold(memInfo) { return diff --git a/pp/go/cppbridge/entrypoint.go b/pp/go/cppbridge/entrypoint.go index 0c84bbb0a9..343f71736e 100644 --- a/pp/go/cppbridge/entrypoint.go +++ b/pp/go/cppbridge/entrypoint.go @@ -300,11 +300,11 @@ func dumpMemoryProfile(filename string) int { return res.error } -func unsafeCall2() { +func EmptyUnsafeCall2() { var args struct{} var res struct{} - start := time.Now().UnixNano() + // start := time.Now().UnixNano() fastcgo.UnsafeCall2( C.unsafe_call_2, @@ -312,8 +312,19 @@ func unsafeCall2() { uintptr(unsafe.Pointer(&res)), ) - unsafeCall2Sum.Add(float64(time.Now().UnixNano() - start)) - unsafeCall2Count.Inc() + // unsafeCall2Sum.Add(float64(time.Now().UnixNano() - start)) + // unsafeCall2Count.Inc() +} + +func EmptyIUnsafeCall2() { + var args struct{} + var res struct{} + + fastcgo.UnsafeCall2( + C.iunsafe_call_2, + uintptr(unsafe.Pointer(&args)), + uintptr(unsafe.Pointer(&res)), + ) } // diff --git a/pp/go/cppbridge/entrypoint.h b/pp/go/cppbridge/entrypoint.h index 9ef61e02f7..c84dd6043e 100755 --- a/pp/go/cppbridge/entrypoint.h +++ b/pp/go/cppbridge/entrypoint.h @@ -1825,6 +1825,8 @@ extern "C" { */ void prompp_get_flavor(void* res); +void iunsafe_call_2(void* args, void* res); + #ifdef __cplusplus } #endif diff --git a/pp/go/cppbridge/entrypoint_test.go b/pp/go/cppbridge/entrypoint_test.go new file mode 100644 index 0000000000..0376dfcbd9 --- /dev/null +++ b/pp/go/cppbridge/entrypoint_test.go @@ -0,0 +1,19 @@ +package cppbridge_test + +import ( + "testing" + + "github.com/prometheus/prometheus/pp/go/cppbridge" +) + +func BenchmarkEmptyIUnsafeCall2(b *testing.B) { + for i := 0; i < b.N; i++ { + cppbridge.EmptyIUnsafeCall2() + } +} + +func BenchmarkEmptyUnsafeCall2(b *testing.B) { + for i := 0; i < b.N; i++ { + cppbridge.EmptyUnsafeCall2() + } +} From 736725c177455f3ea9ca533251f8e979223d4320 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Fri, 18 Apr 2025 18:15:54 +0300 Subject: [PATCH 29/90] created prompp_primitives_lss_copy_added_series Go-binding --- pp/entrypoint/primitives_lss.cpp | 26 +++++-- pp/entrypoint/primitives_lss.h | 13 ++++ pp/go/cppbridge/entrypoint.go | 17 +++++ pp/go/cppbridge/entrypoint.h | 13 ++++ pp/go/cppbridge/primitives_lss.go | 12 ++- pp/go/cppbridge/primitives_lss_test.go | 101 +++++++++++++++---------- 6 files changed, 136 insertions(+), 46 deletions(-) diff --git a/pp/entrypoint/primitives_lss.cpp b/pp/entrypoint/primitives_lss.cpp index 7cf9d8e881..e77d697b6b 100644 --- a/pp/entrypoint/primitives_lss.cpp +++ b/pp/entrypoint/primitives_lss.cpp @@ -25,6 +25,19 @@ extern "C" void prompp_primitives_lss_ctor(void* args, void* res) { new (res) Result{.lss = create_lss(static_cast(args)->lss_type)}; } +extern "C" void prompp_primitives_lss_copy_added_series(void* args, void* res) { + struct Arguments { + LssVariantPtr lss; + }; + struct Result { + LssVariantPtr lss; + }; + + const auto arguments = static_cast(args); + const auto result = new (res) Result{.lss = create_lss(LssType::kQueryableEncodingBimap)}; + std::get(*arguments->lss).copy_added_series(std::get(*result->lss)); +} + extern "C" void prompp_primitives_lss_dtor(void* args) { struct Arguments { LssVariantPtr lss; @@ -127,11 +140,14 @@ void prompp_primitives_lss_get_label_sets(void* args, void* res) { out->label_sets.resize(in->series_ids.size()); for (size_t i = 0; i < in->series_ids.size(); ++i) { - auto in_label_set = lss[in->series_ids[i]]; - auto& out_label_set = out->label_sets[i]; - out_label_set.reserve(in_label_set.size()); - std::ranges::transform(in_label_set, std::back_inserter(out_label_set), - [](const auto& label) PROMPP_LAMBDA_INLINE { return Label({.name = String{label.first}, .value = String{label.second}}); }); + const auto ls_id = in->series_ids[i]; + if (lss.size() > ls_id) [[likely]] { + auto in_label_set = lss[ls_id]; + auto& out_label_set = out->label_sets[i]; + out_label_set.reserve(in_label_set.size()); + std::ranges::transform(in_label_set, std::back_inserter(out_label_set), + [](const auto& label) PROMPP_LAMBDA_INLINE { return Label({.name = String{label.first}, .value = String{label.second}}); }); + } } }, *in->lss); diff --git a/pp/entrypoint/primitives_lss.h b/pp/entrypoint/primitives_lss.h index acad0fa639..b1d3e74007 100644 --- a/pp/entrypoint/primitives_lss.h +++ b/pp/entrypoint/primitives_lss.h @@ -15,6 +15,19 @@ extern "C" { */ void prompp_primitives_lss_ctor(void* args, void* res); +/** + * @brief Construct a new Primitives label sets. + * + * @param args { + * lss uintptr // pointer of label sets; + * } + * + * @param res { + * lss uintptr // pointer to constructed label sets; + * } + */ +void prompp_primitives_lss_copy_added_series(void* args, void* res); + /** * @brief Destroy Primitives label sets. * diff --git a/pp/go/cppbridge/entrypoint.go b/pp/go/cppbridge/entrypoint.go index ee06d87d03..a8eb5e1528 100644 --- a/pp/go/cppbridge/entrypoint.go +++ b/pp/go/cppbridge/entrypoint.go @@ -845,6 +845,23 @@ func primitivesLSSCtor(lss_type uint32) uintptr { return res.lss } +func primitivesLSSCopyAddedSeries(lss uintptr) uintptr { + args := struct { + lss uintptr + }{lss} + var res struct { + lss uintptr + } + + fastcgo.UnsafeCall2( + C.prompp_primitives_lss_copy_added_series, + uintptr(unsafe.Pointer(&args)), + uintptr(unsafe.Pointer(&res)), + ) + + return res.lss +} + // primitivesLSSDtor - wrapper for destructor C-EncodingBimap. func primitivesLSSDtor(lss uintptr) { args := struct { diff --git a/pp/go/cppbridge/entrypoint.h b/pp/go/cppbridge/entrypoint.h index 08b0c9ec5d..afe75514db 100755 --- a/pp/go/cppbridge/entrypoint.h +++ b/pp/go/cppbridge/entrypoint.h @@ -382,6 +382,19 @@ extern "C" { */ void prompp_primitives_lss_ctor(void* args, void* res); +/** + * @brief Construct a new Primitives label sets. + * + * @param args { + * lss uintptr // pointer of label sets; + * } + * + * @param res { + * lss uintptr // pointer to constructed label sets; + * } + */ +void prompp_primitives_lss_copy_added_series(void* args, void* res); + /** * @brief Destroy Primitives label sets. * diff --git a/pp/go/cppbridge/primitives_lss.go b/pp/go/cppbridge/primitives_lss.go index 313b636911..c60eb9cb11 100644 --- a/pp/go/cppbridge/primitives_lss.go +++ b/pp/go/cppbridge/primitives_lss.go @@ -90,7 +90,12 @@ func NewQueryableLssStorage() *LabelSetStorage { // newLabelSetStorage init new LabelSetStorage with lss type. func newLabelSetStorage(lssType uint32) *LabelSetStorage { - lss := &LabelSetStorage{pointer: primitivesLSSCtor(lssType)} + return newLabelSetStorageFromPointer(primitivesLSSCtor(lssType)) +} + +// newLabelSetStorageFromPointer init new LabelSetStorage with pointer to constructed lss +func newLabelSetStorageFromPointer(lssPointer uintptr) *LabelSetStorage { + lss := &LabelSetStorage{pointer: lssPointer} runtime.SetFinalizer(lss, func(lss *LabelSetStorage) { primitivesLSSDtor(lss.pointer) }) @@ -166,6 +171,11 @@ func (lss *LabelSetStorage) GetLabelSets(labelSetIDs []uint32) *LabelSetStorageG return result } +// CopyAddedSeries - returns copy of lss with label sets which were added via FindOrEmplace +func (lss *LabelSetStorage) CopyAddedSeries() *LabelSetStorage { + return newLabelSetStorageFromPointer(primitivesLSSCopyAddedSeries(lss.pointer)) +} + // Pointer return c-pointer. func (lss *LabelSetStorage) Pointer() uintptr { return lss.pointer diff --git a/pp/go/cppbridge/primitives_lss_test.go b/pp/go/cppbridge/primitives_lss_test.go index a1f6fdde86..a33684a776 100644 --- a/pp/go/cppbridge/primitives_lss_test.go +++ b/pp/go/cppbridge/primitives_lss_test.go @@ -123,44 +123,51 @@ func (s *QueryableLSSSuite) TestQuery() { } func (s *QueryableLSSSuite) TestGetLabelSets() { + // Arrange + + // Act fetchedLabelSets := s.lss.GetLabelSets(s.labelSetIDs) - for index, labelSet := range s.labelSets { - s.Require().True(isLabelSetEqualsToLabels(labelSet, fetchedLabelSets.LabelsSets()[index])) - } + // Assert + s.Equal(labelSetToCppBridgeLabels(s.labelSets), fetchedLabelSets.LabelsSets()) } -func isLabelSetEqualsToLabels(labelSet model.LabelSet, labels cppbridge.Labels) bool { - labelSetString := labelSet.String() - labelsString := "" - for _, label := range labels { - labelsString += label.Name + ":" + label.Value + ";" +func labelSetToCppBridgeLabels(labelSets []model.LabelSet) []cppbridge.Labels { + result := make([]cppbridge.Labels, 0, len(labelSets)) + for _, labelSet := range labelSets { + cppLabels := make(cppbridge.Labels, labelSet.Len()) + for i := 0; i < labelSet.Len(); i++ { + cppLabels[i].Name = labelSet.Key(i) + cppLabels[i].Value = labelSet.Value(i) + } + result = append(result, cppLabels) } - return labelSetString == labelsString + + return result } type queryLabelNameCase struct { - matchers []model.LabelMatcher - expected_status uint32 - expected_names []string + matchers []model.LabelMatcher + expectedStatus uint32 + expectedNames []string } var queryLabelNamesCases = []queryLabelNameCase{ { - matchers: []model.LabelMatcher{}, - expected_status: cppbridge.LSSQueryStatusMatch, - expected_names: []string{"che", "foo", "lol", "zhe"}, + matchers: []model.LabelMatcher{}, + expectedStatus: cppbridge.LSSQueryStatusMatch, + expectedNames: []string{"che", "foo", "lol", "zhe"}, }, { - matchers: []model.LabelMatcher{{Name: "lol", Value: ".+", MatcherType: model.MatcherTypeRegexpMatch}}, - expected_status: cppbridge.LSSQueryStatusMatch, - expected_names: []string{"che", "lol", "zhe"}, + matchers: []model.LabelMatcher{{Name: "lol", Value: ".+", MatcherType: model.MatcherTypeRegexpMatch}}, + expectedStatus: cppbridge.LSSQueryStatusMatch, + expectedNames: []string{"che", "lol", "zhe"}, }, } func (s *QueryableLSSSuite) TestQueryLabelNames() { - for _, test_case := range queryLabelNamesCases { - s.testQueryLabelNamesImpl(test_case) + for _, testCase := range queryLabelNamesCases { + s.testQueryLabelNamesImpl(testCase) } } @@ -171,45 +178,59 @@ func (s *QueryableLSSSuite) testQueryLabelNamesImpl(test_case queryLabelNameCase result := s.lss.QueryLabelNames(test_case.matchers) // Assert - s.Equal(test_case.expected_status, result.Status()) - s.Equal(test_case.expected_names, result.Names()) + s.Equal(test_case.expectedStatus, result.Status()) + s.Equal(test_case.expectedNames, result.Names()) } type queryLabelValuesCase struct { - label_name string - matchers []model.LabelMatcher - expected_status uint32 - expected_values []string + labelName string + matchers []model.LabelMatcher + expectedStatus uint32 + expectedValues []string } var queryLabelValuesCases = []queryLabelValuesCase{ { - label_name: "foo", - matchers: []model.LabelMatcher{}, - expected_status: cppbridge.LSSQueryStatusMatch, - expected_values: []string{"bar", "baz"}, + labelName: "foo", + matchers: []model.LabelMatcher{}, + expectedStatus: cppbridge.LSSQueryStatusMatch, + expectedValues: []string{"bar", "baz"}, }, { - label_name: "foo", - matchers: []model.LabelMatcher{{Name: "foo", Value: ".+", MatcherType: model.MatcherTypeRegexpMatch}}, - expected_status: cppbridge.LSSQueryStatusMatch, - expected_values: []string{"bar", "baz"}, + labelName: "foo", + matchers: []model.LabelMatcher{{Name: "foo", Value: ".+", MatcherType: model.MatcherTypeRegexpMatch}}, + expectedStatus: cppbridge.LSSQueryStatusMatch, + expectedValues: []string{"bar", "baz"}, }, } func (s *QueryableLSSSuite) TestQueryLabelValues() { - for _, test_case := range queryLabelValuesCases { - s.testQueryLabelValuesImpl(test_case) + for _, testCase := range queryLabelValuesCases { + s.testQueryLabelValuesImpl(testCase) } } -func (s *QueryableLSSSuite) testQueryLabelValuesImpl(test_case queryLabelValuesCase) { +func (s *QueryableLSSSuite) testQueryLabelValuesImpl(testCase queryLabelValuesCase) { // Arrange // Act - result := s.lss.QueryLabelValues(test_case.label_name, test_case.matchers) + result := s.lss.QueryLabelValues(testCase.labelName, testCase.matchers) // Assert - s.Equal(test_case.expected_status, result.Status()) - s.Equal(test_case.expected_values, result.Values()) + s.Equal(testCase.expectedStatus, result.Status()) + s.Equal(testCase.expectedValues, result.Values()) +} + +func (s *QueryableLSSSuite) TestCopyAddedSeries() { + // Arrange + emptyLabelsSets := make([]cppbridge.Labels, len(s.labelSetIDs)) + + // Act + lssCopy := s.lss.CopyAddedSeries() + lssCopyOfCopy := lssCopy.CopyAddedSeries() + + // Assert + + s.Equal(labelSetToCppBridgeLabels(s.labelSets), lssCopy.GetLabelSets(s.labelSetIDs).LabelsSets()) + s.Equal(emptyLabelsSets, lssCopyOfCopy.GetLabelSets(s.labelSetIDs).LabelsSets()) } From 4575b6e0e00b5872075698663ea9e9587bd70765 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 22 Apr 2025 12:56:34 +0000 Subject: [PATCH 30/90] add otlphandler --- pp-pkg/handler/interface.go | 8 + pp-pkg/handler/otlp_handler.go | 727 ++++++++++++++++++++++++++++ pp-pkg/handler/pp_converter_test.go | 158 ++++++ pp/go/model/labelset.go | 16 +- web/api/v1/api.go | 2 +- 5 files changed, 909 insertions(+), 2 deletions(-) create mode 100644 pp-pkg/handler/otlp_handler.go create mode 100644 pp-pkg/handler/pp_converter_test.go diff --git a/pp-pkg/handler/interface.go b/pp-pkg/handler/interface.go index 6fc84e953a..6b231ecdf2 100644 --- a/pp-pkg/handler/interface.go +++ b/pp-pkg/handler/interface.go @@ -11,8 +11,16 @@ import ( // Receiver interface. type Receiver interface { + Appender(ctx context.Context) storage.Appender AppendSnappyProtobuf(ctx context.Context, compressedData relabeler.ProtobufData, relabelerID string, commitToWal bool) error AppendHashdex(ctx context.Context, hashdex cppbridge.ShardedData, relabelerID string, commitToWal bool) error + AppendTimeSeries( + ctx context.Context, + data relabeler.TimeSeriesData, + state *cppbridge.State, + relabelerID string, + commitToWal bool, + ) (cppbridge.RelabelerStats, error) RelabelerIDIsExist(relabelerID string) bool HeadQueryable() storage.Queryable HeadStatus(limit int) relabeler.HeadStatus diff --git a/pp-pkg/handler/otlp_handler.go b/pp-pkg/handler/otlp_handler.go new file mode 100644 index 0000000000..283288fa9f --- /dev/null +++ b/pp-pkg/handler/otlp_handler.go @@ -0,0 +1,727 @@ +package handler + +import ( + "errors" + "fmt" + "io" + "math" + "net/http" + "slices" + "sort" + "strconv" + "strings" + "time" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/klauspost/compress/gzip" + "github.com/prometheus/common/model" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" + conventions "go.opentelemetry.io/collector/semconv/v1.6.1" + "go.uber.org/multierr" + + prom_config "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/model/value" + ppmodel "github.com/prometheus/prometheus/pp/go/model" + "github.com/prometheus/prometheus/pp/go/relabeler" + "github.com/prometheus/prometheus/storage" + prometheustranslator "github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheus" +) + +const ( + pbContentType = "application/x-protobuf" + jsonContentType = "application/json" +) + +const ( + sumStr = "_sum" + countStr = "_count" + bucketStr = "_bucket" + leStr = "le" + pInfStr = "+Inf" + quantileStr = "quantile" + targetMetricName = "target_info" +) + +// OTLPWriteHandler handler for otlp data via remote write. +type OTLPWriteHandler struct { + logger log.Logger + receiver Receiver +} + +func NewOTLPWriteHandler(logger log.Logger, receiver Receiver) *OTLPWriteHandler { + return &OTLPWriteHandler{ + logger: logger, + receiver: receiver, + } +} + +// ServeHTTP implementation http.Handler. +func (h *OTLPWriteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + req, err := DecodeOTLPWriteRequest(r) + if err != nil { + level.Error(h.logger).Log("msg", "Error decoding remote write request", "err", err.Error()) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + converter := NewPPConverter(h.logger, req.Metrics().MetricCount()) + if err := converter.FromMetrics(req.Metrics()); err != nil { + level.Warn(h.logger).Log("msg", "Error translating OTLP metrics to Prometheus write request", "err", err) + } + + stats, err := h.receiver.AppendTimeSeries( + r.Context(), + converter.TimeSeries(), + nil, + prom_config.TransparentRelabeler, + true, + ) + + switch { + case err == nil: + case errors.Is(err, storage.ErrOutOfOrderSample), + errors.Is(err, storage.ErrOutOfBounds), + errors.Is(err, storage.ErrDuplicateSampleForTimestamp): + // Indicated an out of order sample is a bad request to prevent retries. + http.Error(w, err.Error(), http.StatusBadRequest) + return + default: + level.Error(h.logger).Log("msg", "Error appending remote write", "err", err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + level.Debug(h.logger).Log("msg", "append metrics", "stats", stats) + + w.WriteHeader(http.StatusOK) +} + +// DecodeOTLPWriteRequest decode OTLP from http.Request. +func DecodeOTLPWriteRequest(r *http.Request) (pmetricotlp.ExportRequest, error) { + contentType := r.Header.Get("Content-Type") + var decoderFunc func(buf []byte) (pmetricotlp.ExportRequest, error) + switch contentType { + case pbContentType: + decoderFunc = func(buf []byte) (pmetricotlp.ExportRequest, error) { + req := pmetricotlp.NewExportRequest() + return req, req.UnmarshalProto(buf) + } + + case jsonContentType: + decoderFunc = func(buf []byte) (pmetricotlp.ExportRequest, error) { + req := pmetricotlp.NewExportRequest() + return req, req.UnmarshalJSON(buf) + } + + default: + return pmetricotlp.NewExportRequest(), fmt.Errorf( + "unsupported content type: %s, supported: [%s, %s]", + contentType, + jsonContentType, + pbContentType, + ) + } + + reader := r.Body + // Handle compression. + switch r.Header.Get("Content-Encoding") { + case "gzip": + gr, err := gzip.NewReader(reader) + if err != nil { + return pmetricotlp.NewExportRequest(), err + } + reader = gr + + case "": + // No compression. + + default: + return pmetricotlp.NewExportRequest(), fmt.Errorf( + "unsupported compression: %s. Only \"gzip\" or no compression supported", + r.Header.Get("Content-Encoding"), + ) + } + + body, err := io.ReadAll(reader) + if err != nil { + r.Body.Close() + return pmetricotlp.NewExportRequest(), err + } + if err = r.Body.Close(); err != nil { + return pmetricotlp.NewExportRequest(), err + } + otlpReq, err := decoderFunc(body) + if err != nil { + return pmetricotlp.NewExportRequest(), err + } + + return otlpReq, nil +} + +// +// timeSeriesData +// + +// timeSeriesData implementation relabeler.TimeSeriesData. +type timeSeriesData struct { + timeSeries []ppmodel.TimeSeries +} + +// TimeSeries return slice ppmodel.TimeSeries. +func (d *timeSeriesData) TimeSeries() []ppmodel.TimeSeries { + return d.timeSeries +} + +// Destroy implementation relabeler.TimeSeriesData. +func (d *timeSeriesData) Destroy() { + d.timeSeries = nil +} + +// +// PPConverter +// + +// PPConverter converts from OTel write format to PP remote write format. +type PPConverter struct { + labels map[uint64][]*ppmodel.LabelSet + timeSeries []ppmodel.TimeSeries + logger log.Logger +} + +// NewPPConverter init new *PPConverter. +func NewPPConverter(logger log.Logger, count int) *PPConverter { + return &PPConverter{ + labels: make(map[uint64][]*ppmodel.LabelSet, count), + timeSeries: make([]ppmodel.TimeSeries, 0, count), + logger: logger, + } +} + +// FromMetrics converts pmetric.Metrics to Prometheus remote write format. +func (c *PPConverter) FromMetrics(md pmetric.Metrics) (errs error) { + resourceMetricsSlice := md.ResourceMetrics() + for i := 0; i < resourceMetricsSlice.Len(); i++ { + resourceMetrics := resourceMetricsSlice.At(i) + resource := resourceMetrics.Resource() + scopeMetricsSlice := resourceMetrics.ScopeMetrics() + // keep track of the most recent timestamp in the ResourceMetrics for + // use with the "target" info metric + var mostRecentTimestamp pcommon.Timestamp + for j := 0; j < scopeMetricsSlice.Len(); j++ { + metricSlice := scopeMetricsSlice.At(j).Metrics() + + // TODO: decide if instrumentation library information should be exported as labels + for k := 0; k < metricSlice.Len(); k++ { + metric := metricSlice.At(k) + mostRecentTimestamp = max(mostRecentTimestamp, mostRecentTimestampInMetric(metric)) + + if !isValidAggregationTemporality(metric) { + errs = multierr.Append( + errs, + fmt.Errorf("invalid temporality and type combination for metric %q", metric.Name()), + ) + continue + } + + promName := prometheustranslator.BuildCompliantName(metric, "", true) + + // handle individual metrics based on type + //exhaustive:enforce + switch metric.Type() { + case pmetric.MetricTypeGauge: + dataPoints := metric.Gauge().DataPoints() + if dataPoints.Len() == 0 { + errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) + break + } + c.addGaugeNumberDataPoints(dataPoints, resource, promName) + case pmetric.MetricTypeSum: + dataPoints := metric.Sum().DataPoints() + if dataPoints.Len() == 0 { + errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) + break + } + c.addSumNumberDataPoints(dataPoints, resource, promName) + case pmetric.MetricTypeHistogram: + dataPoints := metric.Histogram().DataPoints() + if dataPoints.Len() == 0 { + errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) + break + } + c.addHistogramDataPoints(dataPoints, resource, promName) + case pmetric.MetricTypeExponentialHistogram: + dataPoints := metric.ExponentialHistogram().DataPoints() + if dataPoints.Len() == 0 { + errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) + break + } + errs = multierr.Append(errs, c.addExponentialHistogramDataPoints( + dataPoints, + resource, + promName, + )) + case pmetric.MetricTypeSummary: + dataPoints := metric.Summary().DataPoints() + if dataPoints.Len() == 0 { + errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) + break + } + c.addSummaryDataPoints(dataPoints, resource, promName) + default: + errs = multierr.Append(errs, errors.New("unsupported metric type")) + } + } + } + c.addResourceTargetInfo(resource, mostRecentTimestamp) + } + + return +} + +// TimeSeries returns a slice of the ppmodel.TimeSeries that were converted from OTel format. +func (c *PPConverter) TimeSeries() relabeler.TimeSeriesData { + slices.SortFunc( + c.timeSeries, + func(a, b ppmodel.TimeSeries) int { + return strings.Compare(a.LabelSet.String(), b.LabelSet.String()) + }, + ) + return &timeSeriesData{ + timeSeries: c.timeSeries, + } +} + +// addGaugeNumberDataPoints add Gauge DataPoints. +func (c *PPConverter) addGaugeNumberDataPoints( + dataPoints pmetric.NumberDataPointSlice, + resource pcommon.Resource, + name string, +) { + for x := 0; x < dataPoints.Len(); x++ { + pt := dataPoints.At(x) + labels := c.createAttributes( + resource, + pt.Attributes(), + nil, + true, + model.MetricNameLabel, + name, + ) + + var v float64 + switch pt.ValueType() { + case pmetric.NumberDataPointValueTypeInt: + v = float64(pt.IntValue()) + case pmetric.NumberDataPointValueTypeDouble: + v = pt.DoubleValue() + } + if pt.Flags().NoRecordedValue() { + v = math.Float64frombits(value.StaleNaN) + } + c.addTimeSeries(labels, v, convertTimeStamp(pt.Timestamp())) + } +} + +// addSumNumberDataPoints add Sum DataPoints. +func (c *PPConverter) addSumNumberDataPoints( + dataPoints pmetric.NumberDataPointSlice, + resource pcommon.Resource, + name string, +) { + for x := 0; x < dataPoints.Len(); x++ { + pt := dataPoints.At(x) + lbls := c.createAttributes( + resource, + pt.Attributes(), + nil, + true, + model.MetricNameLabel, + name, + ) + + var v float64 + switch pt.ValueType() { + case pmetric.NumberDataPointValueTypeInt: + v = float64(pt.IntValue()) + case pmetric.NumberDataPointValueTypeDouble: + v = pt.DoubleValue() + } + if pt.Flags().NoRecordedValue() { + v = math.Float64frombits(value.StaleNaN) + } + c.addTimeSeries(lbls, v, convertTimeStamp(pt.Timestamp())) + } +} + +// addHistogramDataPoints add Histogram DataPoints. +func (c *PPConverter) addHistogramDataPoints( + dataPoints pmetric.HistogramDataPointSlice, + resource pcommon.Resource, + baseName string, +) { + for x := 0; x < dataPoints.Len(); x++ { + pt := dataPoints.At(x) + timestamp := convertTimeStamp(pt.Timestamp()) + baseLabels := c.createAttributes(resource, pt.Attributes(), nil, false) + + // If the sum is unset, it indicates the _sum metric point should be + // omitted + if pt.HasSum() { + // treat sum as a sample in an individual TimeSeries + sum := pt.Sum() + if pt.Flags().NoRecordedValue() { + sum = math.Float64frombits(value.StaleNaN) + } + + c.addTimeSeries(createLabels(baseName+sumStr, baseLabels), sum, timestamp) + } + + // treat count as a sample in an individual TimeSeries + count := float64(pt.Count()) + if pt.Flags().NoRecordedValue() { + count = math.Float64frombits(value.StaleNaN) + } + + c.addTimeSeries(createLabels(baseName+countStr, baseLabels), count, timestamp) + + // cumulative count for conversion to cumulative histogram + var cumulativeCount uint64 + + // process each bound, based on histograms proto definition, # of buckets = # of explicit bounds + 1 + for i := 0; i < pt.ExplicitBounds().Len() && i < pt.BucketCounts().Len(); i++ { + bound := pt.ExplicitBounds().At(i) + cumulativeCount += pt.BucketCounts().At(i) + + bucket := float64(cumulativeCount) + if pt.Flags().NoRecordedValue() { + bucket = math.Float64frombits(value.StaleNaN) + } + + c.addTimeSeries( + createLabels(baseName+bucketStr, baseLabels, leStr, strconv.FormatFloat(bound, 'f', -1, 64)), + bucket, + timestamp, + ) + } + + // add le=+Inf bucket + var infBucket float64 + if pt.Flags().NoRecordedValue() { + infBucket = math.Float64frombits(value.StaleNaN) + } else { + infBucket = float64(pt.Count()) + } + + c.addTimeSeries(createLabels(baseName+bucketStr, baseLabels, leStr, pInfStr), infBucket, timestamp) + } +} + +// addExponentialHistogramDataPoints add ExponentialHistogram DataPoints. NotNot implemented. +func (c *PPConverter) addExponentialHistogramDataPoints( + _ pmetric.ExponentialHistogramDataPointSlice, + _ pcommon.Resource, + _ string, +) error { + // skip, TODO Histograms Exemplars + return nil +} + +// addSummaryDataPoints add Summary DataPoints. +func (c *PPConverter) addSummaryDataPoints( + dataPoints pmetric.SummaryDataPointSlice, + resource pcommon.Resource, + baseName string, +) { + for x := 0; x < dataPoints.Len(); x++ { + pt := dataPoints.At(x) + timestamp := convertTimeStamp(pt.Timestamp()) + baseLabels := c.createAttributes(resource, pt.Attributes(), nil, false) + + // treat sum as a sample in an individual TimeSeries + sum := pt.Sum() + if pt.Flags().NoRecordedValue() { + sum = math.Float64frombits(value.StaleNaN) + } + + // sum and count of the summary should append suffix to baseName + c.addTimeSeries(createLabels(baseName+sumStr, baseLabels), sum, timestamp) + + // treat count as a sample in an individual TimeSeries + count := float64(pt.Count()) + if pt.Flags().NoRecordedValue() { + count = math.Float64frombits(value.StaleNaN) + } + + c.addTimeSeries(createLabels(baseName+countStr, baseLabels), count, timestamp) + + // process each percentile/quantile + for i := 0; i < pt.QuantileValues().Len(); i++ { + qt := pt.QuantileValues().At(i) + quantile := qt.Value() + if pt.Flags().NoRecordedValue() { + quantile = math.Float64frombits(value.StaleNaN) + } + + c.addTimeSeries( + createLabels(baseName, baseLabels, quantileStr, strconv.FormatFloat(qt.Quantile(), 'f', -1, 64)), + quantile, + timestamp, + ) + } + } +} + +// addResourceTargetInfo converts the resource to the target info metric. +func (c *PPConverter) addResourceTargetInfo( + resource pcommon.Resource, + timestamp pcommon.Timestamp, +) { + if timestamp == 0 { + return + } + + attributes := resource.Attributes() + identifyingAttrs := []string{ + conventions.AttributeServiceNamespace, + conventions.AttributeServiceName, + conventions.AttributeServiceInstanceID, + } + nonIdentifyingAttrsCount := attributes.Len() + for _, a := range identifyingAttrs { + _, haveAttr := attributes.Get(a) + if haveAttr { + nonIdentifyingAttrsCount-- + } + } + if nonIdentifyingAttrsCount == 0 { + // If we only have job + instance, then target_info isn't useful, so don't add it. + return + } + + labels := c.createAttributes(resource, attributes, identifyingAttrs, false, model.MetricNameLabel, targetMetricName) + haveIdentifier := false + + labels.Range(func(name, _ string) bool { + if name == model.JobLabel || name == model.InstanceLabel { + haveIdentifier = true + return false + } + + return true + }) + + if !haveIdentifier { + // We need at least one identifying label to generate target_info. + return + } + + c.addTimeSeries(labels, 1, convertTimeStamp(timestamp)) +} + +// addTimeSeries add TimeSeries. +func (c *PPConverter) addTimeSeries(lbls ppmodel.LabelSet, v float64, ts int64) { + if lbls.Len() == 0 { + // This shouldn't happen + return + } + + c.timeSeries = append( + c.timeSeries, + ppmodel.TimeSeries{ + LabelSet: *c.getLabelSet(lbls), + Timestamp: uint64(ts), + Value: v, + }, + ) +} + +// getLabelSet check labels in cache and return. +func (c *PPConverter) getLabelSet(lbls ppmodel.LabelSet) *ppmodel.LabelSet { + h := lbls.Hash() + + for _, ls := range c.labels[h] { + if lbls.String() == ls.String() { + return ls + } + } + + c.labels[h] = append(c.labels[h], &lbls) + + return &lbls +} + +// createAttributes create labels attributes. +func (c *PPConverter) createAttributes( + resource pcommon.Resource, + attributes pcommon.Map, + ignoreAttrs []string, + logOnOverwrite bool, + extras ...string, +) ppmodel.LabelSet { + resourceAttrs := resource.Attributes() + serviceName, haveServiceName := resourceAttrs.Get(conventions.AttributeServiceName) + instance, haveInstanceID := resourceAttrs.Get(conventions.AttributeServiceInstanceID) + + // Calculate the maximum possible number of labels we could return so we can preallocate l + maxLabelCount := attributes.Len() + len(extras)/2 + + if haveServiceName { + maxLabelCount++ + } + + if haveInstanceID { + maxLabelCount++ + } + + // map ensures no duplicate label name + l := make(map[string]string, maxLabelCount) + + // Ensure attributes are sorted by key for consistent merging of keys which + // collide when sanitized. + labels := make([]ppmodel.SimpleLabel, 0, maxLabelCount) + + attributes.Range(func(key string, value pcommon.Value) bool { + if !slices.Contains(ignoreAttrs, key) { + labels = append(labels, ppmodel.SimpleLabel{Name: key, Value: value.AsString()}) + } + + return true + }) + sort.Stable(ByLabelName(labels)) + + for _, label := range labels { + finalKey := prometheustranslator.NormalizeLabel(label.Name) + if existingValue, alreadyExists := l[finalKey]; alreadyExists { + l[finalKey] = existingValue + ";" + label.Value + } else { + l[finalKey] = label.Value + } + } + + // Map service.name + service.namespace to job + if haveServiceName { + val := serviceName.AsString() + if serviceNamespace, ok := resourceAttrs.Get(conventions.AttributeServiceNamespace); ok { + val = fmt.Sprintf("%s/%s", serviceNamespace.AsString(), val) + } + l[model.JobLabel] = val + } + + // Map service.instance.id to instance + if haveInstanceID { + l[model.InstanceLabel] = instance.AsString() + } + + for i := 0; i < len(extras); i += 2 { + if i+1 >= len(extras) { + break + } + _, found := l[extras[i]] + if found && logOnOverwrite { + level.Info(c.logger).Log( + "msg", "label "+extras[i]+" is overwritten. Check if Prometheus reserved labels are used.", + ) + } + // internal labels should be maintained + name := extras[i] + if !(len(name) > 4 && name[:2] == "__" && name[len(name)-2:] == "__") { + name = prometheustranslator.NormalizeLabel(name) + } + l[name] = extras[i+1] + } + + return ppmodel.LabelSetFromMap(l) +} + +// +// help +// + +// ByLabelName enables the usage of sort.Sort() with a slice of labels. +type ByLabelName []ppmodel.SimpleLabel + +func (a ByLabelName) Len() int { return len(a) } +func (a ByLabelName) Less(i, j int) bool { return a[i].Name < a[j].Name } +func (a ByLabelName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// mostRecentTimestampInMetric returns the latest timestamp in a batch of metrics. +func mostRecentTimestampInMetric(metric pmetric.Metric) pcommon.Timestamp { + var ts pcommon.Timestamp + // handle individual metric based on type + //exhaustive:enforce + switch metric.Type() { + case pmetric.MetricTypeGauge: + dataPoints := metric.Gauge().DataPoints() + for x := 0; x < dataPoints.Len(); x++ { + ts = max(ts, dataPoints.At(x).Timestamp()) + } + case pmetric.MetricTypeSum: + dataPoints := metric.Sum().DataPoints() + for x := 0; x < dataPoints.Len(); x++ { + ts = max(ts, dataPoints.At(x).Timestamp()) + } + case pmetric.MetricTypeHistogram: + dataPoints := metric.Histogram().DataPoints() + for x := 0; x < dataPoints.Len(); x++ { + ts = max(ts, dataPoints.At(x).Timestamp()) + } + case pmetric.MetricTypeExponentialHistogram: + dataPoints := metric.ExponentialHistogram().DataPoints() + for x := 0; x < dataPoints.Len(); x++ { + ts = max(ts, dataPoints.At(x).Timestamp()) + } + case pmetric.MetricTypeSummary: + dataPoints := metric.Summary().DataPoints() + for x := 0; x < dataPoints.Len(); x++ { + ts = max(ts, dataPoints.At(x).Timestamp()) + } + } + return ts +} + +// isValidAggregationTemporality checks whether an OTel metric has a valid +// aggregation temporality for conversion to a Prometheus metric. +func isValidAggregationTemporality(metric pmetric.Metric) bool { + //exhaustive:enforce + switch metric.Type() { + case pmetric.MetricTypeGauge, pmetric.MetricTypeSummary: + return true + case pmetric.MetricTypeSum: + return metric.Sum().AggregationTemporality() == pmetric.AggregationTemporalityCumulative + case pmetric.MetricTypeHistogram: + return metric.Histogram().AggregationTemporality() == pmetric.AggregationTemporalityCumulative + case pmetric.MetricTypeExponentialHistogram: + return metric.ExponentialHistogram().AggregationTemporality() == pmetric.AggregationTemporalityCumulative + } + return false +} + +// convertTimeStamp converts OTLP timestamp in ns to timestamp in ms. +func convertTimeStamp(timestamp pcommon.Timestamp) int64 { + return timestamp.AsTime().UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond)) +} + +// createLabels create labelset from base. +func createLabels(name string, baseLabels ppmodel.LabelSet, extras ...string) ppmodel.LabelSet { + extraLabelCount := len(extras) / 2 + builder := ppmodel.NewLabelSetSimpleBuilderSize(baseLabels.Len() + extraLabelCount + 1) // +1 for name + + baseLabels.Range(func(name, value string) bool { + builder.Add(name, value) + + return true + }) + + n := len(extras) + n -= n % 2 + for extrasIdx := 0; extrasIdx < n; extrasIdx += 2 { + builder.Set(extras[extrasIdx], extras[extrasIdx+1]) + } + + builder.Set(model.MetricNameLabel, name) + + return builder.Build() +} diff --git a/pp-pkg/handler/pp_converter_test.go b/pp-pkg/handler/pp_converter_test.go new file mode 100644 index 0000000000..57be3860fa --- /dev/null +++ b/pp-pkg/handler/pp_converter_test.go @@ -0,0 +1,158 @@ +package handler_test + +import ( + "fmt" + "slices" + "strings" + "testing" + "time" + + "github.com/go-kit/log" + "github.com/stretchr/testify/suite" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" + + "github.com/prometheus/prometheus/pp-pkg/handler" + ppmodel "github.com/prometheus/prometheus/pp/go/model" + "github.com/prometheus/prometheus/prompb" + "github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite" +) + +type PPConverterSuite struct { + suite.Suite +} + +func TestPPConverterSuite(t *testing.T) { + suite.Run(t, new(PPConverterSuite)) +} + +func (s *PPConverterSuite) TestHappyPath() { + payload := createExportRequest(5, 5, 5, 5, 5) + + converter := prometheusremotewrite.NewPrometheusConverter() + s.Require().NoError(converter.FromMetrics( + payload.Metrics(), + prometheusremotewrite.Settings{AddMetricSuffixes: true}, + )) + + expected := []ppmodel.TimeSeries{} + + b := ppmodel.NewLabelSetSimpleBuilder() + for _, ts := range converter.TimeSeries() { + ls := labelProtosToLabels(b, ts.Labels) + + for _, s := range ts.Samples { + expected = append(expected, ppmodel.TimeSeries{ + LabelSet: ls, + Timestamp: uint64(s.Timestamp), + Value: s.Value, + }) + } + } + slices.SortFunc( + expected, + func(a, b ppmodel.TimeSeries) int { + return strings.Compare(a.LabelSet.String(), b.LabelSet.String()) + }, + ) + + ppconverter := handler.NewPPConverter(log.NewNopLogger(), payload.Metrics().MetricCount()) + s.Require().NoError(ppconverter.FromMetrics(payload.Metrics())) + + actual := ppconverter.TimeSeries().TimeSeries() + + s.Require().Len(actual, len(expected)) + for i := range expected { + s.Equal(expected[i].LabelSet.String(), actual[i].LabelSet.String()) + s.Equal(expected[i].Timestamp, actual[i].Timestamp) + s.Equal(expected[i].Value, actual[i].Value) + } +} + +func createExportRequest( + resourceAttributeCount, + histogramCount, + nonHistogramCount, + labelsPerMetric, + exemplarsPerSeries int, +) pmetricotlp.ExportRequest { + request := pmetricotlp.NewExportRequest() + + rm := request.Metrics().ResourceMetrics().AppendEmpty() + generateAttributes(rm.Resource().Attributes(), "resource", resourceAttributeCount) + + metrics := rm.ScopeMetrics().AppendEmpty().Metrics() + ts := pcommon.NewTimestampFromTime(time.Now()) + + for i := 1; i <= histogramCount; i++ { + m := metrics.AppendEmpty() + m.SetEmptyHistogram() + m.SetName(fmt.Sprintf("histogram-%v", i)) + m.Histogram().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + h := m.Histogram().DataPoints().AppendEmpty() + h.SetTimestamp(ts) + + // Set 50 samples, 10 each with values 0.5, 1, 2, 4, and 8 + h.SetCount(50) + h.SetSum(155) + h.BucketCounts().FromRaw([]uint64{10, 10, 10, 10, 10, 0}) + // Bucket boundaries include the upper limit (ie. each sample is on the upper limit of its bucket) + h.ExplicitBounds().FromRaw([]float64{.5, 1, 2, 4, 8, 16}) + + generateAttributes(h.Attributes(), "series", labelsPerMetric) + generateExemplars(h.Exemplars(), exemplarsPerSeries, ts) + } + + for i := 1; i <= nonHistogramCount; i++ { + m := metrics.AppendEmpty() + m.SetEmptySum() + m.SetName(fmt.Sprintf("sum-%v", i)) + m.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + point := m.Sum().DataPoints().AppendEmpty() + point.SetTimestamp(ts) + point.SetDoubleValue(1.23) + generateAttributes(point.Attributes(), "series", labelsPerMetric) + generateExemplars(point.Exemplars(), exemplarsPerSeries, ts) + } + + for i := 1; i <= nonHistogramCount; i++ { + m := metrics.AppendEmpty() + m.SetEmptyGauge() + m.SetName(fmt.Sprintf("gauge-%v", i)) + point := m.Gauge().DataPoints().AppendEmpty() + point.SetTimestamp(ts) + point.SetDoubleValue(1.23) + generateAttributes(point.Attributes(), "series", labelsPerMetric) + generateExemplars(point.Exemplars(), exemplarsPerSeries, ts) + } + + return request +} + +func generateAttributes(m pcommon.Map, prefix string, count int) { + for i := 1; i <= count; i++ { + m.PutStr(fmt.Sprintf("%v-name-%v", prefix, i), fmt.Sprintf("value-%v", i)) + } +} + +func generateExemplars(exemplars pmetric.ExemplarSlice, count int, ts pcommon.Timestamp) { + for i := 1; i <= count; i++ { + e := exemplars.AppendEmpty() + e.SetTimestamp(ts) + e.SetDoubleValue(2.22) + e.SetSpanID(pcommon.SpanID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}) + e.SetTraceID(pcommon.TraceID{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + }) + } +} + +func labelProtosToLabels(b *ppmodel.LabelSetSimpleBuilder, labelPairs []prompb.Label) ppmodel.LabelSet { + b.Reset() + for _, l := range labelPairs { + b.Add(l.Name, l.Value) + } + + return b.Build() +} diff --git a/pp/go/model/labelset.go b/pp/go/model/labelset.go index dd454ee27f..716230b391 100644 --- a/pp/go/model/labelset.go +++ b/pp/go/model/labelset.go @@ -8,6 +8,7 @@ import ( "strings" "unsafe" + "github.com/cespare/xxhash/v2" "github.com/mailru/easyjson/jwriter" ) @@ -163,6 +164,15 @@ func (ls LabelSet) ToMap() map[string]string { return m } +// Range calls f on each pair label name, label value. +func (ls LabelSet) Range(f func(name, value string) bool) { + for i := 0; i < ls.Len(); i++ { + if !f(ls.Key(i), ls.Value(i)) { + return + } + } +} + // With return LabelSet with label key-value func (ls LabelSet) With(key, value string) LabelSet { i, ok := ls.get(key) @@ -373,6 +383,11 @@ func (ls *LabelSet) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } +// Hash returns a hash value for the label set. +func (ls LabelSet) Hash() uint64 { + return xxhash.Sum64(ls.data) +} + func (ls *LabelSet) append(key, value string) { dKey := delegatedStringView{int32(len(ls.data)), int32(len(key))} ls.data = append(ls.data, []byte(key)...) @@ -527,7 +542,6 @@ func (builder *LabelSetSimpleBuilder) Add(name, value string) { builder.pairs = append(builder.pairs, SimpleLabel{Name: name, Value: value}) n := len(builder.pairs) builder.sorted = builder.sorted && (n > 1 && builder.pairs[n-1].Name > builder.pairs[n-2].Name) - } // Set the name/value pair as a label. A value of "" means delete that label. diff --git a/web/api/v1/api.go b/web/api/v1/api.go index 6d5dd566f4..130e3edd6d 100644 --- a/web/api/v1/api.go +++ b/web/api/v1/api.go @@ -306,7 +306,7 @@ func NewAPI( a.opHandler = handler.NewOpHandler(receiver, logger, registerer) // PP_CHANGES.md: rebuild on cpp } if otlpEnabled { - a.otlpWriteHandler = remote.NewOTLPWriteHandler(logger, ap) + a.otlpWriteHandler = handler.NewOTLPWriteHandler(logger, receiver) // PP_CHANGES.md: rebuild on cpp } return a From 2f5e472ddc6be3bed1cd02a7b39335670497becc Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 22 Apr 2025 12:56:51 +0000 Subject: [PATCH 31/90] add test --- pp/go/model/labelset.go | 2 +- pp/go/model/labelset_test.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pp/go/model/labelset.go b/pp/go/model/labelset.go index 716230b391..9b2e5347c4 100644 --- a/pp/go/model/labelset.go +++ b/pp/go/model/labelset.go @@ -165,7 +165,7 @@ func (ls LabelSet) ToMap() map[string]string { } // Range calls f on each pair label name, label value. -func (ls LabelSet) Range(f func(name, value string) bool) { +func (ls LabelSet) Range(f func(lname, lvalue string) bool) { for i := 0; i < ls.Len(); i++ { if !f(ls.Key(i), ls.Value(i)) { return diff --git a/pp/go/model/labelset_test.go b/pp/go/model/labelset_test.go index 0fe486a89b..374a2ad31a 100644 --- a/pp/go/model/labelset_test.go +++ b/pp/go/model/labelset_test.go @@ -220,6 +220,25 @@ func (s *LabelSetSuite) TestLabelSet_UnmarshalYAML_Quick() { s.Require().NoError(quick.CheckEqual(identity, convertation, nil)) } +func (s *LabelSetSuite) TestLabelSet_Range() { + lsMap := map[string]string{ + "__name__": "example", + "instance": "instance", + "job": "test", + "container": "~unknown", + "flags": "empty", + } + ls := model.LabelSetFromMap(lsMap) + + ls.Range(func(lname, lvalue string) bool { + value, ok := lsMap[lname] + s.Require().True(ok) + s.Require().Equal(value, lvalue) + + return true + }) +} + func (s *LabelSetSuite) TestLabelSetSimpleBuilder_Build() { labels := []model.SimpleLabel{ {"__name__", "example"}, From 34c21b749ce64619b3f049a364e01c0aef9abe85 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Wed, 23 Apr 2025 11:28:28 +0000 Subject: [PATCH 32/90] for save --- cmd/prometheus/main.go | 6 ++- model/labels/labels_stringlabels.go | 63 +++++++++++++++++++++------- pp-pkg/receiver/receiver.go | 4 ++ pp/entrypoint/primitives_lss.cpp | 16 +++++++ pp/entrypoint/primitives_lss.h | 2 + pp/go/cppbridge/entrypoint.go | 18 ++++++++ pp/go/cppbridge/entrypoint.h | 2 + pp/go/cppbridge/primitives_lss.go | 4 ++ pp/go/relabeler/appender/appender.go | 7 ++++ pp/go/relabeler/appender/head.go | 9 ++++ pp/go/relabeler/head/head.go | 37 ++++++++++++++++ pp/go/relabeler/head/manager/head.go | 5 +++ pp/go/relabeler/head/shard.go | 12 +++++- pp/go/relabeler/interface.go | 3 ++ 14 files changed, 171 insertions(+), 17 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index bde5572d37..e775f6f7ee 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -712,9 +712,9 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - BlockDuration: 15 * time.Minute, + // BlockDuration: 15 * time.Minute, Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), - // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), + BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), }, headCatalog, reloadBlocksTriggerNotifier, @@ -730,6 +730,8 @@ func main() { os.Exit(1) } + labels.CurReceiver = receiver + remoteWriterReadyNotifier := ready.NewNotifiableNotifier() remoteWriter := remotewriter.New(dataDir, headCatalog, clock, remoteWriterReadyNotifier, prometheus.DefaultRegisterer) diff --git a/model/labels/labels_stringlabels.go b/model/labels/labels_stringlabels.go index 879175c985..a14de97cfb 100644 --- a/model/labels/labels_stringlabels.go +++ b/model/labels/labels_stringlabels.go @@ -482,7 +482,7 @@ func (b *Builder) Reset(base Labels) { func (b *Builder) Labels() Labels { if len(b.del) == 0 && len(b.add) == 0 { - ul.add(b.base.Hash()) + ul.add(b.base) return b.base } @@ -519,7 +519,7 @@ func (b *Builder) Labels() Labels { } ret := Labels{data: yoloString(buf)} - ul.add(ret.Hash()) + ul.add(ret) return ret } @@ -672,7 +672,7 @@ func (b *ScratchBuilder) Labels() Labels { b.output = Labels{data: yoloString(buf)} } - ul.add(b.output.Hash()) + ul.add(b.output) return b.output } @@ -688,7 +688,7 @@ func (b *ScratchBuilder) Overwrite(ls *Labels) { marshalLabelsToSizedBuffer(b.add, b.overwriteBuffer) ls.data = yoloString(b.overwriteBuffer) - ul.add(ls.Hash()) + ul.add(*ls) } // Symbol-table is no-op, just for api parity with dedupelabels. @@ -716,26 +716,61 @@ func (b *ScratchBuilder) SetSymbolTable(_ *SymbolTable) { // uniqLables // -var ul = &uniqLables{ - counter: promauto.NewCounter( - prometheus.CounterOpts{ - Name: "prompp_labels_unique_total", - Help: "Current unique labels.", - }, - ), -} +var ( + ul = &uniqLables{ + counter: promauto.NewCounter( + prometheus.CounterOpts{ + Name: "prompp_labels_unique_total", + Help: "Current unique labels.", + }, + ), + // size: promauto.NewCounter( + // prometheus.CounterOpts{ + // Name: "prompp_labels_unique_size", + // Help: "Current unique labels size.", + // }, + // ), + size: promauto.NewHistogram( + prometheus.HistogramOpts{ + Name: "prompp_labels_unique_size", + Help: "Current unique labels size", + Buckets: []float64{ + 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, + 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000, + }, + }, + ), + } + + CurReceiver Receiver +) type uniqLables struct { counter prometheus.Counter + size prometheus.Histogram sync.Map } -func (ul *uniqLables) add(hash uint64) { - _, loaded := ul.Map.LoadOrStore(hash, nil) +func (ul *uniqLables) add(ls Labels) { + if CurReceiver == nil { + return + } + + if CurReceiver.Find(ls) { + return + } + + _, loaded := ul.Map.LoadOrStore(ls.Hash(), nil) if loaded { return } ul.counter.Inc() + // ul.size.Add(float64(len(ls.data))) + ul.size.Observe(float64(len(ls.data))) +} + +type Receiver interface { + Find(ls Labels) bool } diff --git a/pp-pkg/receiver/receiver.go b/pp-pkg/receiver/receiver.go index 058c5fa78c..edd5fa5ab6 100644 --- a/pp-pkg/receiver/receiver.go +++ b/pp-pkg/receiver/receiver.go @@ -250,6 +250,10 @@ func NewReceiver( return r, nil } +func (rr *Receiver) Find(ls labels.Labels) bool { + return rr.appender.Find(ls) +} + // AppendSnappyProtobuf append compressed via snappy Protobuf data to relabeling hashdex data. func (rr *Receiver) AppendSnappyProtobuf( ctx context.Context, diff --git a/pp/entrypoint/primitives_lss.cpp b/pp/entrypoint/primitives_lss.cpp index 7cf9d8e881..ad1183d5b6 100644 --- a/pp/entrypoint/primitives_lss.cpp +++ b/pp/entrypoint/primitives_lss.cpp @@ -65,6 +65,22 @@ extern "C" void prompp_primitives_lss_find_or_emplace(void* args, void* res) { *in->lss)}; } +extern "C" void prompp_primitives_lss_find(void* args, void* res) { + struct Arguments { + LssVariantPtr lss; + PromPP::Primitives::Go::LabelSet label_set; + }; + struct Result { + bool has; + }; + + auto in = static_cast(args); + auto& lss = std::get(*in->lss); + std::optional ls_id = lss.find(in->label_set); + + new (res) Result{.has = ls_id.has_value()}; +} + struct LssQueryResult { PromPP::Primitives::Go::Slice matches; PromPP::Primitives::Go::Slice label_set_lengths; diff --git a/pp/entrypoint/primitives_lss.h b/pp/entrypoint/primitives_lss.h index acad0fa639..28105e9bc8 100644 --- a/pp/entrypoint/primitives_lss.h +++ b/pp/entrypoint/primitives_lss.h @@ -51,6 +51,8 @@ void prompp_primitives_lss_allocated_memory(void* args, void* res); */ void prompp_primitives_lss_find_or_emplace(void* args, void* res); +void prompp_primitives_lss_find(void* args, void* res); + /** * @brief query series from lss * diff --git a/pp/go/cppbridge/entrypoint.go b/pp/go/cppbridge/entrypoint.go index 190d237a3b..6021037643 100644 --- a/pp/go/cppbridge/entrypoint.go +++ b/pp/go/cppbridge/entrypoint.go @@ -1138,6 +1138,24 @@ func primitivesLSSFindOrEmplace(lss uintptr, labelSet model.LabelSet) uint32 { return res.labelSetID } +func primitivesLSSFind(lss uintptr, labelSet model.LabelSet) bool { + args := struct { + lss uintptr + labelSet model.LabelSet + }{lss, labelSet} + var res struct { + has bool + } + + fastcgo.UnsafeCall2( + C.prompp_primitives_lss_find, + uintptr(unsafe.Pointer(&args)), + uintptr(unsafe.Pointer(&res)), + ) + + return res.has +} + func primitivesLSSQuery(lss uintptr, matchers []model.LabelMatcher, querySource uint32) ( matches []uint32, labelSetLengths []uint16, diff --git a/pp/go/cppbridge/entrypoint.h b/pp/go/cppbridge/entrypoint.h index 8311361bee..3fb190e900 100755 --- a/pp/go/cppbridge/entrypoint.h +++ b/pp/go/cppbridge/entrypoint.h @@ -420,6 +420,8 @@ void prompp_primitives_lss_allocated_memory(void* args, void* res); */ void prompp_primitives_lss_find_or_emplace(void* args, void* res); +void prompp_primitives_lss_find(void* args, void* res); + /** * @brief query series from lss * diff --git a/pp/go/cppbridge/primitives_lss.go b/pp/go/cppbridge/primitives_lss.go index 313b636911..817a419c20 100644 --- a/pp/go/cppbridge/primitives_lss.go +++ b/pp/go/cppbridge/primitives_lss.go @@ -124,6 +124,10 @@ func (lss *LabelSetStorage) FindOrEmplace(labelSet model.LabelSet) uint32 { return id } +func (lss *LabelSetStorage) Find(labelSet model.LabelSet) bool { + return primitivesLSSFind(lss.pointer, labelSet) +} + // Query returns a LSSQueryResult that matches the given label matchers. func (lss *LabelSetStorage) Query(matchers []model.LabelMatcher, querySource uint32) *LSSQueryResult { return newLSSQueryResult(primitivesLSSQuery(lss.pointer, matchers, querySource)) diff --git a/pp/go/relabeler/appender/appender.go b/pp/go/relabeler/appender/appender.go index fdbe732f2c..81d636c92a 100644 --- a/pp/go/relabeler/appender/appender.go +++ b/pp/go/relabeler/appender/appender.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/pp/go/cppbridge" "github.com/prometheus/prometheus/pp/go/relabeler" "github.com/prometheus/prometheus/pp/go/relabeler/querier" @@ -155,3 +156,9 @@ func (qa *QueryableAppender) Close() error { defer qa.lock.Unlock() return errors.Join(qa.head.CommitToWal(), qa.head.Flush(), qa.head.Close()) } + +func (qa *QueryableAppender) Find(ls labels.Labels) bool { + qa.lock.Lock() + defer qa.lock.Unlock() + return qa.head.Find(ls) +} diff --git a/pp/go/relabeler/appender/head.go b/pp/go/relabeler/appender/head.go index 9f07f6c6e5..55f26656ef 100644 --- a/pp/go/relabeler/appender/head.go +++ b/pp/go/relabeler/appender/head.go @@ -3,6 +3,7 @@ package appender import ( "context" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/pp/go/relabeler/logger" "github.com/prometheus/prometheus/pp/go/cppbridge" @@ -176,6 +177,10 @@ func (h *RotatableHead) Discard() error { return h.head.Discard() } +func (h *RotatableHead) Find(ls labels.Labels) bool { + return h.head.Find(ls) +} + type HeapProfileWriter interface { WriteHeapProfile() error } @@ -270,3 +275,7 @@ func (h *HeapProfileWritableHead) Discard() error { func NewHeapProfileWritableHead(head relabeler.Head, heapProfileWriter HeapProfileWriter) *HeapProfileWritableHead { return &HeapProfileWritableHead{head: head, heapProfileWriter: heapProfileWriter} } + +func (h *HeapProfileWritableHead) Find(ls labels.Labels) bool { + return h.head.Find(ls) +} diff --git a/pp/go/relabeler/head/head.go b/pp/go/relabeler/head/head.go index 0ff43c7c2f..5e483489c3 100644 --- a/pp/go/relabeler/head/head.go +++ b/pp/go/relabeler/head/head.go @@ -11,6 +11,7 @@ import ( "sync" "sync/atomic" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/pp/go/relabeler/logger" "github.com/prometheus/client_golang/prometheus" @@ -180,6 +181,10 @@ type Head struct { walSize *prometheus.GaugeVec stopc chan struct{} wg *sync.WaitGroup + + // working sync.Map + // counter *prometheus.CounterVec + // size *prometheus.CounterVec } func New( @@ -247,6 +252,21 @@ func New( }, []string{"shard_id"}, ), + // working: sync.Map{}, + // counter: factory.NewCounterVec( + // prometheus.CounterOpts{ + // Name: "prompp_head_labels_unique_total", + // Help: "Current head unique labels size", + // }, + // []string{"id"}, + // ), + // size: factory.NewCounterVec( + // prometheus.CounterOpts{ + // Name: "prompp_head_labels_unique_size", + // Help: "Current head unique labels size", + // }, + // []string{"id"}, + // ), } if err := h.reconfigure(inputRelabelerConfigs, numberOfShards); err != nil { @@ -397,6 +417,9 @@ func (h *Head) Stop() { }) } h.relabelersData = nil + + // h.counter.DeletePartialMatch(prometheus.Labels{"id": h.id}) + // h.size.DeletePartialMatch(prometheus.Labels{"id": h.id}) } func (h *Head) Reconfigure(inputRelabelerConfigs []*config.InputRelabelerConfig, numberOfShards uint16) error { @@ -620,3 +643,17 @@ func (h *Head) stop() { h.wg.Wait() h.stopc = make(chan struct{}) } + +func (h *Head) Find(ls labels.Labels) bool { + var find uint32 + + _ = h.ForEachShard(func(shard relabeler.Shard) error { + if shard.LSS().Find(ls) { + atomic.AddUint32(&find, 1) + } + + return nil + }) + + return find != 0 +} diff --git a/pp/go/relabeler/head/manager/head.go b/pp/go/relabeler/head/manager/head.go index ceaabe52db..72e6a9289c 100644 --- a/pp/go/relabeler/head/manager/head.go +++ b/pp/go/relabeler/head/manager/head.go @@ -4,6 +4,7 @@ import ( "context" "errors" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/pp/go/cppbridge" "github.com/prometheus/prometheus/pp/go/relabeler" "github.com/prometheus/prometheus/pp/go/relabeler/config" @@ -114,3 +115,7 @@ func (h *DiscardableRotatableHead) Discard() (err error) { } return err } + +func (h *DiscardableRotatableHead) Find(ls labels.Labels) bool { + return h.head.Find(ls) +} diff --git a/pp/go/relabeler/head/shard.go b/pp/go/relabeler/head/shard.go index 98d4f16496..03c7d5424f 100644 --- a/pp/go/relabeler/head/shard.go +++ b/pp/go/relabeler/head/shard.go @@ -3,11 +3,12 @@ package head import ( "fmt" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/pp/go/cppbridge" "github.com/prometheus/prometheus/pp/go/model" "github.com/prometheus/prometheus/pp/go/relabeler" "github.com/prometheus/prometheus/pp/go/relabeler/config" - "github.com/prometheus/client_golang/prometheus" ) const chanBufferSize = 64 @@ -44,6 +45,15 @@ func (w *LSS) GetLabelSets(labelSetIDs []uint32) *cppbridge.LabelSetStorageGetLa return w.target.GetLabelSets(labelSetIDs) } +func (w *LSS) Find(ls labels.Labels) bool { + builder := model.NewLabelSetSimpleBuilderSize(ls.Len()) + ls.Range(func(l labels.Label) { + builder.Add(l.Name, l.Value) + }) + + return w.target.Find(builder.Build()) +} + type DataStorage struct { dataStorage *cppbridge.HeadDataStorage encoder *cppbridge.HeadEncoder diff --git a/pp/go/relabeler/interface.go b/pp/go/relabeler/interface.go index e9aac26e3c..3f37621ae3 100644 --- a/pp/go/relabeler/interface.go +++ b/pp/go/relabeler/interface.go @@ -4,6 +4,7 @@ import ( "context" "sync/atomic" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/pp/go/cppbridge" "github.com/prometheus/prometheus/pp/go/model" "github.com/prometheus/prometheus/pp/go/relabeler/config" @@ -25,6 +26,7 @@ type LSS interface { QueryLabelNames(matchers []model.LabelMatcher) *cppbridge.LSSQueryLabelNamesResult Query(matchers []model.LabelMatcher, querySource uint32) *cppbridge.LSSQueryResult GetLabelSets(labelSetIDs []uint32) *cppbridge.LabelSetStorageGetLabelSetsResult + Find(ls labels.Labels) bool } type Wal interface { @@ -74,6 +76,7 @@ type Head interface { Close() error Discard() error String() string + Find(ls labels.Labels) bool } type Distributor interface { From b2bf93981e01a03f63aa6886b9c5f461775b3b68 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Wed, 23 Apr 2025 13:48:37 +0000 Subject: [PATCH 33/90] for save --- model/labels/labels_stringlabels.go | 2 ++ pp-pkg/receiver/receiver.go | 2 +- pp/go/relabeler/appender/storage.go | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/model/labels/labels_stringlabels.go b/model/labels/labels_stringlabels.go index a14de97cfb..1ea977efe2 100644 --- a/model/labels/labels_stringlabels.go +++ b/model/labels/labels_stringlabels.go @@ -769,6 +769,8 @@ func (ul *uniqLables) add(ls Labels) { ul.counter.Inc() // ul.size.Add(float64(len(ls.data))) ul.size.Observe(float64(len(ls.data))) + + // fmt.Println("=== " + ls.String() + " ===") } type Receiver interface { diff --git a/pp-pkg/receiver/receiver.go b/pp-pkg/receiver/receiver.go index edd5fa5ab6..f630a9290f 100644 --- a/pp-pkg/receiver/receiver.go +++ b/pp-pkg/receiver/receiver.go @@ -251,7 +251,7 @@ func NewReceiver( } func (rr *Receiver) Find(ls labels.Labels) bool { - return rr.appender.Find(ls) + return rr.appender.Find(ls) || rr.storage.Find(ls) } // AppendSnappyProtobuf append compressed via snappy Protobuf data to relabeling hashdex data. diff --git a/pp/go/relabeler/appender/storage.go b/pp/go/relabeler/appender/storage.go index 2f861585e0..52c19e85e6 100644 --- a/pp/go/relabeler/appender/storage.go +++ b/pp/go/relabeler/appender/storage.go @@ -8,6 +8,7 @@ import ( "github.com/jonboulle/clockwork" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/pp/go/relabeler" "github.com/prometheus/prometheus/pp/go/relabeler/block" "github.com/prometheus/prometheus/pp/go/relabeler/logger" @@ -281,3 +282,20 @@ func (qs *QueryableStorage) shrink(persisted ...string) { type noOpWriteNotifier struct{} func (noOpWriteNotifier) NotifyWritten() {} + +func (qs *QueryableStorage) Find(ls labels.Labels) bool { + qs.mtx.Lock() + heads := make([]relabeler.Head, len(qs.heads)) + copy(heads, qs.heads) + qs.mtx.Unlock() + + var find bool + + for _, head := range heads { + if head.Find(ls) { + find = true + } + } + + return find +} From 39e65a59d32a58181dc22caf5145bb3cdcc51121 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Thu, 24 Apr 2025 09:30:49 +0000 Subject: [PATCH 34/90] for save --- cmd/prometheus/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index e775f6f7ee..fb79132054 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -730,7 +730,9 @@ func main() { os.Exit(1) } - labels.CurReceiver = receiver + time.AfterFunc(3*time.Minute, func() { + labels.CurReceiver = receiver + }) remoteWriterReadyNotifier := ready.NewNotifiableNotifier() remoteWriter := remotewriter.New(dataDir, headCatalog, clock, remoteWriterReadyNotifier, prometheus.DefaultRegisterer) From 9c343f1b6ba90daa4e46f4e350ed3936f7390a9e Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 6 May 2025 09:02:35 +0000 Subject: [PATCH 35/90] add go implementation --- pp/go/relabeler/appender/appender_test.go | 63 +++++++- pp/go/relabeler/appender/head.go | 10 +- pp/go/relabeler/head/builder.go | 36 +++-- pp/go/relabeler/head/load.go | 67 ++++++++- pp/go/relabeler/head/manager/manager.go | 172 ++++++++++++++-------- pp/series_data/querier/instant_querier.h | 2 +- 6 files changed, 263 insertions(+), 87 deletions(-) diff --git a/pp/go/relabeler/appender/appender_test.go b/pp/go/relabeler/appender/appender_test.go index 3c51ba6a94..1114ec908a 100644 --- a/pp/go/relabeler/appender/appender_test.go +++ b/pp/go/relabeler/appender/appender_test.go @@ -48,7 +48,7 @@ func (s *AppenderSuite) SetupSuite() { s.errorHandler() } -func (s *AppenderSuite) TearDownSuite() { +func (*AppenderSuite) TearDownSuite() { logger.Unset() } @@ -1484,7 +1484,7 @@ func (s *AppenderSuite) TestManagerRelabelerRelabelingWithRotate() { s.Require().NoError(err) tmpDirsToRemove = append(tmpDirsToRemove, tmpDir) - var generation uint64 = 0 + var generation uint64 headID := "head_id" hd, err := head.Create(headID, generation, tmpDir, inputRelabelerConfigs, numberOfShards, 0, head.NoOpLastAppendedSegmentIDSetter{}, prometheus.DefaultRegisterer) @@ -1503,7 +1503,39 @@ func (s *AppenderSuite) TestManagerRelabelerRelabelingWithRotate() { tmpDirsToRemove = append(tmpDirsToRemove, newDir) newHeadID := "head_id" generation++ - newHead, buildErr := head.Create(newHeadID, generation, newDir, inputRelabelerConfigs, numberOfShards, 0, head.NoOpLastAppendedSegmentIDSetter{}, prometheus.DefaultRegisterer) + newHead, buildErr := head.Create( + newHeadID, + generation, + newDir, + inputRelabelerConfigs, + numberOfShards, + 0, + head.NoOpLastAppendedSegmentIDSetter{}, + prometheus.DefaultRegisterer, + ) + s.Require().NoError(buildErr) + headsToClose = append(headsToClose, newHead) + return newHead, nil + }, + func( + targetLsses []*cppbridge.LabelSetStorage, + inputRelabelerConfigs []*config.InputRelabelerConfig, + ) (relabeler.Head, error) { + newDir, buildErr := os.MkdirTemp("", "appender_test") + s.Require().NoError(buildErr) + tmpDirsToRemove = append(tmpDirsToRemove, newDir) + generation++ + newHeadID := fmt.Sprintf("head_id_%d", generation) + newHead, buildErr := head.CreateWithLSS( + newHeadID, + generation, + newDir, + inputRelabelerConfigs, + targetLsses, + 0, + head.NoOpLastAppendedSegmentIDSetter{}, + prometheus.DefaultRegisterer, + ) s.Require().NoError(buildErr) headsToClose = append(headsToClose, newHead) return newHead, nil @@ -2391,7 +2423,7 @@ func (s *AppenderSuite) TestManagerRelabelerRelabelingWithRotateWithStaleNans() s.Require().NoError(err) tmpDirsToRemove = append(tmpDirsToRemove, tmpDir) - var generation uint64 = 0 + var generation uint64 headID := fmt.Sprintf("head_id_%d", generation) hd, err := head.Create(headID, generation, tmpDir, inputRelabelerConfigs, numberOfShards, 0, head.NoOpLastAppendedSegmentIDSetter{}, prometheus.DefaultRegisterer) @@ -2415,6 +2447,29 @@ func (s *AppenderSuite) TestManagerRelabelerRelabelingWithRotateWithStaleNans() headsToClose = append(headsToClose, newHead) return newHead, nil }, + func( + targetLsses []*cppbridge.LabelSetStorage, + inputRelabelerConfigs []*config.InputRelabelerConfig, + ) (relabeler.Head, error) { + newDir, buildErr := os.MkdirTemp("", "appender_test") + s.Require().NoError(buildErr) + tmpDirsToRemove = append(tmpDirsToRemove, newDir) + generation++ + newHeadID := fmt.Sprintf("head_id_%d", generation) + newHead, buildErr := head.CreateWithLSS( + newHeadID, + generation, + newDir, + inputRelabelerConfigs, + targetLsses, + 0, + head.NoOpLastAppendedSegmentIDSetter{}, + prometheus.DefaultRegisterer, + ) + s.Require().NoError(buildErr) + headsToClose = append(headsToClose, newHead) + return newHead, nil + }, ) rotatableHead := appender.NewRotatableHead(hd, noOpStorage{}, builder, appender.NoOpHeadActivator{}) diff --git a/pp/go/relabeler/appender/head.go b/pp/go/relabeler/appender/head.go index 9f07f6c6e5..b2ad049f8f 100644 --- a/pp/go/relabeler/appender/head.go +++ b/pp/go/relabeler/appender/head.go @@ -17,7 +17,7 @@ type Storage interface { // HeadBuilder - head builder. type HeadBuilder interface { - Build() (relabeler.Head, error) + BuildWithLSS(targetLsses []*cppbridge.LabelSetStorage) (relabeler.Head, error) BuildWithConfig(inputRelabelerConfigs []*config.InputRelabelerConfig, numberOfShards uint16) (relabeler.Head, error) } @@ -133,7 +133,13 @@ func NewRotatableHead(head relabeler.Head, storage Storage, builder HeadBuilder, // Rotate - relabeler.Head interface implementation. func (h *RotatableHead) Rotate() error { - newHead, err := h.builder.Build() + targetLsses := make([]*cppbridge.LabelSetStorage, h.head.NumberOfShards()) + _ = h.head.ForEachShard(func(shard relabeler.Shard) error { + targetLsses[shard.ShardID()] = shard.LSS().Raw().CopyAddedSeries() + return nil + }) + + newHead, err := h.builder.BuildWithLSS(targetLsses) if err != nil { return err } diff --git a/pp/go/relabeler/head/builder.go b/pp/go/relabeler/head/builder.go index 2edab7c605..1abe655c24 100644 --- a/pp/go/relabeler/head/builder.go +++ b/pp/go/relabeler/head/builder.go @@ -1,6 +1,7 @@ package head import ( + "github.com/prometheus/prometheus/pp/go/cppbridge" "github.com/prometheus/prometheus/pp/go/relabeler" "github.com/prometheus/prometheus/pp/go/relabeler/config" ) @@ -17,22 +18,39 @@ func (fn ConfigSourceFunc) Config() (inputRelabelerConfigs []*config.InputRelabe type BuildFunc func(inputRelabelerConfigs []*config.InputRelabelerConfig, numberOfShards uint16) (relabeler.Head, error) +type BuildWithLSSFunc func( + targetLsses []*cppbridge.LabelSetStorage, + inputRelabelerConfigs []*config.InputRelabelerConfig, +) (relabeler.Head, error) + type Builder struct { - configSource ConfigSource - buildFunc BuildFunc + configSource ConfigSource + buildFunc BuildFunc + buildWithLSSFunc BuildWithLSSFunc } -func NewBuilder(configSource ConfigSource, buildFunc BuildFunc) *Builder { +func NewBuilder(configSource ConfigSource, buildFunc BuildFunc, buildWithLSSFunc BuildWithLSSFunc) *Builder { return &Builder{ - configSource: configSource, - buildFunc: buildFunc, + configSource: configSource, + buildFunc: buildFunc, + buildWithLSSFunc: buildWithLSSFunc, } } -func (b *Builder) Build() (relabeler.Head, error) { - return b.buildFunc(b.configSource.Config()) +// BuildWithConfig build head with incoming config. +func (b *Builder) BuildWithConfig( + inputRelabelerConfigs []*config.InputRelabelerConfig, + numberOfShards uint16, +) (relabeler.Head, error) { + return b.buildFunc(inputRelabelerConfigs, numberOfShards) } -func (b *Builder) BuildWithConfig(inputRelabelerConfigs []*config.InputRelabelerConfig, numberOfShards uint16) (relabeler.Head, error) { - return b.buildFunc(inputRelabelerConfigs, numberOfShards) +// BuildWithLSS head with target lsses. +func (b *Builder) BuildWithLSS(targetLsses []*cppbridge.LabelSetStorage) (relabeler.Head, error) { + inputRelabelerConfigs, numberOfShards := b.configSource.Config() + if uint16(len(targetLsses)) != numberOfShards { //nolint:gosec // no overflow + return b.buildFunc(inputRelabelerConfigs, numberOfShards) + } + + return b.buildWithLSSFunc(targetLsses, inputRelabelerConfigs) } diff --git a/pp/go/relabeler/head/load.go b/pp/go/relabeler/head/load.go index f37b019d52..2675372bb7 100644 --- a/pp/go/relabeler/head/load.go +++ b/pp/go/relabeler/head/load.go @@ -19,6 +19,7 @@ const ( HeadWalEncoderDecoderLogShards uint8 = 0 ) +// Create head. func Create( id string, generation uint64, @@ -56,22 +57,76 @@ func Create( return New(id, generation, configs, lsses, wals, dataStorages, numberOfShards, registerer) } +func CreateWithLSS( + id string, + generation uint64, + dir string, + configs []*config.InputRelabelerConfig, + targetLsses []*cppbridge.LabelSetStorage, + maxSegmentSize uint32, + lastAppendedSegmentIDSetter LastAppendedSegmentIDSetter, + registerer prometheus.Registerer, +) (_ *Head, err error) { + numberOfShards := uint16(len(targetLsses)) + + lsses := make([]*LSS, numberOfShards) + wals := make([]*ShardWal, numberOfShards) + dataStorages := make([]*DataStorage, numberOfShards) + + defer func() { + if err == nil { + return + } + for _, wal := range wals { + if wal != nil { + _ = wal.Close() + } + } + }() + + swn := newSegmentWriteNotifier(numberOfShards, lastAppendedSegmentIDSetter) + + for shardID := uint16(0); shardID < numberOfShards; shardID++ { + lsses[shardID], wals[shardID], dataStorages[shardID], err = createShardWithLSS( + dir, + shardID, + swn, + targetLsses[shardID], + maxSegmentSize, + ) + if err != nil { + return nil, fmt.Errorf("failed to create shard: %w", err) + } + } + + return New(id, generation, configs, lsses, wals, dataStorages, numberOfShards, registerer) +} + +// createShard create shard for head. func createShard( dir string, shardID uint16, swn *segmentWriteNotifier, maxSegmentSize uint32, +) (*LSS, *ShardWal, *DataStorage, error) { + return createShardWithLSS(dir, shardID, swn, cppbridge.NewQueryableLssStorage(), maxSegmentSize) +} + +// createShardWithLSS create shard for head with lss. +func createShardWithLSS( + dir string, + shardID uint16, + swn *segmentWriteNotifier, + targetLss *cppbridge.LabelSetStorage, + maxSegmentSize uint32, ) (*LSS, *ShardWal, *DataStorage, error) { inputLss := cppbridge.NewLssStorage() - targetLss := cppbridge.NewQueryableLssStorage() lss := &LSS{ input: inputLss, target: targetLss, } - shardFilePath := filepath.Join(dir, fmt.Sprintf("shard_%d.wal", shardID)) - var shardFile *os.File - shardFile, err := os.Create(shardFilePath) + shardFile, err := os.Create(filepath.Join(filepath.Clean(dir), fmt.Sprintf("shard_%d.wal", shardID))) if err != nil { return nil, nil, nil, fmt.Errorf("failed to create shard wal file: %w", err) } @@ -253,14 +308,14 @@ func (l *ShardLoader) Load() (result ShardLoadResult) { } numberOfSegments := lastReadSegmentID + 1 - result.NumberOfSegments = uint32(numberOfSegments) + result.NumberOfSegments = uint32(numberOfSegments) // #nosec G115 // no overflow sw, err := newSegmentWriter(l.shardID, shardWalFile, l.notifier) if err != nil { result.Err = err return } - l.notifier.Set(l.shardID, uint32(numberOfSegments)) + l.notifier.Set(l.shardID, uint32(numberOfSegments)) // #nosec G115 // no overflow result.Wal = newShardWal(decoder.CreateEncoder(), l.maxSegmentSize, sw) if result.Err == nil { result.Corrupted = false diff --git a/pp/go/relabeler/head/manager/manager.go b/pp/go/relabeler/head/manager/manager.go index ece70fbd23..ba09e0e4cc 100644 --- a/pp/go/relabeler/head/manager/manager.go +++ b/pp/go/relabeler/head/manager/manager.go @@ -9,34 +9,33 @@ import ( "time" "github.com/jonboulle/clockwork" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/prometheus/pp/go/cppbridge" "github.com/prometheus/prometheus/pp/go/relabeler" "github.com/prometheus/prometheus/pp/go/relabeler/config" "github.com/prometheus/prometheus/pp/go/relabeler/head" "github.com/prometheus/prometheus/pp/go/relabeler/head/catalog" "github.com/prometheus/prometheus/pp/go/relabeler/logger" "github.com/prometheus/prometheus/pp/go/util" - "github.com/prometheus/client_golang/prometheus" ) +// ConfigSource implementation of the config of heads. type ConfigSource interface { Get() (inputRelabelerConfigs []*config.InputRelabelerConfig, numberOfShards uint16) } +// Catalog implementation of the catalog of heads. type Catalog interface { - List(filter func(record *catalog.Record) bool, sortLess func(lhs, rhs *catalog.Record) bool) ([]*catalog.Record, error) + List( + filter func(record *catalog.Record) bool, + sortLess func(lhs, rhs *catalog.Record) bool, + ) ([]*catalog.Record, error) Create(numberOfShards uint16) (*catalog.Record, error) SetStatus(id string, status catalog.Status) (*catalog.Record, error) SetCorrupted(id string) (*catalog.Record, error) } -type metrics struct { - CreatedHeadsCount prometheus.Counter - RotatedHeadsCount prometheus.Counter - CorruptedHeadsCount prometheus.Counter - PersistedHeadsCount prometheus.Counter - DeletedHeadsCount prometheus.Counter -} - +// Manager of heads. type Manager struct { dir string clock clockwork.Clock @@ -48,13 +47,23 @@ type Manager struct { registerer prometheus.Registerer } +// SetLastAppendedSegmentIDFn function to set the last added segment id. type SetLastAppendedSegmentIDFn func(segmentID uint32) +// SetLastAppendedSegmentID to set the last added segment id. func (fn SetLastAppendedSegmentIDFn) SetLastAppendedSegmentID(segmentID uint32) { fn(segmentID) } -func New(dir string, clock clockwork.Clock, configSource ConfigSource, catalog Catalog, maxSegmentSize uint32, registerer prometheus.Registerer) (*Manager, error) { +// New init new Manager. +func New( + dir string, + clock clockwork.Clock, + configSource ConfigSource, + headCatalog Catalog, + maxSegmentSize uint32, + registerer prometheus.Registerer, +) (*Manager, error) { dirStat, err := os.Stat(dir) if err != nil { return nil, fmt.Errorf("failed to stat dir: %w", err) @@ -70,7 +79,7 @@ func New(dir string, clock clockwork.Clock, configSource ConfigSource, catalog C dir: dir, clock: clock, configSource: configSource, - catalog: catalog, + catalog: headCatalog, maxSegmentSize: maxSegmentSize, counter: factory.NewCounterVec( prometheus.CounterOpts{ @@ -83,6 +92,7 @@ func New(dir string, clock clockwork.Clock, configSource ConfigSource, catalog C }, nil } +// HeadLoadResult head loading result. type HeadLoadResult struct { headRecord *catalog.Record head relabeler.Head @@ -91,6 +101,11 @@ type HeadLoadResult struct { err error } +// Restore head from wal. +// +//revive:disable-next-line:cognitive-complexity function is not complicated. +//revive:disable-next-line:function-length long but readable. +//revive:disable-next-line:cyclomatic but readable func (m *Manager) Restore(blockDuration time.Duration) (active relabeler.Head, rotated []relabeler.Head, err error) { headRecords, err := m.catalog.List( func(record *catalog.Record) bool { @@ -111,7 +126,12 @@ func (m *Manager) Restore(blockDuration time.Duration) (active relabeler.Head, r cfgs, _ := m.configSource.Get() generation := m.generation wg.Add(1) - go func(index int, headRecord *catalog.Record, inputRelabelerConfigs []*config.InputRelabelerConfig, generation uint64) { + go func( + index int, + headRecord *catalog.Record, + inputRelabelerConfigs []*config.InputRelabelerConfig, + generation uint64, + ) { defer wg.Done() headLoadResults[index] = m.loadHead(headRecord, inputRelabelerConfigs, generation) }(index, hr, cfgs, generation) @@ -126,20 +146,23 @@ func (m *Manager) Restore(blockDuration time.Duration) (active relabeler.Head, r } if i == len(headLoadResults)-1 { - statusIsAppropriate := loadResult.headRecord.Status() == catalog.StatusNew || loadResult.headRecord.Status() == catalog.StatusActive - isInBlockTimeRange := m.clock.Now().Sub(time.UnixMilli(loadResult.headRecord.CreatedAt())).Milliseconds() < blockDuration.Milliseconds() + statusIsAppropriate := loadResult.headRecord.Status() == catalog.StatusNew || + loadResult.headRecord.Status() == catalog.StatusActive + isInBlockTimeRange := m.clock.Now().Sub( + time.UnixMilli(loadResult.headRecord.CreatedAt()), + ).Milliseconds() < blockDuration.Milliseconds() isNotCorrupted := !loadResult.corrupted if isNotCorrupted && statusIsAppropriate && isInBlockTimeRange { active = loadResult.head if _, err = m.catalog.SetStatus(loadResult.headRecord.ID(), catalog.StatusActive); err != nil { - return nil, nil, fmt.Errorf("failed to set status: %w", err) + return nil, nil, fmt.Errorf("failed to set active status: %w", err) } continue } } if _, err = m.catalog.SetStatus(loadResult.headRecord.ID(), catalog.StatusRotated); err != nil { - return nil, nil, fmt.Errorf("failed to set status: %w", err) + return nil, nil, fmt.Errorf("failed to set rotated status: %w", err) } loadResult.head.Stop() @@ -154,7 +177,7 @@ func (m *Manager) Restore(blockDuration time.Duration) (active relabeler.Head, r return nil, nil, fmt.Errorf("failed to build active head: %w", err) } if _, err = m.catalog.SetStatus(active.ID(), catalog.StatusActive); err != nil { - return nil, nil, fmt.Errorf("failed to set status: %w", err) + return nil, nil, fmt.Errorf("failed to set active status: %w", err) } } @@ -186,10 +209,9 @@ func (m *Manager) loadHead( inputRelabelerConfigs, headRecord.NumberOfShards(), m.maxSegmentSize, - SetLastAppendedSegmentIDFn(func(segmentID uint32) { - headRecord.SetLastAppendedSegmentID(segmentID) - }), - m.registerer) + SetLastAppendedSegmentIDFn(func(segmentID uint32) { headRecord.SetLastAppendedSegmentID(segmentID) }), + m.registerer, + ) if err != nil { result.err = err return result @@ -218,53 +240,27 @@ func (m *Manager) loadHead( logger.Errorf("failed to set corrupted state, head id: %s: %v", headRecord.ID(), setCorruptedErr) } } - m.counter.With(prometheus.Labels{"type": "corrupted"}).Inc() } - headReleaseFn := headRecord.Acquire() - drh := NewDiscardableRotatableHead( - h, - func(id string, err error) error { - if _, rotateErr := m.catalog.SetStatus(id, catalog.StatusRotated); rotateErr != nil { - return errors.Join(err, rotateErr) - } - m.counter.With(prometheus.Labels{"type": "rotated"}).Inc() - return err - }, - func(id string) error { - var discardErr error - if _, discardErr = m.catalog.SetStatus(id, catalog.StatusPersisted); discardErr != nil { - return discardErr - } - m.counter.With(prometheus.Labels{"type": "persisted"}).Inc() - return nil - }, - func(id string) error { - headReleaseFn() - return nil - }, - ) - m.counter.With(prometheus.Labels{"type": "created"}).Inc() - - result.head = drh + result.head = m.createDiscardableRotatableHead(h, headRecord.Acquire()) result.corrupted = corrupted return result } -func (m *Manager) Build() (relabeler.Head, error) { - cfgs, numberOfShards := m.configSource.Get() - return m.BuildWithConfig(cfgs, numberOfShards) -} - -func (m *Manager) BuildWithConfig(inputRelabelerConfigs []*config.InputRelabelerConfig, numberOfShards uint16) (h relabeler.Head, err error) { +// BuildWithConfig build head with incoming config. +func (m *Manager) BuildWithConfig( + inputRelabelerConfigs []*config.InputRelabelerConfig, + numberOfShards uint16, +) (h relabeler.Head, err error) { headRecord, err := m.catalog.Create(numberOfShards) if err != nil { return nil, err } headDir := filepath.Join(m.dir, headRecord.ID()) - if err = os.Mkdir(headDir, 0777); err != nil { + //revive:disable-next-line:add-constant // this is already a constant + if err = os.Mkdir(headDir, 0o777); err != nil { //nolint:gosec // need this permissions return nil, err } defer func() { @@ -281,17 +277,64 @@ func (m *Manager) BuildWithConfig(inputRelabelerConfigs []*config.InputRelabeler inputRelabelerConfigs, numberOfShards, m.maxSegmentSize, - SetLastAppendedSegmentIDFn(func(segmentID uint32) { - headRecord.SetLastAppendedSegmentID(segmentID) - }), - m.registerer) + SetLastAppendedSegmentIDFn(func(segmentID uint32) { headRecord.SetLastAppendedSegmentID(segmentID) }), + m.registerer, + ) + if err != nil { + return nil, fmt.Errorf("failed to create head: %w", err) + } + + m.generation++ + + return m.createDiscardableRotatableHead(h, headRecord.Acquire()), nil +} + +// BuildWithLSS head with target lsses. +func (m *Manager) BuildWithLSS(targetLsses []*cppbridge.LabelSetStorage) (h relabeler.Head, err error) { + inputRelabelerConfigs, numberOfShards := m.configSource.Get() + + if uint16(len(targetLsses)) != numberOfShards { //nolint:gosec // no overflow + return m.BuildWithConfig(inputRelabelerConfigs, numberOfShards) + } + + headRecord, err := m.catalog.Create(numberOfShards) + if err != nil { + return nil, err + } + + headDir := filepath.Join(m.dir, headRecord.ID()) + //revive:disable-next-line:add-constant // this is already a constant + if err = os.Mkdir(headDir, 0o777); err != nil { //nolint:gosec // need this permissions + return nil, err + } + defer func() { + if err != nil { + err = errors.Join(err, os.RemoveAll(headDir)) + } + }() + + generation := m.generation + h, err = head.CreateWithLSS( + headRecord.ID(), + generation, + headDir, + inputRelabelerConfigs, + targetLsses, + m.maxSegmentSize, + SetLastAppendedSegmentIDFn(func(segmentID uint32) { headRecord.SetLastAppendedSegmentID(segmentID) }), + m.registerer, + ) if err != nil { return nil, fmt.Errorf("failed to create head: %w", err) } m.generation++ - releaseHeadFn := headRecord.Acquire() + return m.createDiscardableRotatableHead(h, headRecord.Acquire()), nil +} + +// createDiscardableRotatableHead create discardable and rotatable head. +func (m *Manager) createDiscardableRotatableHead(h relabeler.Head, releaseHeadFn func()) *DiscardableRotatableHead { m.counter.With(prometheus.Labels{"type": "created"}).Inc() return NewDiscardableRotatableHead( h, @@ -303,16 +346,15 @@ func (m *Manager) BuildWithConfig(inputRelabelerConfigs []*config.InputRelabeler return err }, func(id string) error { - var discardErr error - if _, discardErr = m.catalog.SetStatus(id, catalog.StatusPersisted); discardErr != nil { + if _, discardErr := m.catalog.SetStatus(id, catalog.StatusPersisted); discardErr != nil { return discardErr } m.counter.With(prometheus.Labels{"type": "persisted"}).Inc() return nil }, - func(id string) error { + func(_ string) error { releaseHeadFn() return nil }, - ), nil + ) } diff --git a/pp/series_data/querier/instant_querier.h b/pp/series_data/querier/instant_querier.h index dd932bb668..d452af0874 100644 --- a/pp/series_data/querier/instant_querier.h +++ b/pp/series_data/querier/instant_querier.h @@ -14,7 +14,7 @@ class InstantQuerier { public: PROMPP_ALWAYS_INLINE static void query_sample(Sample& sample, const DataStorage& storage, LabelSetID ls_id, const Timestamp& timestamp) noexcept { - if (storage.open_chunks.size() <= ls_id) [[unlikely]] { + if (storage.open_chunks.size() <= ls_id || storage.open_chunks[ls_id].is_empty()) [[unlikely]] { return; } if (const auto series_last_ts = Decoder::get_series_max_timestamp(storage, ls_id); timestamp >= series_last_ts) { From ae3ca2cf86886ab17710521bfc093a9176be2a96 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Tue, 6 May 2025 12:18:37 +0300 Subject: [PATCH 36/90] changed logic of prompp_primitives_lss_copy_added_series --- pp/entrypoint/primitives_lss.cpp | 11 ++++------- pp/entrypoint/primitives_lss.h | 8 +++----- pp/go/cppbridge/entrypoint.go | 15 +++++---------- pp/go/cppbridge/entrypoint.h | 8 +++----- pp/go/cppbridge/primitives_lss.go | 6 +++--- pp/go/cppbridge/primitives_lss_test.go | 7 ++++--- 6 files changed, 22 insertions(+), 33 deletions(-) diff --git a/pp/entrypoint/primitives_lss.cpp b/pp/entrypoint/primitives_lss.cpp index e77d697b6b..20b4d3c7a6 100644 --- a/pp/entrypoint/primitives_lss.cpp +++ b/pp/entrypoint/primitives_lss.cpp @@ -25,17 +25,14 @@ extern "C" void prompp_primitives_lss_ctor(void* args, void* res) { new (res) Result{.lss = create_lss(static_cast(args)->lss_type)}; } -extern "C" void prompp_primitives_lss_copy_added_series(void* args, void* res) { +extern "C" void prompp_primitives_lss_copy_added_series(void* args) { struct Arguments { - LssVariantPtr lss; - }; - struct Result { - LssVariantPtr lss; + LssVariantPtr source; + LssVariantPtr destination; }; const auto arguments = static_cast(args); - const auto result = new (res) Result{.lss = create_lss(LssType::kQueryableEncodingBimap)}; - std::get(*arguments->lss).copy_added_series(std::get(*result->lss)); + std::get(*arguments->source).copy_added_series(std::get(*arguments->destination)); } extern "C" void prompp_primitives_lss_dtor(void* args) { diff --git a/pp/entrypoint/primitives_lss.h b/pp/entrypoint/primitives_lss.h index b1d3e74007..1b153f0dea 100644 --- a/pp/entrypoint/primitives_lss.h +++ b/pp/entrypoint/primitives_lss.h @@ -19,14 +19,12 @@ void prompp_primitives_lss_ctor(void* args, void* res); * @brief Construct a new Primitives label sets. * * @param args { - * lss uintptr // pointer of label sets; + * source uintptr // pointer to source label sets + * destination uintptr // pointer to destination label sets * } * - * @param res { - * lss uintptr // pointer to constructed label sets; - * } */ -void prompp_primitives_lss_copy_added_series(void* args, void* res); +void prompp_primitives_lss_copy_added_series(void* args); /** * @brief Destroy Primitives label sets. diff --git a/pp/go/cppbridge/entrypoint.go b/pp/go/cppbridge/entrypoint.go index 33791db79d..6e49aa51c3 100644 --- a/pp/go/cppbridge/entrypoint.go +++ b/pp/go/cppbridge/entrypoint.go @@ -1063,21 +1063,16 @@ func primitivesLSSCtor(lss_type uint32) uintptr { return res.lss } -func primitivesLSSCopyAddedSeries(lss uintptr) uintptr { +func primitivesLSSCopyAddedSeries(source, destination uintptr) { args := struct { - lss uintptr - }{lss} - var res struct { - lss uintptr - } + source uintptr + destination uintptr + }{source, destination} - fastcgo.UnsafeCall2( + fastcgo.UnsafeCall1( C.prompp_primitives_lss_copy_added_series, uintptr(unsafe.Pointer(&args)), - uintptr(unsafe.Pointer(&res)), ) - - return res.lss } // primitivesLSSDtor - wrapper for destructor C-EncodingBimap. diff --git a/pp/go/cppbridge/entrypoint.h b/pp/go/cppbridge/entrypoint.h index c692f1381b..94f45e4639 100755 --- a/pp/go/cppbridge/entrypoint.h +++ b/pp/go/cppbridge/entrypoint.h @@ -386,14 +386,12 @@ void prompp_primitives_lss_ctor(void* args, void* res); * @brief Construct a new Primitives label sets. * * @param args { - * lss uintptr // pointer of label sets; + * source uintptr // pointer to source label sets + * destination uintptr // pointer to destination label sets * } * - * @param res { - * lss uintptr // pointer to constructed label sets; - * } */ -void prompp_primitives_lss_copy_added_series(void* args, void* res); +void prompp_primitives_lss_copy_added_series(void* args); /** * @brief Destroy Primitives label sets. diff --git a/pp/go/cppbridge/primitives_lss.go b/pp/go/cppbridge/primitives_lss.go index ea9e88cc37..8a462d018d 100644 --- a/pp/go/cppbridge/primitives_lss.go +++ b/pp/go/cppbridge/primitives_lss.go @@ -171,9 +171,9 @@ func (lss *LabelSetStorage) GetLabelSets(labelSetIDs []uint32) *LabelSetStorageG return result } -// CopyAddedSeries - returns copy of lss with label sets which were added via FindOrEmplace -func (lss *LabelSetStorage) CopyAddedSeries() *LabelSetStorage { - return newLabelSetStorageFromPointer(primitivesLSSCopyAddedSeries(lss.pointer)) +// CopyAddedSeries - copy label sets which were added via FindOrEmplace to destination +func (lss *LabelSetStorage) CopyAddedSeries(destination *LabelSetStorage) { + primitivesLSSCopyAddedSeries(lss.pointer, destination.pointer) } // Pointer return c-pointer. diff --git a/pp/go/cppbridge/primitives_lss_test.go b/pp/go/cppbridge/primitives_lss_test.go index a33684a776..0cb54de4b8 100644 --- a/pp/go/cppbridge/primitives_lss_test.go +++ b/pp/go/cppbridge/primitives_lss_test.go @@ -224,13 +224,14 @@ func (s *QueryableLSSSuite) testQueryLabelValuesImpl(testCase queryLabelValuesCa func (s *QueryableLSSSuite) TestCopyAddedSeries() { // Arrange emptyLabelsSets := make([]cppbridge.Labels, len(s.labelSetIDs)) + lssCopy := cppbridge.NewQueryableLssStorage() + lssCopyOfCopy := cppbridge.NewQueryableLssStorage() // Act - lssCopy := s.lss.CopyAddedSeries() - lssCopyOfCopy := lssCopy.CopyAddedSeries() + s.lss.CopyAddedSeries(lssCopy) + lssCopy.CopyAddedSeries(lssCopyOfCopy) // Assert - s.Equal(labelSetToCppBridgeLabels(s.labelSets), lssCopy.GetLabelSets(s.labelSetIDs).LabelsSets()) s.Equal(emptyLabelsSets, lssCopyOfCopy.GetLabelSets(s.labelSetIDs).LabelsSets()) } From d1a0786af309053d91a606d89692d0e275d58e05 Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Tue, 6 May 2025 11:14:55 +0000 Subject: [PATCH 37/90] for save --- cmd/prometheus/main.go | 3 ++- pp/go/relabeler/appender/head.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index cf4bdf2e6f..eb34f57767 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -712,7 +712,8 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), + // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), + BlockDuration: 6 * time.Minute, Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), }, headCatalog, diff --git a/pp/go/relabeler/appender/head.go b/pp/go/relabeler/appender/head.go index b2ad049f8f..3ef91549b0 100644 --- a/pp/go/relabeler/appender/head.go +++ b/pp/go/relabeler/appender/head.go @@ -135,7 +135,8 @@ func NewRotatableHead(head relabeler.Head, storage Storage, builder HeadBuilder, func (h *RotatableHead) Rotate() error { targetLsses := make([]*cppbridge.LabelSetStorage, h.head.NumberOfShards()) _ = h.head.ForEachShard(func(shard relabeler.Shard) error { - targetLsses[shard.ShardID()] = shard.LSS().Raw().CopyAddedSeries() + targetLsses[shard.ShardID()] = cppbridge.NewQueryableLssStorage() + shard.LSS().Raw().CopyAddedSeries(targetLsses[shard.ShardID()]) return nil }) From d1786f23683f82c4388d17ad8fcd5a1e0dead97b Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Wed, 7 May 2025 11:45:36 +0000 Subject: [PATCH 38/90] for save --- cmd/prometheus/main.go | 3 +- pp/go/relabeler/appender/appender_test.go | 46 --------------- pp/go/relabeler/appender/head.go | 56 +++++++++++------- pp/go/relabeler/head/builder.go | 33 ++++------- pp/go/relabeler/head/head.go | 9 +++ pp/go/relabeler/head/load.go | 70 +++-------------------- pp/go/relabeler/head/manager/head.go | 5 ++ pp/go/relabeler/head/manager/manager.go | 51 ++--------------- pp/go/relabeler/interface.go | 1 + 9 files changed, 74 insertions(+), 200 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index eb34f57767..cf4bdf2e6f 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -712,8 +712,7 @@ func main() { cfgFile.RemoteWriteConfigs, localStoragePath, receiver.RotationInfo{ - // BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), - BlockDuration: 6 * time.Minute, + BlockDuration: time.Duration(cfg.tsdb.MinBlockDuration), Seed: cfgFile.GlobalConfig.ExternalLabels.Hash(), }, headCatalog, diff --git a/pp/go/relabeler/appender/appender_test.go b/pp/go/relabeler/appender/appender_test.go index 1114ec908a..3c470fe684 100644 --- a/pp/go/relabeler/appender/appender_test.go +++ b/pp/go/relabeler/appender/appender_test.go @@ -1517,29 +1517,6 @@ func (s *AppenderSuite) TestManagerRelabelerRelabelingWithRotate() { headsToClose = append(headsToClose, newHead) return newHead, nil }, - func( - targetLsses []*cppbridge.LabelSetStorage, - inputRelabelerConfigs []*config.InputRelabelerConfig, - ) (relabeler.Head, error) { - newDir, buildErr := os.MkdirTemp("", "appender_test") - s.Require().NoError(buildErr) - tmpDirsToRemove = append(tmpDirsToRemove, newDir) - generation++ - newHeadID := fmt.Sprintf("head_id_%d", generation) - newHead, buildErr := head.CreateWithLSS( - newHeadID, - generation, - newDir, - inputRelabelerConfigs, - targetLsses, - 0, - head.NoOpLastAppendedSegmentIDSetter{}, - prometheus.DefaultRegisterer, - ) - s.Require().NoError(buildErr) - headsToClose = append(headsToClose, newHead) - return newHead, nil - }, ) rotatableHead := appender.NewRotatableHead(hd, noOpStorage{}, builder, appender.NoOpHeadActivator{}) @@ -2447,29 +2424,6 @@ func (s *AppenderSuite) TestManagerRelabelerRelabelingWithRotateWithStaleNans() headsToClose = append(headsToClose, newHead) return newHead, nil }, - func( - targetLsses []*cppbridge.LabelSetStorage, - inputRelabelerConfigs []*config.InputRelabelerConfig, - ) (relabeler.Head, error) { - newDir, buildErr := os.MkdirTemp("", "appender_test") - s.Require().NoError(buildErr) - tmpDirsToRemove = append(tmpDirsToRemove, newDir) - generation++ - newHeadID := fmt.Sprintf("head_id_%d", generation) - newHead, buildErr := head.CreateWithLSS( - newHeadID, - generation, - newDir, - inputRelabelerConfigs, - targetLsses, - 0, - head.NoOpLastAppendedSegmentIDSetter{}, - prometheus.DefaultRegisterer, - ) - s.Require().NoError(buildErr) - headsToClose = append(headsToClose, newHead) - return newHead, nil - }, ) rotatableHead := appender.NewRotatableHead(hd, noOpStorage{}, builder, appender.NoOpHeadActivator{}) diff --git a/pp/go/relabeler/appender/head.go b/pp/go/relabeler/appender/head.go index 3ef91549b0..ba698ddb34 100644 --- a/pp/go/relabeler/appender/head.go +++ b/pp/go/relabeler/appender/head.go @@ -17,7 +17,7 @@ type Storage interface { // HeadBuilder - head builder. type HeadBuilder interface { - BuildWithLSS(targetLsses []*cppbridge.LabelSetStorage) (relabeler.Head, error) + Build() (relabeler.Head, error) BuildWithConfig(inputRelabelerConfigs []*config.InputRelabelerConfig, numberOfShards uint16) (relabeler.Head, error) } @@ -37,6 +37,21 @@ type RotatableHead struct { headActivator HeadActivator } +// NewRotatableHead - RotatableHead constructor. +func NewRotatableHead( + head relabeler.Head, + storage Storage, + builder HeadBuilder, + headActivator HeadActivator, +) *RotatableHead { + return &RotatableHead{ + head: head, + storage: storage, + builder: builder, + headActivator: headActivator, + } +} + // ID - relabeler.Head interface implementation. func (h *RotatableHead) ID() string { return h.head.ID() @@ -121,30 +136,15 @@ func (h *RotatableHead) Close() error { return h.head.Close() } -// NewRotatableHead - RotatableHead constructor. -func NewRotatableHead(head relabeler.Head, storage Storage, builder HeadBuilder, headActivator HeadActivator) *RotatableHead { - return &RotatableHead{ - head: head, - storage: storage, - builder: builder, - headActivator: headActivator, - } -} - // Rotate - relabeler.Head interface implementation. func (h *RotatableHead) Rotate() error { - targetLsses := make([]*cppbridge.LabelSetStorage, h.head.NumberOfShards()) - _ = h.head.ForEachShard(func(shard relabeler.Shard) error { - targetLsses[shard.ShardID()] = cppbridge.NewQueryableLssStorage() - shard.LSS().Raw().CopyAddedSeries(targetLsses[shard.ShardID()]) - return nil - }) - - newHead, err := h.builder.BuildWithLSS(targetLsses) + newHead, err := h.builder.Build() if err != nil { return err } + newHead.CopySeriesFrom(h.head) + if err = h.headActivator.Activate(newHead.ID()); err != nil { return err } @@ -183,6 +183,15 @@ func (h *RotatableHead) Discard() error { return h.head.Discard() } +// CopySeriesFrom copy series from other head. +func (h *RotatableHead) CopySeriesFrom(other relabeler.Head) { + h.head.CopySeriesFrom(other) +} + +// +// HeapProfileWritableHead +// + type HeapProfileWriter interface { WriteHeapProfile() error } @@ -192,6 +201,10 @@ type HeapProfileWritableHead struct { heapProfileWriter HeapProfileWriter } +func NewHeapProfileWritableHead(head relabeler.Head, heapProfileWriter HeapProfileWriter) *HeapProfileWritableHead { + return &HeapProfileWritableHead{head: head, heapProfileWriter: heapProfileWriter} +} + func (h *HeapProfileWritableHead) ID() string { return h.head.ID() } @@ -274,6 +287,7 @@ func (h *HeapProfileWritableHead) Discard() error { return h.head.Discard() } -func NewHeapProfileWritableHead(head relabeler.Head, heapProfileWriter HeapProfileWriter) *HeapProfileWritableHead { - return &HeapProfileWritableHead{head: head, heapProfileWriter: heapProfileWriter} +// CopySeriesFrom copy series from other head. +func (h *HeapProfileWritableHead) CopySeriesFrom(other relabeler.Head) { + h.head.CopySeriesFrom(other) } diff --git a/pp/go/relabeler/head/builder.go b/pp/go/relabeler/head/builder.go index 1abe655c24..dfe3dfdc6b 100644 --- a/pp/go/relabeler/head/builder.go +++ b/pp/go/relabeler/head/builder.go @@ -1,7 +1,6 @@ package head import ( - "github.com/prometheus/prometheus/pp/go/cppbridge" "github.com/prometheus/prometheus/pp/go/relabeler" "github.com/prometheus/prometheus/pp/go/relabeler/config" ) @@ -18,25 +17,23 @@ func (fn ConfigSourceFunc) Config() (inputRelabelerConfigs []*config.InputRelabe type BuildFunc func(inputRelabelerConfigs []*config.InputRelabelerConfig, numberOfShards uint16) (relabeler.Head, error) -type BuildWithLSSFunc func( - targetLsses []*cppbridge.LabelSetStorage, - inputRelabelerConfigs []*config.InputRelabelerConfig, -) (relabeler.Head, error) - type Builder struct { - configSource ConfigSource - buildFunc BuildFunc - buildWithLSSFunc BuildWithLSSFunc + configSource ConfigSource + buildFunc BuildFunc } -func NewBuilder(configSource ConfigSource, buildFunc BuildFunc, buildWithLSSFunc BuildWithLSSFunc) *Builder { +func NewBuilder(configSource ConfigSource, buildFunc BuildFunc) *Builder { return &Builder{ - configSource: configSource, - buildFunc: buildFunc, - buildWithLSSFunc: buildWithLSSFunc, + configSource: configSource, + buildFunc: buildFunc, } } +// Build head. +func (b *Builder) Build() (relabeler.Head, error) { + return b.buildFunc(b.configSource.Config()) +} + // BuildWithConfig build head with incoming config. func (b *Builder) BuildWithConfig( inputRelabelerConfigs []*config.InputRelabelerConfig, @@ -44,13 +41,3 @@ func (b *Builder) BuildWithConfig( ) (relabeler.Head, error) { return b.buildFunc(inputRelabelerConfigs, numberOfShards) } - -// BuildWithLSS head with target lsses. -func (b *Builder) BuildWithLSS(targetLsses []*cppbridge.LabelSetStorage) (relabeler.Head, error) { - inputRelabelerConfigs, numberOfShards := b.configSource.Config() - if uint16(len(targetLsses)) != numberOfShards { //nolint:gosec // no overflow - return b.buildFunc(inputRelabelerConfigs, numberOfShards) - } - - return b.buildWithLSSFunc(targetLsses, inputRelabelerConfigs) -} diff --git a/pp/go/relabeler/head/head.go b/pp/go/relabeler/head/head.go index 0ff43c7c2f..ea955cb730 100644 --- a/pp/go/relabeler/head/head.go +++ b/pp/go/relabeler/head/head.go @@ -541,6 +541,15 @@ func (h *Head) Rotate() error { return nil } +// CopySeriesFrom copy series from other head. +func (h *Head) CopySeriesFrom(other relabeler.Head) { + _ = other.ForEachShard(func(shard relabeler.Shard) error { + shard.LSS().Raw().CopyAddedSeries(h.lsses[shard.ShardID()].Raw()) + return nil + }) +} + +// Close wals and clear metrics. func (h *Head) Close() error { h.memoryInUse.DeletePartialMatch(prometheus.Labels{"generation": strconv.FormatUint(h.generation, 10)}) var err error diff --git a/pp/go/relabeler/head/load.go b/pp/go/relabeler/head/load.go index 2675372bb7..4727f06361 100644 --- a/pp/go/relabeler/head/load.go +++ b/pp/go/relabeler/head/load.go @@ -57,51 +57,6 @@ func Create( return New(id, generation, configs, lsses, wals, dataStorages, numberOfShards, registerer) } -func CreateWithLSS( - id string, - generation uint64, - dir string, - configs []*config.InputRelabelerConfig, - targetLsses []*cppbridge.LabelSetStorage, - maxSegmentSize uint32, - lastAppendedSegmentIDSetter LastAppendedSegmentIDSetter, - registerer prometheus.Registerer, -) (_ *Head, err error) { - numberOfShards := uint16(len(targetLsses)) - - lsses := make([]*LSS, numberOfShards) - wals := make([]*ShardWal, numberOfShards) - dataStorages := make([]*DataStorage, numberOfShards) - - defer func() { - if err == nil { - return - } - for _, wal := range wals { - if wal != nil { - _ = wal.Close() - } - } - }() - - swn := newSegmentWriteNotifier(numberOfShards, lastAppendedSegmentIDSetter) - - for shardID := uint16(0); shardID < numberOfShards; shardID++ { - lsses[shardID], wals[shardID], dataStorages[shardID], err = createShardWithLSS( - dir, - shardID, - swn, - targetLsses[shardID], - maxSegmentSize, - ) - if err != nil { - return nil, fmt.Errorf("failed to create shard: %w", err) - } - } - - return New(id, generation, configs, lsses, wals, dataStorages, numberOfShards, registerer) -} - // createShard create shard for head. func createShard( dir string, @@ -109,23 +64,6 @@ func createShard( swn *segmentWriteNotifier, maxSegmentSize uint32, ) (*LSS, *ShardWal, *DataStorage, error) { - return createShardWithLSS(dir, shardID, swn, cppbridge.NewQueryableLssStorage(), maxSegmentSize) -} - -// createShardWithLSS create shard for head with lss. -func createShardWithLSS( - dir string, - shardID uint16, - swn *segmentWriteNotifier, - targetLss *cppbridge.LabelSetStorage, - maxSegmentSize uint32, -) (*LSS, *ShardWal, *DataStorage, error) { - inputLss := cppbridge.NewLssStorage() - lss := &LSS{ - input: inputLss, - target: targetLss, - } - shardFile, err := os.Create(filepath.Join(filepath.Clean(dir), fmt.Sprintf("shard_%d.wal", shardID))) if err != nil { return nil, nil, nil, fmt.Errorf("failed to create shard wal file: %w", err) @@ -138,7 +76,12 @@ func createShardWithLSS( _ = shardFile.Close() }() - shardWalEncoder := cppbridge.NewHeadWalEncoder(shardID, HeadWalEncoderDecoderLogShards, targetLss) + lss := &LSS{ + input: cppbridge.NewLssStorage(), + target: cppbridge.NewQueryableLssStorage(), + } + + shardWalEncoder := cppbridge.NewHeadWalEncoder(shardID, HeadWalEncoderDecoderLogShards, lss.target) _, err = WriteHeader(shardFile, FileFormatVersion, shardWalEncoder.Version()) if err != nil { return nil, nil, nil, fmt.Errorf("failed to write header: %w", err) @@ -150,6 +93,7 @@ func createShardWithLSS( } shardWal := newShardWal(shardWalEncoder, maxSegmentSize, sw) + cppDataStorage := cppbridge.NewHeadDataStorage() dataStorage := &DataStorage{ dataStorage: cppDataStorage, diff --git a/pp/go/relabeler/head/manager/head.go b/pp/go/relabeler/head/manager/head.go index ceaabe52db..c2744a17b6 100644 --- a/pp/go/relabeler/head/manager/head.go +++ b/pp/go/relabeler/head/manager/head.go @@ -114,3 +114,8 @@ func (h *DiscardableRotatableHead) Discard() (err error) { } return err } + +// CopySeriesFrom copy series from other head. +func (h *DiscardableRotatableHead) CopySeriesFrom(other relabeler.Head) { + h.head.CopySeriesFrom(other) +} diff --git a/pp/go/relabeler/head/manager/manager.go b/pp/go/relabeler/head/manager/manager.go index ba09e0e4cc..fe69bac892 100644 --- a/pp/go/relabeler/head/manager/manager.go +++ b/pp/go/relabeler/head/manager/manager.go @@ -10,7 +10,6 @@ import ( "github.com/jonboulle/clockwork" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/prometheus/pp/go/cppbridge" "github.com/prometheus/prometheus/pp/go/relabeler" "github.com/prometheus/prometheus/pp/go/relabeler/config" "github.com/prometheus/prometheus/pp/go/relabeler/head" @@ -248,6 +247,12 @@ func (m *Manager) loadHead( return result } +// Build head. +func (m *Manager) Build() (relabeler.Head, error) { + cfgs, numberOfShards := m.configSource.Get() + return m.BuildWithConfig(cfgs, numberOfShards) +} + // BuildWithConfig build head with incoming config. func (m *Manager) BuildWithConfig( inputRelabelerConfigs []*config.InputRelabelerConfig, @@ -289,50 +294,6 @@ func (m *Manager) BuildWithConfig( return m.createDiscardableRotatableHead(h, headRecord.Acquire()), nil } -// BuildWithLSS head with target lsses. -func (m *Manager) BuildWithLSS(targetLsses []*cppbridge.LabelSetStorage) (h relabeler.Head, err error) { - inputRelabelerConfigs, numberOfShards := m.configSource.Get() - - if uint16(len(targetLsses)) != numberOfShards { //nolint:gosec // no overflow - return m.BuildWithConfig(inputRelabelerConfigs, numberOfShards) - } - - headRecord, err := m.catalog.Create(numberOfShards) - if err != nil { - return nil, err - } - - headDir := filepath.Join(m.dir, headRecord.ID()) - //revive:disable-next-line:add-constant // this is already a constant - if err = os.Mkdir(headDir, 0o777); err != nil { //nolint:gosec // need this permissions - return nil, err - } - defer func() { - if err != nil { - err = errors.Join(err, os.RemoveAll(headDir)) - } - }() - - generation := m.generation - h, err = head.CreateWithLSS( - headRecord.ID(), - generation, - headDir, - inputRelabelerConfigs, - targetLsses, - m.maxSegmentSize, - SetLastAppendedSegmentIDFn(func(segmentID uint32) { headRecord.SetLastAppendedSegmentID(segmentID) }), - m.registerer, - ) - if err != nil { - return nil, fmt.Errorf("failed to create head: %w", err) - } - - m.generation++ - - return m.createDiscardableRotatableHead(h, headRecord.Acquire()), nil -} - // createDiscardableRotatableHead create discardable and rotatable head. func (m *Manager) createDiscardableRotatableHead(h relabeler.Head, releaseHeadFn func()) *DiscardableRotatableHead { m.counter.With(prometheus.Labels{"type": "created"}).Inc() diff --git a/pp/go/relabeler/interface.go b/pp/go/relabeler/interface.go index 32545a7fae..d18ef93693 100644 --- a/pp/go/relabeler/interface.go +++ b/pp/go/relabeler/interface.go @@ -75,6 +75,7 @@ type Head interface { Close() error Discard() error String() string + CopySeriesFrom(other Head) } type Distributor interface { From ba61359f678faf164ee4076aae394bcea0db0aff Mon Sep 17 00:00:00 2001 From: Alexandr Yudin Date: Mon, 12 May 2025 09:58:45 +0000 Subject: [PATCH 39/90] fix merge --- pp/entrypoint/series_data_data_storage.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/pp/entrypoint/series_data_data_storage.cpp b/pp/entrypoint/series_data_data_storage.cpp index 869eb73de9..7999d235de 100644 --- a/pp/entrypoint/series_data_data_storage.cpp +++ b/pp/entrypoint/series_data_data_storage.cpp @@ -149,7 +149,6 @@ extern "C" void prompp_series_data_serialized_chunk_recoder_ctor(void* args, voi }; const auto in = static_cast(args); - const auto& ls_id_set = std::get(*in->lss).ls_id_set(); new (res) Result{ .chunk_recoder = std::make_unique(std::in_place_type, series_data::chunk::SerializedChunkIterator{in->buffer.span()}, in->time_interval), From 644cf0c7bc390e5936b7a6632b63ea9800eaa382 Mon Sep 17 00:00:00 2001 From: Vladimir Pustovalov Date: Tue, 13 May 2025 15:37:26 +0300 Subject: [PATCH 40/90] created entrypoint/label_set module --- pp/entrypoint/label_set.cpp | 59 ++++++++++++++++++++++ pp/entrypoint/label_set.h | 44 +++++++++++++++++ pp/entrypoint/primitives_lss.cpp | 56 --------------------- pp/entrypoint/primitives_lss.h | 41 --------------- pp/go/cppbridge/entrypoint.go | 12 ++--- pp/go/cppbridge/entrypoint.h | 85 +++++++++++++++++--------------- pp/go/cppbridge/labels_cpp.go | 6 +-- 7 files changed, 156 insertions(+), 147 deletions(-) create mode 100644 pp/entrypoint/label_set.cpp create mode 100644 pp/entrypoint/label_set.h diff --git a/pp/entrypoint/label_set.cpp b/pp/entrypoint/label_set.cpp new file mode 100644 index 0000000000..4899fcf0a8 --- /dev/null +++ b/pp/entrypoint/label_set.cpp @@ -0,0 +1,59 @@ +#include "label_set.h" + +#include "entrypoint/head/lss.h" +#include "primitives/go_model.h" +#include "primitives/go_slice.h" + +using entrypoint::head::LssVariantPtr; + +void prompp_label_set_length(void* args, void* res) { + struct Arguments { + LssVariantPtr lss; + uint32_t series_id; + }; + struct Result { + size_t length; + }; + + auto in = static_cast(args); + + std::visit([in, res](auto& lss) { new (res) Result{.length = lss[in->series_id].size()}; }, *in->lss); +} + +void prompp_label_set_serialize(void* args, void* res) { + using PromPP::Primitives::Go::Label; + using PromPP::Primitives::Go::Slice; + using PromPP::Primitives::Go::String; + + struct Arguments { + LssVariantPtr lss; + uint32_t series_id; + }; + struct Result { + Slice