Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
test_metric_storage.cpp File Reference
#include <gtest/gtest.h>
#include <kcenon/monitoring/utils/ring_buffer.h>
#include <kcenon/monitoring/utils/metric_types.h>
#include <kcenon/monitoring/utils/time_series.h>
#include <kcenon/monitoring/utils/metric_storage.h>
#include <chrono>
#include <thread>
#include <vector>
#include <random>
Include dependency graph for test_metric_storage.cpp:

Go to the source code of this file.

Classes

class  MetricStorageTest
 Test suite for Phase 3 P1: Memory-efficient metric storage. More...
 

Functions

 TEST_F (MetricStorageTest, RingBufferBasicOperations)
 
 TEST_F (MetricStorageTest, RingBufferOverwrite)
 
 TEST_F (MetricStorageTest, RingBufferBatchOperations)
 
 TEST_F (MetricStorageTest, RingBufferPeek)
 
 TEST_F (MetricStorageTest, CompactMetricValue)
 
 TEST_F (MetricStorageTest, MetricBatch)
 
 TEST_F (MetricStorageTest, HistogramData)
 
 TEST_F (MetricStorageTest, TimeSeriesBasicOperations)
 
 TEST_F (MetricStorageTest, TimeSeriesQuery)
 
 TEST_F (MetricStorageTest, MetricStorageBasicOperations)
 
 TEST_F (MetricStorageTest, MetricStorageBatchOperations)
 
 TEST_F (MetricStorageTest, MetricStorageCapacityLimits)
 
 TEST_F (MetricStorageTest, MetricStorageThreadSafety)
 
 TEST_F (MetricStorageTest, ConfigurationValidation)
 

Function Documentation

◆ TEST_F() [1/14]

TEST_F ( MetricStorageTest ,
CompactMetricValue  )

Definition at line 133 of file test_metric_storage.cpp.

133 {
134 auto metadata = create_metric_metadata("test_metric", metric_type::counter);
135
136 compact_metric_value metric_double(metadata, 3.14159);
137 EXPECT_EQ(metric_double.as_double(), 3.14159);
138 EXPECT_TRUE(metric_double.is_numeric());
139
140 compact_metric_value metric_int(metadata, int64_t(42));
141 EXPECT_EQ(metric_int.as_int64(), 42);
142 EXPECT_EQ(metric_int.as_double(), 42.0);
143 EXPECT_TRUE(metric_int.is_numeric());
144
145 compact_metric_value metric_string(metadata, std::string("test"));
146 EXPECT_EQ(metric_string.as_string(), "test");
147 EXPECT_FALSE(metric_string.is_numeric());
148}
metric_metadata create_metric_metadata(const std::string &name, metric_type type, size_t tag_count=0)
Create metric metadata from name and type.
Memory-efficient metric value storage.

References kcenon::monitoring::compact_metric_value::as_double(), kcenon::monitoring::compact_metric_value::as_int64(), kcenon::monitoring::compact_metric_value::as_string(), kcenon::monitoring::create_metric_metadata(), and kcenon::monitoring::compact_metric_value::is_numeric().

Here is the call graph for this function:

◆ TEST_F() [2/14]

TEST_F ( MetricStorageTest ,
ConfigurationValidation  )

Definition at line 382 of file test_metric_storage.cpp.

