@cryptotaxi247 / netdata-1 / commits / 26b563cfa

Add an obsoletion time for statsd private charts (#16269)

add an obsoletion time for private charts

Emmanuel Vasilakis committed Nov 7, 2023 at 11:35 UTC 26b563cfad9f9222c948e502f0b87cdaa2663287
2 files changed +71 -17
collectors/statsd.plugin/README.md
+4 -6
@@ -173,8 +173,8 @@ You can find the configuration at `/etc/netdata/netdata.conf`:
173 # update every (flushInterval) = 1
174 # udp messages to process at once = 10
175 # create private charts for metrics matching = *
176 - # max private charts allowed = 200
176 # max private charts hard limit = 1000
177 + # cleanup obsolete charts after secs = 0
178 # private charts memory mode = save
179 # private charts history = 3996
180 # histograms and timers percentile (percentThreshold) = 95.00000
@@ -234,13 +234,11 @@ The default behavior is to use the same settings as the rest of the Netdata Agen
234 - `private charts memory mode`
235 - `private charts history`
236
237 -### Optimize private metric charts visualization and storage
237 +### Optimize private metric charts storage
238
239 -If you have thousands of metrics, each with its own private chart, you may notice that your web browser becomes slow when you view the Netdata dashboard (this is a web browser issue we need to address at the Netdata UI). So, Netdata has a protection to stop creating charts when `max private charts allowed = 200` (soft limit) is reached.
239 +For optimization reasons, Netdata imposes a hard limit on private metric charts. The limit is set via the `max private charts hard limit` setting (which defaults to 1000 charts). Metrics above this hard limit are still collected, but they can only be used in synthetic charts (once a metric is added to chart, it will be sent to backend servers too).
240
241 -The metrics above this soft limit are still processed by Netdata, can be used in synthetic charts and will be available to be sent to backend time-series databases, up to `max private charts hard limit = 1000`. So, between 200 and 1000 charts, Netdata will still generate charts, but they will automatically be created with `memory mode = none` (Netdata will not maintain a database for them). These metrics will be sent to backend time series databases, if the backend configuration is set to `as collected`.
242 -
243 -Metrics above the hard limit are still collected, but they can only be used in synthetic charts (once a metric is added to chart, it will be sent to backend servers too).
241 +If you have many ephemeral metrics collected (i.e. that you collect values for a certain amount of time), you can set the configuration option `set charts as obsolete after secs`. Setting a value in seconds here, means that Netdata will mark those metrics (and their private charts) as obsolete after the specified time has passed since the last sent metric value. Those charts will later be deleted according to the setting in `cleanup obsolete charts after secs`. Setting `set charts as obsolete after secs` to 0 (which is also the default value) will disable this functionality.
242
243 Example private charts (automatically generated without any configuration):
244
collectors/statsd.plugin/statsd.c
+67 -11
@@ -95,6 +95,7 @@ typedef enum __attribute__((packed)) statsd_metric_options {
95 STATSD_METRIC_OPTION_USEFUL = 0x00000080, // set when the charting thread finds the metric useful (i.e. used in a chart)
96 STATSD_METRIC_OPTION_COLLECTION_FULL_LOGGED = 0x00000100, // set when the collection is full for this metric
97 STATSD_METRIC_OPTION_UPDATED_CHART_METADATA = 0x00000200, // set when the private chart metadata have been updated via tags
98 + STATSD_METRIC_OPTION_OBSOLETE = 0x00004000, // set when the metric is obsoleted
99 } STATS_METRIC_OPTIONS;
100
101 typedef enum __attribute__((packed)) statsd_metric_type {
@@ -117,6 +118,7 @@ typedef struct statsd_metric {
118 // metadata about data collection
119 collected_number events; // the number of times this metric has been collected (never resets)
120 uint32_t count; // the number of times this metric has been collected since the last flush
121 + time_t last_collected; // timestamp of the last incoming value
122
123 // the actual collected data
124 union {
@@ -268,6 +270,7 @@ static struct statsd {
270 collected_number decimal_detail;
271 uint32_t private_charts;
272 uint32_t max_private_charts_hard;
273 + uint32_t set_obsolete_after;
274
275 STATSD_APP *apps;
276 uint32_t recvmmsg_size;
@@ -476,6 +479,16 @@ static inline int value_is_zinit(const char *value) {
479 #define is_metric_checked(m) ((m)->options & STATSD_METRIC_OPTION_CHECKED)
480 #define is_metric_useful_for_collection(m) (!is_metric_checked(m) || ((m)->options & STATSD_METRIC_OPTION_USEFUL))
481
482 +static inline void metric_update_counters_and_obsoletion(STATSD_METRIC *m) {
483 + m->events++;
484 + m->count++;
485 + m->last_collected = now_realtime_sec();
486 + if (m->st && unlikely(rrdset_flag_check(m->st, RRDSET_FLAG_OBSOLETE))) {
487 + rrdset_isnot_obsolete(m->st);
488 + m->options &= ~STATSD_METRIC_OPTION_OBSOLETE;
489 + }
490 +}
491 +
492 static inline void statsd_process_gauge(STATSD_METRIC *m, const char *value, const char *sampling) {
493 if(!is_metric_useful_for_collection(m)) return;
494
@@ -498,8 +511,7 @@ static inline void statsd_process_gauge(STATSD_METRIC *m, const char *value, con
511 else
512 m->gauge.value = statsd_parse_float(value, 1.0);
513
501 - m->events++;
502 - m->count++;
514 + metric_update_counters_and_obsoletion(m);
515 }
516 }
517
@@ -516,8 +528,7 @@ static inline void statsd_process_counter_or_meter(STATSD_METRIC *m, const char
528 else {
529 m->counter.value += llrintndd((NETDATA_DOUBLE) statsd_parse_int(value, 1) / statsd_parse_sampling_rate(sampling));
530
519 - m->events++;
520 - m->count++;
531 + metric_update_counters_and_obsoletion(m);
532 }
533 }
534
@@ -559,8 +570,7 @@ static inline void statsd_process_histogram_or_timer(STATSD_METRIC *m, const cha
570 m->histogram.ext->values[m->histogram.ext->used++] = v;
571 }
572
562 - m->events++;
563 - m->count++;
573 + metric_update_counters_and_obsoletion(m);
574 }
575 }
576
@@ -597,8 +607,7 @@ static inline void statsd_process_set(STATSD_METRIC *m, const char *value) {
607 #else
608 dictionary_set(m->set.dict, value, NULL, 0);
609 #endif
600 - m->events++;
601 - m->count++;
610 + metric_update_counters_and_obsoletion(m);
611 }
612 }
613
@@ -630,8 +639,7 @@ static inline void statsd_process_dictionary(STATSD_METRIC *m, const char *value
639 }
640
641 t->count++;
633 - m->events++;
634 - m->count++;
642 + metric_update_counters_and_obsoletion(m);
643 }
644 }
645
@@ -1627,6 +1635,9 @@ static inline RRDSET *statsd_private_rrdset_create(
1635 static inline void statsd_private_chart_gauge(STATSD_METRIC *m) {
1636 netdata_log_debug(D_STATSD, "updating private chart for gauge metric '%s'", m->name);
1637
1638 + if(m->st && unlikely(rrdset_flag_check(m->st, RRDSET_FLAG_OBSOLETE)))
1639 + return;
1640 +
1641 if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) {
1642 m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA;
1643
@@ -1667,6 +1678,9 @@ static inline void statsd_private_chart_gauge(STATSD_METRIC *m) {
1678 static inline void statsd_private_chart_counter_or_meter(STATSD_METRIC *m, const char *dim, const char *family) {
1679 netdata_log_debug(D_STATSD, "updating private chart for %s metric '%s'", dim, m->name);
1680
1681 + if(m->st && unlikely(rrdset_flag_check(m->st, RRDSET_FLAG_OBSOLETE)))
1682 + return;
1683 +
1684 if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) {
1685 m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA;
1686
@@ -1707,6 +1721,9 @@ static inline void statsd_private_chart_counter_or_meter(STATSD_METRIC *m, const
1721 static inline void statsd_private_chart_set(STATSD_METRIC *m) {
1722 netdata_log_debug(D_STATSD, "updating private chart for set metric '%s'", m->name);
1723
1724 + if(m->st && unlikely(rrdset_flag_check(m->st, RRDSET_FLAG_OBSOLETE)))
1725 + return;
1726 +
1727 if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) {
1728 m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA;
1729
@@ -1747,6 +1764,9 @@ static inline void statsd_private_chart_set(STATSD_METRIC *m) {
1764 static inline void statsd_private_chart_dictionary(STATSD_METRIC *m) {
1765 netdata_log_debug(D_STATSD, "updating private chart for dictionary metric '%s'", m->name);
1766
1767 + if(m->st && unlikely(rrdset_flag_check(m->st, RRDSET_FLAG_OBSOLETE)))
1768 + return;
1769 +
1770 if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) {
1771 m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA;
1772
@@ -1790,6 +1810,9 @@ static inline void statsd_private_chart_dictionary(STATSD_METRIC *m) {
1810 static inline void statsd_private_chart_timer_or_histogram(STATSD_METRIC *m, const char *dim, const char *family, const char *units) {
1811 netdata_log_debug(D_STATSD, "updating private chart for %s metric '%s'", dim, m->name);
1812
1813 + if(m->st && unlikely(rrdset_flag_check(m->st, RRDSET_FLAG_OBSOLETE)))
1814 + return;
1815 +
1816 if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) {
1817 m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA;
1818
@@ -1842,6 +1865,16 @@ static inline void statsd_private_chart_timer_or_histogram(STATSD_METRIC *m, con
1865 // --------------------------------------------------------------------------------------------------------------------
1866 // statsd flush metrics
1867
1868 +static inline void metric_check_obsoletion(STATSD_METRIC *m) {
1869 + if(statsd.set_obsolete_after &&
1870 + !rrdset_flag_check(m->st, RRDSET_FLAG_OBSOLETE) &&
1871 + m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED &&
1872 + m->last_collected + statsd.set_obsolete_after < now_realtime_sec()) {
1873 + rrdset_is_obsolete(m->st);
1874 + m->options |= STATSD_METRIC_OPTION_OBSOLETE;
1875 + }
1876 +}
1877 +
1878 static inline void statsd_flush_gauge(STATSD_METRIC *m) {
1879 netdata_log_debug(D_STATSD, "flushing gauge metric '%s'", m->name);
1880
@@ -1855,6 +1888,8 @@ static inline void statsd_flush_gauge(STATSD_METRIC *m) {
1888
1889 if(unlikely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))))
1890 statsd_private_chart_gauge(m);
1891 +
1892 + metric_check_obsoletion(m);
1893 }
1894
1895 static inline void statsd_flush_counter_or_meter(STATSD_METRIC *m, const char *dim, const char *family) {
@@ -1870,6 +1905,8 @@ static inline void statsd_flush_counter_or_meter(STATSD_METRIC *m, const char *d
1905
1906 if(unlikely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))))
1907 statsd_private_chart_counter_or_meter(m, dim, family);
1908 +
1909 + metric_check_obsoletion(m);
1910 }
1911
1912 static inline void statsd_flush_counter(STATSD_METRIC *m) {
@@ -1896,6 +1933,8 @@ static inline void statsd_flush_set(STATSD_METRIC *m) {
1933
1934 if(unlikely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))))
1935 statsd_private_chart_set(m);
1936 +
1937 + metric_check_obsoletion(m);
1938 }
1939
1940 static inline void statsd_flush_dictionary(STATSD_METRIC *m) {
@@ -1924,6 +1963,8 @@ static inline void statsd_flush_dictionary(STATSD_METRIC *m) {
1963 dictionary_entries(m->dictionary.dict));
1964 }
1965 }
1966 +
1967 + metric_check_obsoletion(m);
1968 }
1969
1970 static inline void statsd_flush_timer_or_histogram(STATSD_METRIC *m, const char *dim, const char *family, const char *units) {
@@ -1977,6 +2018,8 @@ static inline void statsd_flush_timer_or_histogram(STATSD_METRIC *m, const char
2018
2019 if(unlikely(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))))
2020 statsd_private_chart_timer_or_histogram(m, dim, family, units);
2021 +
2022 + metric_check_obsoletion(m);
2023 }
2024
2025 static inline void statsd_flush_timer(STATSD_METRIC *m) {
@@ -2326,8 +2369,20 @@ static inline void statsd_flush_index_metrics(STATSD_INDEX *index, void (*flush_
2369 dfe_done(m);
2370
2371 // flush all the useful metrics
2329 - for(m = index->first_useful; m ; m = m->next_useful) {
2372 + STATSD_METRIC *m_prev;
2373 + for(m_prev = m = index->first_useful; m ; m = m->next_useful) {
2374 flush_metric(m);
2375 + if (m->options & STATSD_METRIC_OPTION_OBSOLETE) {
2376 + if (m == index->first_useful)
2377 + index->first_useful = m->next_useful;
2378 + else
2379 + m_prev->next_useful = m->next_useful;
2380 + dictionary_del(index->dict, m->name);
2381 + index->useful--;
2382 + index->metrics--;
2383 + statsd.private_charts--;
2384 + } else
2385 + m_prev = m;
2386 }
2387 }
2388
@@ -2447,6 +2502,7 @@ void *statsd_main(void *ptr) {
2502 config_get(CONFIG_SECTION_STATSD, "create private charts for metrics matching", "*"), NULL,
2503 SIMPLE_PATTERN_EXACT, true);
2504 statsd.max_private_charts_hard = (size_t)config_get_number(CONFIG_SECTION_STATSD, "max private charts hard limit", (long long)statsd.max_private_charts_hard);
2505 + statsd.set_obsolete_after = (size_t)config_get_number(CONFIG_SECTION_STATSD, "set charts as obsolete after secs", (long long)statsd.set_obsolete_after);
2506 statsd.decimal_detail = (collected_number)config_get_number(CONFIG_SECTION_STATSD, "decimal detail", (long long int)statsd.decimal_detail);
2507 statsd.tcp_idle_timeout = (size_t) config_get_number(CONFIG_SECTION_STATSD, "disconnect idle tcp clients after seconds", (long long int)statsd.tcp_idle_timeout);
2508 statsd.private_charts_hidden = (unsigned int)config_get_boolean(CONFIG_SECTION_STATSD, "private charts hidden", statsd.private_charts_hidden);