diff --git a/include/ylt/metric/counter.hpp b/include/ylt/metric/counter.hpp index 70c02e3c5..95a2f3e41 100644 --- a/include/ylt/metric/counter.hpp +++ b/include/ylt/metric/counter.hpp @@ -218,6 +218,9 @@ class basic_dynamic_counter : public dynamic_metric { labels_value, thread_local_value(dupli_count_)); if (r) { g_user_metric_label_count.local_value()++; + if (ylt_label_max_age.count()) { + it->second.set_created_time(std::chrono::system_clock::now()); + } } set_value(it->second.local_value(), value, op_type_t::INC); } @@ -232,6 +235,9 @@ class basic_dynamic_counter : public dynamic_metric { labels_value, thread_local_value(dupli_count_)); if (r) { g_user_metric_label_count.local_value()++; + if (ylt_label_max_age.count()) { + it->second.set_created_time(std::chrono::system_clock::now()); + } } return it->second.update(value); } @@ -271,6 +277,21 @@ class basic_dynamic_counter : public dynamic_metric { return value_map_.size(); } + void clean_expired_label() override { + if (ylt_label_max_age.count() == 0) { + return; + } + + auto now = std::chrono::system_clock::now(); + std::lock_guard lock(mtx_); + std::erase_if(value_map_, [&now](auto &pair) { + bool r = std::chrono::duration_cast( + now - pair.second.get_created_time()) + .count() >= ylt_label_max_age.count(); + return r; + }); + } + void remove_label_value( const std::map &labels) override { { diff --git a/include/ylt/metric/gauge.hpp b/include/ylt/metric/gauge.hpp index cbaaa759a..cd2ba69a4 100644 --- a/include/ylt/metric/gauge.hpp +++ b/include/ylt/metric/gauge.hpp @@ -74,6 +74,9 @@ class basic_dynamic_gauge : public basic_dynamic_counter { labels_value, thread_local_value(dupli_count_)); if (r) { g_user_metric_label_count.local_value()++; + if (ylt_label_max_age.count()) { + it->second.set_created_time(std::chrono::system_clock::now()); + } } set_value(it->second.local_value(), value, op_type_t::DEC); diff --git a/include/ylt/metric/metric.hpp b/include/ylt/metric/metric.hpp index b86ef9b97..f3f2179d4 100644 --- a/include/ylt/metric/metric.hpp +++ b/include/ylt/metric/metric.hpp @@ -135,6 +135,8 @@ class metric_t { labels_value_.end(); } + virtual void clean_expired_label() {} + virtual bool has_label_value(const std::vector& label_value) { return labels_value_ == label_value; } @@ -229,6 +231,9 @@ inline std::atomic g_user_metric_count = 0; inline std::atomic ylt_metric_capacity = 10000000; inline int64_t ylt_label_capacity = 20000000; +inline std::chrono::seconds ylt_label_max_age{0}; +inline std::chrono::seconds ylt_label_check_expire_duration{0}; + inline void set_metric_capacity(int64_t max_count) { ylt_metric_capacity = max_count; } @@ -236,4 +241,11 @@ inline void set_metric_capacity(int64_t max_count) { inline void set_label_capacity(int64_t max_label_count) { ylt_label_capacity = max_label_count; } + +inline void set_label_max_age( + std::chrono::seconds max_age, + std::chrono::seconds check_duration = std::chrono::seconds(60 * 10)) { + ylt_label_max_age = max_age; + ylt_label_check_expire_duration = check_duration; +} } // namespace ylt::metric \ No newline at end of file diff --git a/include/ylt/metric/metric_manager.hpp b/include/ylt/metric/metric_manager.hpp index e128e7d38..d9b68b7d4 100644 --- a/include/ylt/metric/metric_manager.hpp +++ b/include/ylt/metric/metric_manager.hpp @@ -505,7 +505,34 @@ class dynamic_metric_manager { } private: - dynamic_metric_manager() = default; + void clean_label_expired() { + executor_ = coro_io::create_io_context_pool(1); + timer_ = std::make_shared(executor_->get_executor()); + check_label_expired().via(executor_->get_executor()).start([](auto&&) { + }); + } + + async_simple::coro::Lazy check_label_expired() { + auto timer = timer_; + timer_->expires_after(ylt_label_check_expire_duration); + bool r = co_await timer_->async_await(); + if (!r) { + co_return; + } + + std::unique_lock lock(mtx_); + for (auto& [_, m] : metric_map_) { + m->clean_expired_label(); + } + lock.unlock(); + co_await check_label_expired(); + } + + dynamic_metric_manager() { + if (ylt_label_max_age.count() > 0) { + clean_label_expired(); + } + } std::vector> get_metric_by_label_value( const std::vector& label_value) { @@ -532,6 +559,8 @@ class dynamic_metric_manager { std::shared_mutex mtx_; std::unordered_map> metric_map_; + std::shared_ptr timer_ = nullptr; + std::shared_ptr executor_ = nullptr; }; struct ylt_default_metric_tag_t {}; diff --git a/include/ylt/metric/thread_local_value.hpp b/include/ylt/metric/thread_local_value.hpp index 3d554b8bb..2a13b2edc 100644 --- a/include/ylt/metric/thread_local_value.hpp +++ b/include/ylt/metric/thread_local_value.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -98,7 +99,14 @@ class thread_local_value { return val; } + void set_created_time(std::chrono::system_clock::time_point tm) { + created_time_ = tm; + } + + auto get_created_time() { return created_time_; } + private: std::vector *>> duplicates_; + std::chrono::system_clock::time_point created_time_{}; }; } // namespace ylt::metric diff --git a/src/metric/tests/test_metric.cpp b/src/metric/tests/test_metric.cpp index 6cafe9f9c..fbe9794c7 100644 --- a/src/metric/tests/test_metric.cpp +++ b/src/metric/tests/test_metric.cpp @@ -8,6 +8,22 @@ using namespace ylt; using namespace ylt::metric; struct metrc_tag {}; + +struct test_tag {}; + +TEST_CASE("test metric manager clean expired label") { + set_label_max_age(std::chrono::seconds(1), std::chrono::seconds(1)); + auto& inst = dynamic_metric_manager::instance(); + auto [ec, c] = inst.create_metric_dynamic( + std::string("some_counter"), "", std::array{"url"}); + c->inc({"/"}); + c->inc({"/test"}); + std::this_thread::sleep_for(std::chrono::seconds(2)); + c->inc({"/index"}); + size_t count = c->label_value_count(); + CHECK(count == 1); +} + TEST_CASE("test metric manager") { auto c = std::make_shared("test1", ""); auto g = std::make_shared("test2", "");