382 {
383 // Test invalid ring buffer capacity (not power of 2)
384 ring_buffer_config invalid_ring_config;
385 invalid_ring_config.capacity = 1000; // Not a power of 2
386
387 auto validation = invalid_ring_config.validate();
388 EXPECT_FALSE(validation.is_ok());
389
390 // Test valid configuration
391 ring_buffer_config valid_ring_config;
392 valid_ring_config.capacity = 1024; // Power of 2
393
394 validation = valid_ring_config.validate();
395 EXPECT_TRUE(validation.is_ok());
396
397 // Test invalid time series configuration
398 time_series_config invalid_ts_config;
399 invalid_ts_config.retention_period = std::chrono::seconds(-1);
400
401 validation = invalid_ts_config.validate();
402 EXPECT_FALSE(validation.is_ok());
403
404 // Test invalid metric storage configuration
405 metric_storage_config invalid_storage_config;
406 invalid_storage_config.max_metrics = 0;
407
408 validation = invalid_storage_config.validate();
409 EXPECT_FALSE(validation.is_ok());
410}
Configuration for metric storage.
common::VoidResult validate() const
Validate configuration.
Configuration for ring buffer behavior.
Definition ring_buffer.h:36
common::VoidResult validate() const
Validate ring buffer configuration.
Definition ring_buffer.h:45
Configuration for time series storage.
Definition time_series.h:34
common::VoidResult validate() const
Validate configuration.
Definition time_series.h:44
std::chrono::seconds retention_period
Definition time_series.h:35

References kcenon::monitoring::ring_buffer_config::capacity, kcenon::monitoring::metric_storage_config::max_metrics, kcenon::monitoring::time_series_config::retention_period, kcenon::monitoring::metric_storage_config::validate(), kcenon::monitoring::ring_buffer_config::validate(), and kcenon::monitoring::time_series_config::validate().

Here is the call graph for this function:

◆ TEST_F() [3/14]

TEST_F ( MetricStorageTest ,
HistogramData  )

Definition at line 168 of file test_metric_storage.cpp.

168 {
169 histogram_data hist;
171
172 // Add some samples
173 std::vector<double> samples = {0.001, 0.01, 0.1, 0.5, 1.0, 2.0, 5.0, 10.0};
174 for (double sample : samples) {
175 hist.add_sample(sample);
176 }
177
178 EXPECT_EQ(hist.total_count, samples.size());
179 EXPECT_GT(hist.sum, 0);
180 EXPECT_GT(hist.mean(), 0);
181
182 // Check bucket counts
183 for (const auto& bucket : hist.buckets) {
184 EXPECT_GE(bucket.count, 0);
185 }
186}
Histogram data with buckets.
double mean() const noexcept
Get mean value.
void init_standard_buckets()
Initialize standard buckets.
std::vector< histogram_bucket > buckets
void add_sample(double value)
Add value to histogram.

References kcenon::monitoring::histogram_data::add_sample(), kcenon::monitoring::histogram_data::buckets, kcenon::monitoring::histogram_data::init_standard_buckets(), kcenon::monitoring::histogram_data::mean(), kcenon::monitoring::histogram_data::sum, and kcenon::monitoring::histogram_data::total_count.

Here is the call graph for this function:

◆ TEST_F() [4/14]

TEST_F ( MetricStorageTest ,
MetricBatch  )

Definition at line 150 of file test_metric_storage.cpp.

150 {
151 metric_batch batch(1);
152
153 auto metadata = create_metric_metadata("test", metric_type::gauge);
154
155 for (int i = 0; i < 5; ++i) {
156 compact_metric_value metric(metadata, static_cast<double>(i));
157 batch.add_metric(std::move(metric));
158 }
159
160 EXPECT_EQ(batch.size(), 5);
161 EXPECT_FALSE(batch.empty());
162 EXPECT_GT(batch.memory_footprint(), 0);
163
164 batch.clear();
165 EXPECT_TRUE(batch.empty());
166}
Batch of metrics for efficient processing.
Basic metric structure for interface compatibility.

References kcenon::monitoring::metric_batch::add_metric(), kcenon::monitoring::metric_batch::clear(), kcenon::monitoring::create_metric_metadata(), kcenon::monitoring::metric_batch::empty(), kcenon::monitoring::metric_batch::memory_footprint(), and kcenon::monitoring::metric_batch::size().

Here is the call graph for this function:

◆ TEST_F() [5/14]

