10#include <gtest/gtest.h>
32 kcenon::common::VoidResult
set_enabled(
bool enable)
override {
34 return kcenon::common::ok();
38 return kcenon::common::ok();
41 kcenon::common::VoidResult
cleanup()
override {
42 return kcenon::common::ok();
45 kcenon::common::Result<metrics_snapshot>
collect()
override {
49 snapshot.
capture_time = std::chrono::system_clock::now();
53 return kcenon::common::ok(std::move(snapshot));
82 EXPECT_EQ(config.
strategy, adaptation_strategy::balanced);
90 std::chrono::milliseconds(100));
92 std::chrono::milliseconds(5000));
99 auto mock = std::make_shared<mock_collector>(
"test_collector");
108 auto result = collector.
collect();
109 ASSERT_TRUE(result.is_ok());
110 EXPECT_EQ(mock->get_collect_count(), 1);
119 collector.
adapt(sys_metrics);
122 EXPECT_EQ(stats.current_load_level, load_level::critical);
126 auto mock = std::make_shared<mock_collector>(
"test_collector");
144 collector.
adapt(low_load);
146 EXPECT_EQ(stats.current_load_level, load_level::low);
149 collector.
adapt(high_load);
152 EXPECT_EQ(stats.current_load_level, load_level::critical);
153 EXPECT_GT(stats.total_adaptations, 0);
154 EXPECT_GT(stats.downscale_count, 0);
158 auto mock = std::make_shared<mock_collector>(
"test_collector");
161 auto result = monitor.register_collector(
"test", mock);
162 ASSERT_TRUE(result.is_ok());
163 EXPECT_TRUE(result.value());
166 result = monitor.register_collector(
"test", mock);
167 ASSERT_FALSE(result.is_ok());
168 EXPECT_EQ(result.error().code,
static_cast<int>(monitoring_error_code::already_exists));
171 result = monitor.unregister_collector(
"test");
172 ASSERT_TRUE(result.is_ok());
173 EXPECT_TRUE(result.value());
176 result = monitor.unregister_collector(
"test");
177 ASSERT_FALSE(result.is_ok());
178 EXPECT_EQ(result.error().code,
static_cast<int>(monitoring_error_code::not_found));
182 auto mock = std::make_shared<mock_collector>(
"test_collector");
183 monitor.register_collector(
"test", mock);
185 EXPECT_FALSE(monitor.is_running());
187 auto result = monitor.start();
188 ASSERT_TRUE(result.is_ok());
189 EXPECT_TRUE(monitor.is_running());
192 result = monitor.start();
193 ASSERT_TRUE(result.is_ok());
195 result = monitor.stop();
196 ASSERT_TRUE(result.is_ok());
197 EXPECT_FALSE(monitor.is_running());
201 auto high_priority = std::make_shared<mock_collector>(
"high");
202 auto medium_priority = std::make_shared<mock_collector>(
"medium");
203 auto low_priority = std::make_shared<mock_collector>(
"low");
205 monitor.register_collector(
"high", high_priority);
206 monitor.register_collector(
"medium", medium_priority);
207 monitor.register_collector(
"low", low_priority);
210 monitor.set_collector_priority(
"high", 100);
211 monitor.set_collector_priority(
"medium", 50);
212 monitor.set_collector_priority(
"low", 10);
215 auto active = monitor.get_active_collectors();
216 EXPECT_GE(
active.size(), 1);
218 EXPECT_EQ(
active[0],
"high");
223 auto mock = std::make_shared<mock_collector>(
"test");
224 monitor.register_collector(
"test", mock);
227 monitor.set_global_strategy(adaptation_strategy::conservative);
230 auto result = monitor.force_adaptation();
231 ASSERT_TRUE(result.is_ok());
234 auto stats_result = monitor.get_collector_stats(
"test");
235 ASSERT_TRUE(stats_result.is_ok());
240 auto mock1 = std::make_shared<mock_collector>(
"collector1");
241 auto mock2 = std::make_shared<mock_collector>(
"collector2");
243 monitor.register_collector(
"collector1", mock1);
244 monitor.register_collector(
"collector2", mock2);
246 auto all_stats = monitor.get_all_stats();
247 EXPECT_EQ(all_stats.size(), 2);
248 EXPECT_TRUE(all_stats.find(
"collector1") != all_stats.end());
249 EXPECT_TRUE(all_stats.find(
"collector2") != all_stats.end());
253 auto mock = std::make_shared<mock_collector>(
"scoped");
262 EXPECT_TRUE(stats.is_ok());
267 EXPECT_FALSE(stats.is_ok());
271 auto mock = std::make_shared<mock_collector>(
"test");
286 collector.
adapt(metrics);
290 EXPECT_GE(
static_cast<int>(stats.current_load_level),
291 static_cast<int>(load_level::high));
295 auto mock = std::make_shared<mock_collector>(
"test");
307 collector.
adapt(metrics1);
311 EXPECT_NEAR(stats1.average_cpu_usage, 20.0, 1.0);
316 collector.
adapt(metrics2);
320 EXPECT_GT(stats2.average_cpu_usage, 20.0);
321 EXPECT_LE(stats2.average_cpu_usage, 60.0);
325 auto mock = std::make_shared<mock_collector>(
"test");
330 monitor.register_collector(
"test", mock, config);
334 std::this_thread::sleep_for(std::chrono::milliseconds(1500));
336 auto stats = monitor.get_collector_stats(
"test");
337 ASSERT_TRUE(stats.is_ok());
340 EXPECT_GE(stats.value().total_adaptations, 0);
344 auto mock = std::make_shared<mock_collector>(
"test");
353 auto result = collector.
collect();
354 EXPECT_TRUE(result.is_ok());
360 auto mock = std::make_shared<mock_collector>(
"global_test");
361 auto result = global.register_collector(
"global_test", mock);
362 ASSERT_TRUE(result.is_ok());
365 global.unregister_collector(
"global_test");
369 auto mock = std::make_shared<mock_collector>(
"test");
373 conservative_config.
strategy = adaptation_strategy::conservative;
379 conservative_collector.
adapt(metrics);
380 auto conservative_stats = conservative_collector.
get_stats();
384 aggressive_config.
strategy = adaptation_strategy::aggressive;
387 aggressive_collector.
adapt(metrics);
388 auto aggressive_stats = aggressive_collector.
get_stats();
391 EXPECT_LE(
static_cast<int>(conservative_stats.current_load_level),
392 static_cast<int>(aggressive_stats.current_load_level));
396 const int num_threads = 10;
397 const int collectors_per_thread = 5;
399 std::vector<std::thread> threads;
401 for (
int t = 0; t < num_threads; ++t) {
402 threads.emplace_back([
this, t, collectors_per_thread]() {
403 for (
int c = 0; c < collectors_per_thread; ++c) {
404 std::string name =
"collector_" + std::to_string(t) +
"_" + std::to_string(c);
405 auto mock = std::make_shared<mock_collector>(name);
407 monitor.register_collector(name, mock);
411 monitor.set_collector_priority(name, t * 10 + c);
415 monitor.get_collector_stats(name);
421 for (
auto& thread : threads) {
425 auto all_stats = monitor.get_all_stats();
426 EXPECT_EQ(all_stats.size(), num_threads * collectors_per_thread);
434 auto mock = std::make_shared<mock_collector>(
"test");
448 collector.
adapt(metrics);
451 EXPECT_EQ(stats.current_load_level, load_level::low);
456 collector.
adapt(metrics);
459 EXPECT_EQ(stats.current_load_level, load_level::low);
463 collector.
adapt(metrics);
466 EXPECT_EQ(stats.current_load_level, load_level::moderate);
470 auto mock = std::make_shared<mock_collector>(
"test");
482 collector.
adapt(metrics);
485 EXPECT_EQ(stats.current_load_level, load_level::low);
489 collector.
adapt(metrics);
492 EXPECT_EQ(stats.current_load_level, load_level::moderate);
496 auto mock = std::make_shared<mock_collector>(
"test");
510 collector.
adapt(metrics);
514 EXPECT_EQ(stats.current_load_level, load_level::critical);
515 EXPECT_EQ(stats.total_adaptations, 1);
519 collector.
adapt(metrics);
523 EXPECT_EQ(stats.current_load_level, load_level::critical);
524 EXPECT_EQ(stats.cooldown_prevented_changes, 1);
527 std::this_thread::sleep_for(std::chrono::milliseconds(110));
530 collector.
adapt(metrics);
532 EXPECT_EQ(stats.current_load_level, load_level::idle);
533 EXPECT_EQ(stats.total_adaptations, 2);
537 collector.
adapt(metrics);
541 EXPECT_EQ(stats.current_load_level, load_level::idle);
542 EXPECT_EQ(stats.cooldown_prevented_changes, 2);
546 auto mock = std::make_shared<mock_collector>(
"test");
556 std::vector<std::pair<double, load_level>> load_progression = {
557 {10.0, load_level::idle},
558 {25.0, load_level::low},
559 {45.0, load_level::moderate},
560 {65.0, load_level::high},
561 {85.0, load_level::critical}
564 for (
const auto& [
cpu, expected_level] : load_progression) {
569 collector.
adapt(metrics);
572 EXPECT_EQ(stats.current_load_level, expected_level)
573 <<
"Failed at CPU " <<
cpu <<
"%";
579 EXPECT_GE(stats.total_adaptations, 4);
580 EXPECT_GE(stats.downscale_count, 4);
584 auto mock = std::make_shared<mock_collector>(
"test");
596 collector.
adapt(metrics);
599 std::vector<std::pair<double, load_level>> load_progression = {
600 {75.0, load_level::high},
601 {55.0, load_level::moderate},
602 {35.0, load_level::low},
603 {15.0, load_level::idle}
606 for (
const auto& [
cpu, expected_level] : load_progression) {
608 collector.
adapt(metrics);
611 EXPECT_EQ(stats.current_load_level, expected_level)
612 <<
"Failed at CPU " <<
cpu <<
"%";
616 EXPECT_EQ(stats.upscale_count, 4);
620 auto mock = std::make_shared<mock_collector>(
"test");
633 collector.
adapt(metrics);
635 auto baseline_stats = collector.
get_stats();
637 EXPECT_EQ(baseline_stats.current_load_level, load_level::moderate);
641 collector.
adapt(metrics);
643 auto spike_stats = collector.
get_stats();
645 EXPECT_GE(
static_cast<int>(spike_stats.current_load_level),
646 static_cast<int>(load_level::high));
650 collector.
adapt(metrics);
652 auto continued_spike_stats = collector.
get_stats();
654 EXPECT_EQ(continued_spike_stats.current_load_level, load_level::critical);
658 collector.
adapt(metrics);
660 auto recovery_stats = collector.
get_stats();
662 EXPECT_LE(
static_cast<int>(recovery_stats.current_load_level),
663 static_cast<int>(continued_spike_stats.current_load_level));
667 auto mock = std::make_shared<mock_collector>(
"test");
680 collector.
adapt(metrics);
682 auto initial_stats = collector.
get_stats();
687 for (
int i = 0; i < 10; ++i) {
689 collector.
adapt(metrics);
692 auto final_stats = collector.
get_stats();
694 EXPECT_LE(final_stats.total_adaptations - initial_adaptations, 2);
698 auto mock = std::make_shared<mock_collector>(
"test");
710 collector.
adapt(metrics);
712 auto initial_stats = collector.
get_stats();
716 for (
int i = 0; i < 10; ++i) {
718 collector.
adapt(metrics);
721 auto final_stats = collector.
get_stats();
723 EXPECT_GT(final_stats.total_adaptations - initial_adaptations, 5);
737 auto mock = std::make_shared<mock_collector>(
"test");
751 collector.
adapt(metrics);
755 collector.
adapt(metrics);
759 collector.
adapt(metrics);
761 collector.
adapt(metrics);
765 EXPECT_GE(stats.cooldown_prevented_changes + stats.hysteresis_prevented_changes, 0);
769 auto mock = std::make_shared<mock_collector>(
"test");
785 collector.
adapt(metrics);
789 EXPECT_GE(
static_cast<int>(stats.current_load_level),
790 static_cast<int>(load_level::high));
Adaptive monitoring implementation that adjusts behavior based on system load.
Adaptive collector wrapper.
adaptation_stats get_stats() const
Get current adaptation statistics.
common::Result< kcenon::monitoring::metrics_snapshot > collect()
Collect metrics with adaptive sampling.
void adapt(const kcenon::monitoring::system_metrics &sys_metrics)
Adapt collection behavior based on load.
void set_enabled(bool enabled)
Enable or disable adaptive behavior.
void set_config(const adaptive_config &config)
Set adaptive configuration.
bool is_enabled() const
Check if adaptive behavior is enabled.
Adaptive monitoring controller.
common::Result< bool > stop()
Stop adaptive monitoring.
common::Result< adaptation_stats > get_collector_stats(const std::string &name) const
Get adaptation statistics for a collector.
Adaptive monitoring scope.
bool is_registered() const
Abstract base class for metric collectors.
kcenon::common::VoidResult initialize() override
Initialize the collector.
kcenon::common::VoidResult cleanup() override
Cleanup collector resources.
int get_collect_count() const
std::string get_name() const override
Get collector name.
std::atomic< int > collect_count_
kcenon::common::VoidResult set_enabled(bool enable) override
Enable or disable the collector.
kcenon::common::Result< metrics_snapshot > collect() override
Collect metrics.
mock_collector(const std::string &name)
bool is_enabled() const override
Check if collector is enabled.
adaptive_monitor & global_adaptive_monitor()
Global adaptive monitor instance.
@ cpu
CPU power domain (RAPL)
std::uint64_t total_adaptations
Adaptive configuration parameters.
double memory_warning_threshold
std::chrono::milliseconds cooldown_period
double memory_critical_threshold
std::chrono::seconds adaptation_interval
double get_sampling_rate_for_load(load_level level) const
Get sampling rate for load level.
double critical_sampling_rate
std::chrono::milliseconds get_interval_for_load(load_level level) const
Get collection interval for load level.
double idle_sampling_rate
double moderate_threshold
adaptation_strategy strategy
Complete snapshot of metrics at a point in time.
std::chrono::system_clock::time_point capture_time
void add_metric(const std::string &name, double value)
Add a metric to the snapshot.
double memory_usage_percent
TEST_F(AdaptiveMonitoringTest, AdaptiveConfigDefaults)