From b2e45c64b32de014f396eb321bae48d973fe5d23 Mon Sep 17 00:00:00 2001 From: Phillip Whelan Date: Wed, 5 Nov 2025 17:54:59 -0300 Subject: [PATCH 1/4] map: add cmt_map_metrics_expire for expiring metrics older than the passed expiration timestamp. Signed-off-by: Phillip Whelan --- include/cmetrics/cmt_map.h | 3 +++ src/cmt_map.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/cmetrics/cmt_map.h b/include/cmetrics/cmt_map.h index f67ee155..3e18efce 100644 --- a/include/cmetrics/cmt_map.h +++ b/include/cmetrics/cmt_map.h @@ -57,6 +57,9 @@ int cmt_map_metric_get_val(struct cmt_opts *opts, struct cmt_map *map, double *out_val); void cmt_map_metric_destroy(struct cmt_metric *metric); +int cmt_map_metrics_expire(struct cmt_map *, uint64_t); + void destroy_label_list(struct cfl_list *label_list); + #endif diff --git a/src/cmt_map.c b/src/cmt_map.c index 4639124a..6fe16fd8 100644 --- a/src/cmt_map.c +++ b/src/cmt_map.c @@ -316,3 +316,19 @@ void destroy_label_list(struct cfl_list *label_list) } } +/* This function can be used to expire untouched metrics. + */ +int cmt_map_metrics_expire(struct cmt_map *map, uint64_t expiration) +{ + struct cfl_list *tmp; + struct cfl_list *head; + struct cmt_metric *metric; + + cfl_list_foreach_safe(head, tmp, &map->metrics) { + metric = cfl_list_entry(head, struct cmt_metric, _head); + if (metric->timestamp <= expiration) { + cmt_map_metric_destroy(metric); + } + } + return 0; +} From 079a46274afc98165d2426ac5ddb79c0690f4d5f Mon Sep 17 00:00:00 2001 From: Phillip Whelan Date: Wed, 5 Nov 2025 17:55:50 -0300 Subject: [PATCH 2/4] cmetrics: use cmt_map_metrics_expire to expire context wide metrics. Signed-off-by: Phillip Whelan --- include/cmetrics/cmetrics.h | 1 + src/cmetrics.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/cmetrics/cmetrics.h b/include/cmetrics/cmetrics.h index 75ef78d6..797e770f 100644 --- a/include/cmetrics/cmetrics.h +++ b/include/cmetrics/cmetrics.h @@ -78,5 +78,6 @@ struct cmt *cmt_create(); void cmt_destroy(struct cmt *cmt); int cmt_label_add(struct cmt *cmt, char *key, char *val); char *cmt_version(); +void cmt_expire(struct cmt *cmt, uint64_t expiration); #endif diff --git a/src/cmetrics.c b/src/cmetrics.c index 9817809d..2e8a17f1 100644 --- a/src/cmetrics.c +++ b/src/cmetrics.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -136,6 +137,42 @@ void cmt_destroy(struct cmt *cmt) free(cmt); } +void cmt_expire(struct cmt *cmt, uint64_t expiration) +{ + struct cfl_list *tmp; + struct cfl_list *head; + struct cmt_counter *c; + struct cmt_gauge *g; + struct cmt_summary *s; + struct cmt_histogram *h; + struct cmt_untyped *u; + + cfl_list_foreach_safe(head, tmp, &cmt->counters) { + c = cfl_list_entry(head, struct cmt_counter, _head); + cmt_map_metrics_expire(c->map, expiration); + } + + cfl_list_foreach_safe(head, tmp, &cmt->gauges) { + g = cfl_list_entry(head, struct cmt_gauge, _head); + cmt_map_metrics_expire(g->map, expiration); + } + + cfl_list_foreach_safe(head, tmp, &cmt->summaries) { + s = cfl_list_entry(head, struct cmt_summary, _head); + cmt_map_metrics_expire(s->map, expiration); + } + + cfl_list_foreach_safe(head, tmp, &cmt->histograms) { + h = cfl_list_entry(head, struct cmt_histogram, _head); + cmt_map_metrics_expire(s->map, expiration); + } + + cfl_list_foreach_safe(head, tmp, &cmt->untypeds) { + u = cfl_list_entry(head, struct cmt_untyped, _head); + cmt_map_metrics_expire(u->map, expiration); + } +} + int cmt_label_add(struct cmt *cmt, char *key, char *val) { return cmt_labels_add_kv(cmt->static_labels, key, val); From 5822c9438cc987bc401ef218aa854560da133985 Mon Sep 17 00:00:00 2001 From: Phillip Whelan Date: Wed, 5 Nov 2025 18:43:43 -0300 Subject: [PATCH 3/4] cmetrics: fix typo where wrong pointer was used to expire histograms. Signed-off-by: Phillip Whelan --- src/cmetrics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmetrics.c b/src/cmetrics.c index 2e8a17f1..1ec01ffd 100644 --- a/src/cmetrics.c +++ b/src/cmetrics.c @@ -164,7 +164,7 @@ void cmt_expire(struct cmt *cmt, uint64_t expiration) cfl_list_foreach_safe(head, tmp, &cmt->histograms) { h = cfl_list_entry(head, struct cmt_histogram, _head); - cmt_map_metrics_expire(s->map, expiration); + cmt_map_metrics_expire(h->map, expiration); } cfl_list_foreach_safe(head, tmp, &cmt->untypeds) { From ae8ca7e41404a8d5342d6edd738f10905b6589f4 Mon Sep 17 00:00:00 2001 From: Phillip Whelan Date: Wed, 5 Nov 2025 18:52:27 -0300 Subject: [PATCH 4/4] expire: add tests for expiring counters, gauges, summaries and histograms. Signed-off-by: Phillip Whelan --- tests/CMakeLists.txt | 14 +++- tests/expire.c | 185 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 tests/expire.c diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c71f8a80..e2fc0469 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,7 @@ set(UNIT_TESTS_FILES issues.c null_label.c filter.c + expire.c ) if (CMT_BUILD_PROMETHEUS_TEXT_DECODER) @@ -39,11 +40,16 @@ foreach(source_file ${UNIT_TESTS_FILES}) encode_output.c ) - target_link_libraries(${source_file_we} cmetrics-static cfl-static fluent-otel-proto) + target_link_libraries(${source_file_we} cmetrics-static fluent-otel-proto) + if (CMT_HAVE_CFL) + target_link_libraries(${source_file_we} cfl) + else() + target_link_libraries(${source_file_we} cfl-static) + endif() -if(NOT CMT_SYSTEM_WINDOWS) - target_link_libraries(${source_file_we} pthread) -endif() + if(NOT CMT_SYSTEM_WINDOWS) + target_link_libraries(${source_file_we} pthread) + endif() add_test(NAME ${source_file_we} COMMAND ${CMAKE_BINARY_DIR}/tests/${source_file_we} diff --git a/tests/expire.c b/tests/expire.c new file mode 100644 index 00000000..170515c1 --- /dev/null +++ b/tests/expire.c @@ -0,0 +1,185 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CMetrics + * ======== + * Copyright 2021-2022 The CMetrics Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cmt_tests.h" + +void test_expire_counter() +{ + uint64_t ts; + struct cmt *cmt; + struct cmt_counter *c; + + cmt_initialize(); + + cmt = cmt_create(); + TEST_CHECK(cmt != NULL); + + /* Create a counter metric type */ + c = cmt_counter_create(cmt, "k8s", "network", "uptime", "Network Uptime", 1, (char *[]) {"host"}); + TEST_CHECK(c != NULL); + + /* Timestamp */ + ts = cfl_time_now(); + + cmt_counter_inc(c, ts, 1, (char *[]){"valid"}); + cmt_counter_inc(c, ts-10, 1, (char *[]){"expire"}); + + TEST_CHECK(cfl_list_size(&c->map->metrics) == 2); + cmt_expire(cmt, ts-1); + TEST_CHECK(cfl_list_size(&c->map->metrics) == 1); + + cmt_destroy(cmt); +} + +void test_expire_gauge() +{ + uint64_t ts; + struct cmt *cmt; + struct cmt_gauge *g; + + cmt_initialize(); + + cmt = cmt_create(); + TEST_CHECK(cmt != NULL); + + /* Create a gauge metric type */ + g = cmt_gauge_create(cmt, "k8s", "network", "load", "Network load", 1, (char *[]) {"host"}); + TEST_CHECK(g != NULL); + + /* Timestamp */ + ts = cfl_time_now(); + + cmt_gauge_set(g, ts, 50, 1, (char *[]){"valid"}); + cmt_gauge_set(g, ts-10, 50, 1, (char *[]){"expire"}); + + TEST_CHECK(cfl_list_size(&g->map->metrics) == 2); + cmt_expire(cmt, ts-1); + TEST_CHECK(cfl_list_size(&g->map->metrics) == 1); + + cmt_destroy(cmt); +} + +void test_expire_histogram() +{ + uint64_t ts; + struct cmt *cmt; + struct cmt_histogram *h; + struct cmt_histogram_buckets *buckets; + + cmt_initialize(); + + cmt = cmt_create(); + TEST_CHECK(cmt != NULL); + + /* Create buckets */ + buckets = cmt_histogram_buckets_create(11, + 0.005, 0.01, 0.025, 0.05, + 0.1, 0.25, 0.5, 1.0, 2.5, + 5.0, 10.0); + TEST_CHECK(buckets != NULL); + + /* Create a histogram metric type */ + h = cmt_histogram_create(cmt, + "k8s", "network", "uptime", "Network Uptime", + buckets, + 1, (char *[]) {"host"}); + TEST_CHECK(h != NULL); + + /* Timestamp */ + ts = cfl_time_now(); + + cmt_histogram_observe(h, ts, 1.0, 1, (char *[]){"valid"}); + cmt_histogram_observe(h, ts-10, 1.0, 1, (char *[]){"expire"}); + + TEST_CHECK(cfl_list_size(&h->map->metrics) == 2); + cmt_expire(cmt, ts-1); + TEST_CHECK(cfl_list_size(&h->map->metrics) == 1); + + cmt_destroy(cmt); +} + +void test_expire_summary() +{ + uint64_t ts; + struct cmt *cmt; + struct cmt_summary *s; + double quantiles[6]; + double revised[6]; + double sum; + uint64_t count; + + /* set quantiles, no labels */ + quantiles[0] = 0.1; + quantiles[1] = 0.2; + quantiles[2] = 0.3; + quantiles[3] = 0.4; + quantiles[4] = 0.5; + quantiles[5] = 1.0; + + revised[0] = 1.0; + revised[1] = 2.0; + revised[2] = 3.0; + revised[3] = 4.0; + revised[4] = 5.0; + revised[5] = 6.0; + + count = 10; + sum = 51.612894511314444; + + cmt_initialize(); + + cmt = cmt_create(); + TEST_CHECK(cmt != NULL); + + /* Create a summary metric type */ + s = cmt_summary_create(cmt, + "k8s", "network", "uptime", "Network Uptime", + 6, + quantiles, + 1, (char *[]) {"host"}); + TEST_CHECK(s != NULL); + + /* Timestamp */ + ts = cfl_time_now(); + + cmt_summary_set_default(s, ts, revised, sum, count, 1, (char *[]){"valid"}); + cmt_summary_set_default(s, ts-10, revised, sum, count, 1, (char *[]){"expire"}); + + TEST_CHECK(cfl_list_size(&s->map->metrics) == 2); + cmt_expire(cmt, ts-1); + TEST_CHECK(cfl_list_size(&s->map->metrics) == 1); + + cmt_destroy(cmt); +} + +TEST_LIST = { + {"expire_counter" , test_expire_counter}, + {"expire_gauge" , test_expire_gauge}, + {"expire_histogram" , test_expire_histogram}, + {"expire_summary", test_expire_summary}, + { 0 } +};