TEST_F ( MetricStorageTest ,
MetricStorageBasicOperations  )

Definition at line 251 of file test_metric_storage.cpp.

251 {
253 config.ring_buffer_capacity = 64;
254 config.max_metrics = 100;
255 config.enable_background_processing = false; // Disable for testing
256
257 metric_storage storage(config);
258
259 // Store some metrics
260 auto result1 = storage.store_metric("cpu_usage", 65.5, metric_type::gauge);
261 EXPECT_TRUE(result1.is_ok());
262
263 auto result2 = storage.store_metric("memory_usage", 4096.0, metric_type::gauge);
264 EXPECT_TRUE(result2.is_ok());
265
266 auto result3 = storage.store_metric("request_count", 100.0, metric_type::counter);
267 EXPECT_TRUE(result3.is_ok());
268
269 // Flush to time series
270 storage.flush();
271
272 // Query latest values
273 auto cpu = storage.get_latest_value("cpu_usage");
274 EXPECT_TRUE(cpu.is_ok());
275 EXPECT_EQ(cpu.value(), 65.5);
276
277 auto memory = storage.get_latest_value("memory_usage");
278 EXPECT_TRUE(memory.is_ok());
279 EXPECT_EQ(memory.value(), 4096.0);
280
281 // Get metric names
282 auto names = storage.get_metric_names();
283 EXPECT_GE(names.size(), 3);
284
285 // Check statistics
286 const auto& stats = storage.get_stats();
287 EXPECT_GE(stats.total_metrics_stored.load(), 3);
288 EXPECT_EQ(stats.total_metrics_dropped.load(), 0);
289}
Thread-safe metric storage with ring buffer buffering.
@ storage
Storage device sensor.
@ memory
Memory/DRAM power domain (RAPL)
@ cpu
CPU power domain (RAPL)

References kcenon::monitoring::cpu, kcenon::monitoring::metric_storage_config::enable_background_processing, kcenon::monitoring::metric_storage_config::max_metrics, kcenon::monitoring::memory, kcenon::monitoring::metric_storage_config::ring_buffer_capacity, and kcenon::monitoring::storage.

◆ TEST_F() [6/14]

TEST_F ( MetricStorageTest ,
MetricStorageBatchOperations  )

Definition at line 291 of file test_metric_storage.cpp.

291 {
293 config.enable_background_processing = false;
294 metric_storage storage(config);
295
296 // First, register the metric name by storing one metric
297 auto init_result = storage.store_metric("batch_metric", 0.0, metric_type::gauge);
298 EXPECT_TRUE(init_result.is_ok());
299
300 // Create a batch of metrics with the same name hash
301 metric_batch batch;
302 auto metadata = create_metric_metadata("batch_metric", metric_type::gauge);
303
304 for (int i = 0; i < 50; ++i) {
305 compact_metric_value metric(metadata, static_cast<double>(i));
306 batch.add_metric(std::move(metric));
307 }
308
309 // Store the batch
310 size_t stored = storage.store_metrics_batch(batch);
311 EXPECT_EQ(stored, 50);
312
313 storage.flush();
314
315 // Query the data - should have data from both the initial store and batch
316 time_series_query query;
317 auto result = storage.query_metric("batch_metric", query);
318 EXPECT_TRUE(result.is_ok());
319}
void add_metric(compact_metric_value &&metric)
Add metric to batch.
Query parameters for time series data.

References kcenon::monitoring::metric_batch::add_metric(), kcenon::monitoring::create_metric_metadata(), kcenon::monitoring::metric_storage_config::enable_background_processing, and kcenon::monitoring::storage.

Here is the call graph for this function:

◆ TEST_F() [7/14]

TEST_F ( MetricStorageTest ,
MetricStorageCapacityLimits  )

Definition at line 321 of file test_metric_storage.cpp.

321 {
323 config.max_metrics = 2; // Very low limit for testing
324 config.ring_buffer_capacity = 8;
325 config.enable_background_processing = false;
326
327 metric_storage storage(config);
328
329 // Store metrics up to the limit
330 EXPECT_TRUE(storage.store_metric("metric1", 1.0).is_ok());
331 EXPECT_TRUE(storage.store_metric("metric2", 2.0).is_ok());
332
333 // This should fail due to capacity limit
334 auto result = storage.store_metric("metric3", 3.0);
335 // Note: Might succeed if metrics are stored in ring buffer, depends on implementation
336
337 const auto& stats = storage.get_stats();
338 EXPECT_LE(stats.active_metric_series.load(), 2);
339}

References kcenon::monitoring::metric_storage_config::enable_background_processing, kcenon::monitoring::metric_storage_config::max_metrics, kcenon::monitoring::metric_storage_config::ring_buffer_capacity, and kcenon::monitoring::storage.

◆ TEST_F() [8/14]

TEST_F ( MetricStorageTest ,
MetricStorageThreadSafety  )

Definition at line 341 of file test_metric_storage.cpp.

341 {
343
344 const int num_threads = 4;
345 const int metrics_per_thread = 100;
346 std::vector<std::thread> threads;
347
348 // Launch multiple threads writing metrics
349 for (int t = 0; t < num_threads; ++t) {
350 threads.emplace_back([&storage, t, metrics_per_thread]() {
351 std::random_device rd;
352 std::mt19937 gen(rd());
353 std::uniform_real_distribution<> dis(0.0, 100.0);
354
355 for (int i = 0; i < metrics_per_thread; ++i) {
356 std::string metric_name = "thread_" + std::to_string(t) + "_metric_" + std::to_string(i);
357 double value = dis(gen);
358 storage.store_metric(metric_name, value);
359
360 // Small delay to increase chance of contention
361 std::this_thread::sleep_for(std::chrono::microseconds(1));
362 }
363 });
364 }
365
366 // Wait for all threads to complete
367 for (auto& thread : threads) {
368 thread.join();
369 }
370
371 storage.flush();
372
373 // Verify we stored a reasonable number of metrics
374 const auto& stats = storage.get_stats();
375 EXPECT_GT(stats.total_metrics_stored.load(), num_threads * metrics_per_thread * 0.8);
376
377 auto names = storage.get_metric_names();
378 EXPECT_GT(names.size(), 0);
379}
void flush()
Flush buffered metrics to time series.

References kcenon::monitoring::metric_storage::flush(), and kcenon::monitoring::storage.

Here is the call graph for this function:

◆ TEST_F() [9/14]

TEST_F ( MetricStorageTest ,
RingBufferBasicOperations  )

Definition at line 32 of file test_metric_storage.cpp.

32 {
33 ring_buffer_config config;
34 config.capacity = 8; // Small capacity for testing
35 config.overwrite_old = false;
36 config.batch_size = 4; // Must be <= capacity
37
38 ring_buffer<int> buffer(config);
39
40 EXPECT_EQ(buffer.capacity(), 8);
41 EXPECT_TRUE(buffer.empty());
42 EXPECT_FALSE(buffer.full());
43 EXPECT_EQ(buffer.size(), 0);
44
45 // Write elements
46 for (int i = 0; i < 7; ++i) {
47 auto result = buffer.write(std::move(i));
48 EXPECT_TRUE(result.is_ok()) << "Failed to write " << i;
49 }
50
51 EXPECT_EQ(buffer.size(), 7);
52 EXPECT_FALSE(buffer.empty());
53 EXPECT_TRUE(buffer.full()); // 7 elements in 8-capacity buffer means full
54
55 // Try to write when full
56 auto result = buffer.write(std::move(999));
57 EXPECT_FALSE(result.is_ok()); // Should fail as overwrite_old is false
58
59 // Read elements
60 int value = 0;
61 for (int i = 0; i < 7; ++i) {
62 auto read_result = buffer.read(value);
63 EXPECT_TRUE(read_result.is_ok());
64 EXPECT_EQ(value, i);
65 }
66
67 EXPECT_TRUE(buffer.empty());
68}
Lock-free ring buffer with atomic operations.

References kcenon::monitoring::ring_buffer_config::batch_size, kcenon::monitoring::ring_buffer< T >::capacity(), kcenon::monitoring::ring_buffer_config::capacity, kcenon::monitoring::ring_buffer< T >::empty(), kcenon::monitoring::ring_buffer< T >::full(), kcenon::monitoring::ring_buffer_config::overwrite_old, kcenon::monitoring::ring_buffer< T >::read(), kcenon::monitoring::ring_buffer< T >::size(), and kcenon::monitoring::ring_buffer< T >::write().

Here is the call graph for this function:

◆ TEST_F() [10/14]

TEST_F ( MetricStorageTest ,
RingBufferBatchOperations  )

Definition at line 94 of file test_metric_storage.cpp.

94 {
95 ring_buffer<int> buffer;
96
97 // Write batch
98 std::vector<int> write_data = {1, 2, 3, 4, 5};
99 size_t written = buffer.write_batch(std::move(write_data));
100 EXPECT_EQ(written, 5);
101
102 // Read batch
103 std::vector<int> read_data;
104 size_t read_count = buffer.read_batch(read_data, 10);
105 EXPECT_EQ(read_count, 5);
106 EXPECT_EQ(read_data.size(), 5);
107
108 for (size_t i = 0; i < read_data.size(); ++i) {
109 EXPECT_EQ(read_data[i], static_cast<int>(i + 1));
110 }
111}
size_t write_batch(std::vector< T > &&items)
Write multiple elements in batch.
size_t read_batch(std::vector< T > &items, size_t max_count=SIZE_MAX)
Read multiple elements in batch.

References kcenon::monitoring::ring_buffer< T >::read_batch(), and kcenon::monitoring::ring_buffer< T >::write_batch().

Here is the call graph for this function:

◆ TEST_F() [11/14]

TEST_F ( MetricStorageTest ,
RingBufferOverwrite  )

Definition at line 70 of file test_metric_storage.cpp.

70 {
71 ring_buffer_config config;
72 config.capacity = 4;
73 config.overwrite_old = true;
74 config.batch_size = 2; // Must be <= capacity
75
76 ring_buffer<int> buffer(config);
77
78 // Fill buffer completely
79 for (int i = 0; i < 8; ++i) {
80 auto result = buffer.write(std::move(i));
81 EXPECT_TRUE(result.is_ok());
82 }
83
84 // Should have overwritten, so we should read the last 3 values
85 std::vector<int> read_values;
86 int value = 0;
87 while (buffer.read(value).is_ok()) {
88 read_values.push_back(value);
89 }
90
91 EXPECT_GE(read_values.size(), 3); // Should have at least the last few values
92}

References kcenon::monitoring::ring_buffer_config::batch_size, kcenon::monitoring::ring_buffer_config::capacity, kcenon::monitoring::ring_buffer_config::overwrite_old, kcenon::monitoring::ring_buffer< T >::read(), and kcenon::monitoring::ring_buffer< T >::write().

Here is the call graph for this function:

◆ TEST_F() [12/14]

TEST_F ( MetricStorageTest ,
RingBufferPeek  )

Definition at line 113 of file test_metric_storage.cpp.

113 {
114 ring_buffer<int> buffer;
115
116 buffer.write(std::move(42));
117 buffer.write(std::move(84));
118
119 int value = 0;
120 auto result = buffer.peek(value);
121 EXPECT_TRUE(result.is_ok());
122 EXPECT_EQ(value, 42);
123
124 // Size should not change after peek
125 EXPECT_EQ(buffer.size(), 2);
126
127 // Reading should still get the same value
128 buffer.read(value);
129 EXPECT_EQ(value, 42);
130}
size_t size() const noexcept
Get current number of elements in buffer.
common::VoidResult write(T &&item)
Write a single element to the buffer.
common::VoidResult peek(T &item) const
Peek at the next item without removing it.
common::VoidResult read(T &item)
Read a single element from the buffer.

References kcenon::monitoring::ring_buffer< T >::peek(), kcenon::monitoring::ring_buffer< T >::read(), kcenon::monitoring::ring_buffer< T >::size(), and kcenon::monitoring::ring_buffer< T >::write().

Here is the call graph for this function:

◆ TEST_F() [13/14]

TEST_F ( MetricStorageTest ,
TimeSeriesBasicOperations  )

Definition at line 189 of file test_metric_storage.cpp.

189 {
190 time_series_config config;
191 config.max_points = 100;
192 config.retention_period = std::chrono::seconds(60);
193
194 auto series_result = time_series::create("test_series", config);
195 ASSERT_TRUE(series_result.is_ok());
196 auto& series = series_result.value();
197
198 EXPECT_TRUE(series->empty());
199 EXPECT_EQ(series->name(), "test_series");
200
201 // Add some data points
202 auto now = std::chrono::system_clock::now();
203 for (int i = 0; i < 10; ++i) {
204 auto timestamp = now + std::chrono::seconds(i);
205 auto result = series->add_point(static_cast<double>(i), timestamp);
206 EXPECT_TRUE(result.is_ok());
207 }
208
209 EXPECT_EQ(series->size(), 10);
210 EXPECT_FALSE(series->empty());
211
212 // Get latest value
213 auto latest = series->get_latest_value();
214 EXPECT_TRUE(latest.is_ok());
215 EXPECT_EQ(latest.value(), 9.0);
216}
static common::Result< std::unique_ptr< time_series > > create(const std::string &name, const time_series_config &config={})
Factory method to create time_series with validation.

References kcenon::monitoring::time_series::create(), kcenon::monitoring::time_series_config::max_points, and kcenon::monitoring::time_series_config::retention_period.

Here is the call graph for this function:

◆ TEST_F() [14/14]

TEST_F ( MetricStorageTest ,
TimeSeriesQuery  )

Definition at line 218 of file test_metric_storage.cpp.

218 {
219 auto series_result = time_series::create("query_test");
220 ASSERT_TRUE(series_result.is_ok());
221 auto& series = series_result.value();
222
223 auto now = std::chrono::system_clock::now();
224
225 // Add data points over a time range
226 for (int i = 0; i < 60; ++i) {
227 auto timestamp = now + std::chrono::seconds(i);
228 series->add_point(static_cast<double>(i), timestamp);
229 }
230
231 // Query a subset
232 time_series_query query;
233 query.start_time = now + std::chrono::seconds(10);
234 query.end_time = now + std::chrono::seconds(50);
235 query.step = std::chrono::seconds(10);
236
237 auto result = series->query(query);
238 EXPECT_TRUE(result.is_ok());
239
240 const auto& agg_result = result.value();
241 EXPECT_GT(agg_result.points.size(), 0);
242 EXPECT_GT(agg_result.total_samples, 0);
243
244 auto summary = agg_result.get_summary();
245 EXPECT_GT(summary.count, 0);
246 EXPECT_GE(summary.min_value, 10.0); // Should be in the queried range
247 EXPECT_LE(summary.max_value, 50.0);
248}
@ summary
Pre-calculated quantiles and count/sum.
std::chrono::milliseconds step
std::chrono::system_clock::time_point start_time
std::chrono::system_clock::time_point end_time

References kcenon::monitoring::time_series::create(), kcenon::monitoring::time_series_query::end_time, kcenon::monitoring::time_series_query::start_time, kcenon::monitoring::time_series_query::step, and kcenon::monitoring::summary.

Here is the call graph for this function: