@cryptotaxi247 / netdata-1 / commits / 5247096c8

Collected and available metrics, instances and contexts (#19094)

* make sure there is a valid first_time_s after netdata starts for the first time * show detail in human depending on the time * show retention only when it is in the past * fix calculation of collected and available metrics and instances * recalculate metrics every 30 secs * recalculate every 33 seconds, but for the first 123 seconds do it every second * calculate available and collected metrics, instances and context, on the fly

Costa Tsaousis committed Nov 27, 2024 at 12:27 UTC 5247096c851baa7c4e1234d26351cd30fedc4de6
13 files changed +266 -139
src/database/contexts/api_v2_contexts.c
+4 -22
@@ -220,6 +220,9 @@ static void rrdhost_receiver_to_json(BUFFER *wb, RRDHOST_STATUS *s, const char *
220 buffer_json_member_add_string(wb, "status", rrdhost_ingest_status_to_string(s->ingest.status));
221 buffer_json_member_add_time_t(wb, "since", s->ingest.since);
222 buffer_json_member_add_time_t(wb, "age", s->now - s->ingest.since);
223 + buffer_json_member_add_uint64(wb, "metrics", s->ingest.collected.metrics);
224 + buffer_json_member_add_uint64(wb, "instances", s->ingest.collected.instances);
225 + buffer_json_member_add_uint64(wb, "contexts", s->ingest.collected.contexts);
226
227 if(s->ingest.type == RRDHOST_INGEST_TYPE_CHILD) {
228 if(s->ingest.status == RRDHOST_INGEST_STATUS_OFFLINE)
@@ -447,29 +450,8 @@ static void rrdcontext_to_json_v2_rrdhost(BUFFER *wb, RRDHOST *host, struct rrdc
450 buffer_json_member_add_string(wb, "mode", rrd_memory_mode_name(s.db.mode));
451 buffer_json_member_add_time_t(wb, "first_time", s.db.first_time_s);
452 buffer_json_member_add_time_t(wb, "last_time", s.db.last_time_s);
450 - buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
453
452 - spinlock_lock(&s.host->accounting.spinlock);
453 - int64_t count = 0;
454 -
455 - if (s.host->accounting.cache_timestamp &&
456 - ctl->now - s.host->accounting.cache_timestamp < host->rrd_update_every * 1.5)
457 - count = s.host->accounting.currently_collected;
458 - else {
459 - Pvoid_t *Pvalue;
460 - bool first = true;
461 - Word_t dimension_id = 0;
462 - while ((Pvalue = JudyLFirstThenNext(s.host->accounting.JudyL, &dimension_id, &first))) {
463 - RRDDIM *rd = *Pvalue;
464 - if (rd->collector.last_collected_time.tv_sec > ctl->now - (rd->rrdset->update_every * 2))
465 - count++;
466 - }
467 - s.host->accounting.currently_collected = count;
468 - s.host->accounting.cache_timestamp = ctl->now;
469 - }
470 - spinlock_unlock(&s.host->accounting.spinlock);
471 -
472 - buffer_json_member_add_uint64(wb, "currently_collected_metrics", count);
454 + buffer_json_member_add_uint64(wb, "metrics", s.db.metrics);
455 buffer_json_member_add_uint64(wb, "instances", s.db.instances);
456 buffer_json_member_add_uint64(wb, "contexts", s.db.contexts);
457 }
src/database/contexts/api_v2_contexts_agents.c
+54 -19
@@ -31,7 +31,12 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now
31
32 buffer_json_cloud_status(wb, now_s);
33
34 - size_t currently_collected_metrics = 0;
34 + size_t collected_metrics = 0;
35 + size_t collected_instances = 0;
36 + size_t collected_contexts = 0;
37 + size_t available_metrics = 0;
38 + size_t available_instances = 0;
39 + size_t available_contexts = 0;
40
41 buffer_json_member_add_object(wb, "nodes");
42 {
@@ -40,16 +45,23 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now
45 dfe_start_read(rrdhost_root_index, host) {
46 total++;
47
48 + available_metrics += __atomic_load_n(&host->rrdctx.metrics_count, __ATOMIC_RELAXED);
49 + available_instances += __atomic_load_n(&host->rrdctx.instances_count, __ATOMIC_RELAXED);
50 + available_contexts += __atomic_load_n(&host->rrdctx.contexts_count, __ATOMIC_RELAXED);
51 +
52 if(rrdhost_flag_check(host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED))
53 sending++;
54
46 - if(host != localhost) {
47 - if (rrdhost_is_online(host))
55 + if (rrdhost_is_online(host)) {
56 + collected_metrics += __atomic_load_n(&host->collected.metrics_count, __ATOMIC_RELAXED);
57 + collected_instances += __atomic_load_n(&host->collected.instances_count, __ATOMIC_RELAXED);
58 + collected_contexts += __atomic_load_n(&host->collected.contexts_count, __ATOMIC_RELAXED);
59 +
60 + if(host != localhost)
61 receiving++;
49 - else
50 - archived++;
62 }
52 - currently_collected_metrics += host->accounting.currently_collected;
63 + else
64 + archived++;
65 }
66 dfe_done(host);
67
@@ -60,6 +72,27 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now
72 }
73 buffer_json_object_close(wb); // nodes
74
75 + buffer_json_member_add_object(wb, "metrics");
76 + {
77 + buffer_json_member_add_uint64(wb, "collected", collected_metrics);
78 + buffer_json_member_add_uint64(wb, "available", available_metrics);
79 + }
80 + buffer_json_object_close(wb);
81 +
82 + buffer_json_member_add_object(wb, "instances");
83 + {
84 + buffer_json_member_add_uint64(wb, "collected", collected_instances);
85 + buffer_json_member_add_uint64(wb, "available", available_instances);
86 + }
87 + buffer_json_object_close(wb);
88 +
89 + buffer_json_member_add_object(wb, "contexts");
90 + {
91 + buffer_json_member_add_uint64(wb, "collected", collected_contexts);
92 + buffer_json_member_add_uint64(wb, "available", available_contexts);
93 + }
94 + buffer_json_object_close(wb);
95 +
96 agent_capabilities_to_json(wb, localhost, "capabilities");
97
98 buffer_json_member_add_object(wb, "api");
@@ -108,15 +141,23 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now
141 buffer_json_member_add_double(wb, "disk_percent", percent);
142 }
143
111 - if(first_time_s) {
144 + if(first_time_s < now_s) {
145 time_t retention = now_s - first_time_s;
146
147 buffer_json_member_add_time_t(wb, "from", first_time_s);
148 buffer_json_member_add_time_t(wb, "to", now_s);
149 buffer_json_member_add_time_t(wb, "retention", retention);
150
118 - duration_snprintf_hours(human_retention, sizeof(human_retention),
119 - (int)duration_round_to_resolution(retention, 3600));
151 + if(retention < 60)
152 + duration_snprintf_time_t(human_retention, sizeof(human_retention), retention);
153 + else if(retention < 24 * 60 * 60) {
154 + int64_t rounded_retention_mins = duration_round_to_resolution(retention, 60);
155 + duration_snprintf_mins(human_retention, sizeof(human_retention), rounded_retention_mins);
156 + }
157 + else {
158 + int64_t rounded_retention_hours = duration_round_to_resolution(retention, 3600);
159 + duration_snprintf_hours(human_retention, sizeof(human_retention), rounded_retention_hours);
160 + }
161
162 buffer_json_member_add_string(wb, "retention_human", human_retention);
163
@@ -128,13 +169,11 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now
169 time_t space_retention = (time_t)((NETDATA_DOUBLE)(now_s - first_time_s) * 100.0 / percent);
170 time_t actual_retention = MIN(space_retention, time_retention ? time_retention : space_retention);
171
131 - if (time_retention) {
132 - duration_snprintf_hours(human_retention, sizeof(human_retention),
133 - (int)duration_round_to_resolution(time_retention, 3600));
172 + duration_snprintf_hours(human_retention, sizeof(human_retention),
173 + (int)duration_round_to_resolution(time_retention, 3600));
174
135 - buffer_json_member_add_time_t(wb, "requested_retention", time_retention);
136 - buffer_json_member_add_string(wb, "requested_retention_human", human_retention);
137 - }
175 + buffer_json_member_add_time_t(wb, "requested_retention", time_retention);
176 + buffer_json_member_add_string(wb, "requested_retention_human", human_retention);
177
178 duration_snprintf_hours(human_retention, sizeof(human_retention),
179 (int)duration_round_to_resolution(actual_retention, 3600));
@@ -143,10 +182,6 @@ void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now
182 buffer_json_member_add_string(wb, "expected_retention_human", human_retention);
183 }
184 }
146 -
147 - if(currently_collected_metrics)
148 - buffer_json_member_add_uint64(wb, "currently_collected_metrics", currently_collected_metrics);
149 -
185 buffer_json_object_close(wb);
186 }
187 buffer_json_array_close(wb); // db_size
src/database/contexts/context.c
+10 -4
@@ -64,12 +64,12 @@ static void rrdcontext_insert_callback(const DICTIONARY_ITEM *item __maybe_unuse
64 rc->last_time_s = (time_t)rc->hub.last_time_s;
65
66 if(rc->hub.deleted || !rc->hub.first_time_s)
67 - rrd_flag_set_deleted(rc, RRD_FLAG_NONE);
67 + rrdcontext_set_deleted(rc, RRD_FLAG_NONE);
68 else {
69 if (rc->last_time_s == 0)
70 - rrd_flag_set_collected(rc);
70 + rrdcontext_set_collected(rc);
71 else
72 - rrd_flag_set_archived(rc);
72 + rrdcontext_set_archived(rc);
73 }
74
75 rc->flags |= RRD_FLAG_UPDATE_REASON_LOAD_SQL; // no need for atomics at constructor
@@ -82,6 +82,9 @@ static void rrdcontext_insert_callback(const DICTIONARY_ITEM *item __maybe_unuse
82 rrdinstances_create_in_rrdcontext(rc);
83 spinlock_init(&rc->spinlock);
84
85 + // update the count of contexts
86 + __atomic_add_fetch(&rc->rrdhost->rrdctx.contexts_count, 1, __ATOMIC_RELAXED);
87 +
88 // signal the react callback to do the job
89 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_NEW_OBJECT);
90 }
@@ -90,6 +93,9 @@ static void rrdcontext_delete_callback(const DICTIONARY_ITEM *item __maybe_unuse
93
94 RRDCONTEXT *rc = (RRDCONTEXT *)value;
95
96 + // update the count of contexts
97 + __atomic_sub_fetch(&rc->rrdhost->rrdctx.contexts_count, 1, __ATOMIC_RELAXED);
98 +
99 rrdinstances_destroy_from_rrdcontext(rc);
100 rrdcontext_freez(rc);
101 }
@@ -199,7 +205,7 @@ static bool rrdcontext_conflict_callback(const DICTIONARY_ITEM *item __maybe_unu
205 rrd_flag_set(rc, rc_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no need for atomics on rc_new
206
207 if(rrd_flag_is_collected(rc) && rrd_flag_is_archived(rc))
202 - rrd_flag_set_collected(rc);
208 + rrdcontext_set_collected(rc);
209
210 if(rrd_flag_is_updated(rc))
211 rrd_flag_set(rc, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT);
src/database/contexts/instance.c
+22 -12
@@ -122,6 +122,9 @@ static void rrdinstance_insert_callback(const DICTIONARY_ITEM *item __maybe_unus
122
123 rrdmetrics_create_in_rrdinstance(ri);
124
125 + // update the count of instances
126 + __atomic_add_fetch(&ri->rc->rrdhost->rrdctx.instances_count, 1, __ATOMIC_RELAXED);
127 +
128 // signal the react callback to do the job
129 rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_NEW_OBJECT);
130 }
@@ -131,6 +134,9 @@ static void rrdinstance_delete_callback(const DICTIONARY_ITEM *item __maybe_unus
134
135 internal_error(ri->rrdset, "RRDINSTANCE: '%s' is freed but there is a RRDSET linked to it.", string2str(ri->id));
136
137 + // update the count of instances
138 + __atomic_sub_fetch(&ri->rc->rrdhost->rrdctx.instances_count, 1, __ATOMIC_RELAXED);
139 +
140 rrdinstance_free(ri);
141 }
142
@@ -237,7 +243,7 @@ static bool rrdinstance_conflict_callback(const DICTIONARY_ITEM *item __maybe_un
243 rrd_flag_set(ri, ri_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no need for atomics on ri_new
244
245 if(rrd_flag_is_collected(ri) && rrd_flag_is_archived(ri))
240 - rrd_flag_set_collected(ri);
246 + rrdinstance_set_collected(ri);
247
248 if(rrd_flag_is_updated(ri))
249 rrd_flag_set(ri, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT);
@@ -290,7 +296,7 @@ void rrdinstance_trigger_updates(RRDINSTANCE *ri, const char *function) {
296 else if(unlikely(rrd_flag_is_collected(ri))) {
297 // there is no rrdset, but we have it as collected!
298
293 - rrd_flag_set_archived(ri);
299 + rrdinstance_set_archived(ri);
300 rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING);
301 }
302
@@ -362,7 +368,7 @@ inline void rrdinstance_from_rrdset(RRDSET *st) {
368 if (!rd->rrdcontexts.rrdmetric) continue;
369
370 RRDMETRIC *rm_old = rrdmetric_acquired_value(rd->rrdcontexts.rrdmetric);
365 - rrd_flags_replace(rm_old, RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
371 + rrdmetric_set_deleted_overwrite(rm_old, RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
372 rm_old->rrddim = NULL;
373 rm_old->first_time_s = 0;
374 rm_old->last_time_s = 0;
@@ -378,7 +384,7 @@ inline void rrdinstance_from_rrdset(RRDSET *st) {
384 if(!rrd_flag_check(ri_old, RRD_FLAG_OWN_LABELS))
385 ri_old->rrdlabels = rrdlabels_create();
386
381 - rrd_flags_replace(ri_old, RRD_FLAG_OWN_LABELS|RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
387 + rrdinstance_set_deleted_overwrite(ri_old, RRD_FLAG_UPDATED|RRD_FLAG_OWN_LABELS|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
388 ri_old->rrdset = NULL;
389 ri_old->first_time_s = 0;
390 ri_old->last_time_s = 0;
@@ -428,11 +434,17 @@ static inline RRDINSTANCE *rrdset_get_rrdinstance_with_trace(RRDSET *st, const c
434 return ri;
435 }
436
437 +static inline void rrdinstance_rrdset_not_collected(RRDSET *st) {
438 + st->rrdcontexts.collected = false;
439 +}
440 +
441 inline void rrdinstance_rrdset_is_freed(RRDSET *st) {
442 + rrdinstance_rrdset_not_collected(st);
443 +
444 RRDINSTANCE *ri = rrdset_get_rrdinstance(st);
445 if(unlikely(!ri)) return;
446
435 - rrd_flag_set_archived(ri);
447 + rrdinstance_set_archived(ri);
448
449 if(!rrd_flag_check(ri, RRD_FLAG_OWN_LABELS)) {
450 ri->rrdlabels = rrdlabels_create();
@@ -450,11 +462,10 @@ inline void rrdinstance_rrdset_is_freed(RRDSET *st) {
462
463 rrdcontext_release(st->rrdcontexts.rrdcontext);
464 st->rrdcontexts.rrdcontext = NULL;
453 - st->rrdcontexts.collected = false;
465 }
466
467 inline void rrdinstance_rrdset_has_updated_retention(RRDSET *st) {
457 - st->rrdcontexts.collected = false;
468 + rrdinstance_rrdset_not_collected(st);
469
470 RRDINSTANCE *ri = rrdset_get_rrdinstance(st);
471 if(unlikely(!ri)) return;
@@ -464,7 +475,7 @@ inline void rrdinstance_rrdset_has_updated_retention(RRDSET *st) {
475 }
476
477 inline void rrdinstance_updated_rrdset_name(RRDSET *st) {
467 - st->rrdcontexts.collected = false;
478 + rrdinstance_rrdset_not_collected(st);
479
480 // the chart may not be initialized when this is called
481 if(unlikely(!st->rrdcontexts.rrdinstance)) return;
@@ -502,13 +513,13 @@ inline void rrdinstance_updated_rrdset_flags_no_action(RRDINSTANCE *ri, RRDSET *
513 }
514
515 inline void rrdinstance_updated_rrdset_flags(RRDSET *st) {
505 - st->rrdcontexts.collected = false;
516 + rrdinstance_rrdset_not_collected(st);
517
518 RRDINSTANCE *ri = rrdset_get_rrdinstance(st);
519 if(unlikely(!ri)) return;
520
521 if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)))
511 - rrd_flag_set_archived(ri);
522 + rrdinstance_set_archived(ri);
523
524 rrdinstance_updated_rrdset_flags_no_action(ri, st);
525
@@ -532,11 +543,10 @@ inline void rrdinstance_collected_rrdset(RRDSET *st) {
543 rrdinstance_updated_rrdset_flags_no_action(ri, st);
544
545 if(unlikely(ri->internal.collected_metrics_count && !rrd_flag_is_collected(ri)))
535 - rrd_flag_set_collected(ri);
546 + rrdinstance_set_collected(ri);
547
548 // we use this variable to detect BEGIN/END without SET
549 ri->internal.collected_metrics_count = 0;
550
551 rrdinstance_trigger_updates(ri, __FUNCTION__ );
552 }
542 -
src/database/contexts/internal.h
+103 -23
@@ -17,19 +17,19 @@
17
18 #define LOG_TRANSITIONS false
19
20 -#define WORKER_JOB_HOSTS 1
21 -#define WORKER_JOB_CHECK 2
22 -#define WORKER_JOB_SEND 3
23 -#define WORKER_JOB_DEQUEUE 4
24 -#define WORKER_JOB_RETENTION 5
25 -#define WORKER_JOB_QUEUED 6
26 -#define WORKER_JOB_CLEANUP 7
27 -#define WORKER_JOB_CLEANUP_DELETE 8
28 -#define WORKER_JOB_PP_METRIC 9 // post-processing metrics
29 -#define WORKER_JOB_PP_INSTANCE 10 // post-processing instances
30 -#define WORKER_JOB_PP_CONTEXT 11 // post-processing contexts
31 -#define WORKER_JOB_HUB_QUEUE_SIZE 12
32 -#define WORKER_JOB_PP_QUEUE_SIZE 13
20 +#define WORKER_JOB_HOSTS 1
21 +#define WORKER_JOB_CHECK 2
22 +#define WORKER_JOB_SEND 3
23 +#define WORKER_JOB_DEQUEUE 4
24 +#define WORKER_JOB_RETENTION 5
25 +#define WORKER_JOB_QUEUED 6
26 +#define WORKER_JOB_CLEANUP 7
27 +#define WORKER_JOB_CLEANUP_DELETE 8
28 +#define WORKER_JOB_PP_METRIC 9 // post-processing metrics
29 +#define WORKER_JOB_PP_INSTANCE 10 // post-processing instances
30 +#define WORKER_JOB_PP_CONTEXT 11 // post-processing contexts
31 +#define WORKER_JOB_HUB_QUEUE_SIZE 12
32 +#define WORKER_JOB_PP_QUEUE_SIZE 13
33
34
35 typedef enum __attribute__ ((__packed__)) {
@@ -118,15 +118,14 @@ extern struct rrdcontext_reason rrdcontext_reasons[];
118 #define rrd_flag_check_all(obj, flag) (rrd_flag_check(obj, flag) == (flag))
119
120 // set one or more flags (bits)
121 +// NEVER alter RRD_FLAG_COLLECTED, RRD_FLAG_ARCHIVED, RRD_FLAG_DELETED with this
122 #define rrd_flag_set(obj, flag) __atomic_or_fetch(&((obj)->flags), flag, __ATOMIC_SEQ_CST)
123
124 // clear one or more flags (bits)
125 +// NEVER alter RRD_FLAG_COLLECTED, RRD_FLAG_ARCHIVED, RRD_FLAG_DELETED with this
126 #define rrd_flag_clear(obj, flag) __atomic_and_fetch(&((obj)->flags), ~(flag), __ATOMIC_SEQ_CST)
127
126 -// replace the flags of an object, with the supplied ones
127 -#define rrd_flags_replace(obj, all_flags) __atomic_store_n(&((obj)->flags), all_flags, __ATOMIC_SEQ_CST)
128 -
129 -static inline void
128 +static inline RRD_FLAGS
129 rrd_flag_add_remove_atomic(RRD_FLAGS *flags, RRD_FLAGS check, RRD_FLAGS conditionally_add, RRD_FLAGS always_remove) {
130 RRD_FLAGS expected, desired;
131
@@ -140,6 +139,19 @@ rrd_flag_add_remove_atomic(RRD_FLAGS *flags, RRD_FLAGS check, RRD_FLAGS conditio
139 desired |= (check | conditionally_add);
140
141 } while(!__atomic_compare_exchange_n(flags, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
142 +
143 + return expected;
144 +}
145 +
146 +static inline RRD_FLAGS
147 +rrd_flags_replace_atomic(RRD_FLAGS *flags, RRD_FLAGS desired) {
148 + RRD_FLAGS expected;
149 +
150 + do {
151 + expected = *flags;
152 + } while(!__atomic_compare_exchange_n(flags, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
153 +
154 + return expected;
155 }
156
157 #define rrd_flag_set_collected(obj) \
@@ -266,25 +278,93 @@ typedef struct rrdcontext {
278
279 struct {
280 RRD_FLAGS queued_flags; // the last flags that triggered the post-processing
281 + size_t executions; // how many times this context has been processed
282 usec_t queued_ut; // the last time this was queued
283 usec_t dequeued_ut; // the last time we sent (or deduplicated) this context
271 - size_t executions; // how many times this context has been processed
284 } pp;
285
286 struct {
287 RRD_FLAGS queued_flags; // the last flags that triggered the queueing
288 + size_t dispatches; // the number of times this has been dispatched to hub
289 usec_t queued_ut; // the last time this was queued
290 usec_t delay_calc_ut; // the last time we calculated the scheduled_dispatched_ut
291 usec_t scheduled_dispatch_ut; // the time it was/is scheduled to be sent
292 usec_t dequeued_ut; // the last time we sent (or deduplicated) this context
280 - size_t dispatches; // the number of times this has been dispatched to hub
293 } queue;
282 -
283 - struct {
284 - uint32_t metrics; // the number of metrics in this context
285 - } stats;
294 } RRDCONTEXT;
295
296 +// ----------------------------------------------------------------------------
297 +// helpers for counting collected metrics, instances and contexts
298 +
299 +static inline void rrdmetric_set_collected(RRDMETRIC *rm) {
300 + RRD_FLAGS old = rrd_flag_set_collected(rm);
301 + if(!(old & RRD_FLAG_COLLECTED))
302 + __atomic_add_fetch(&rm->ri->rc->rrdhost->collected.metrics_count, 1, __ATOMIC_RELAXED);
303 +}
304 +
305 +static inline void rrdmetric_set_archived(RRDMETRIC *rm) {
306 + RRD_FLAGS old = rrd_flag_set_archived(rm);
307 + if(old & RRD_FLAG_COLLECTED)
308 + __atomic_sub_fetch(&rm->ri->rc->rrdhost->collected.metrics_count, 1, __ATOMIC_RELAXED);
309 +}
310 +
311 +static inline void rrdmetric_set_deleted(RRDMETRIC *rm, RRD_FLAGS reason) {
312 + RRD_FLAGS old = rrd_flag_set_deleted(rm, reason);
313 + if(old & RRD_FLAG_COLLECTED)
314 + __atomic_sub_fetch(&rm->ri->rc->rrdhost->collected.metrics_count, 1, __ATOMIC_RELAXED);
315 +}
316 +
317 +static inline void rrdmetric_set_deleted_overwrite(RRDMETRIC *rm, RRD_FLAGS replacement) {
318 + replacement &= ~(RRD_FLAG_COLLECTED|RRD_FLAG_ARCHIVED|RRD_FLAG_DELETED);
319 + replacement |= RRD_FLAG_DELETED;
320 + RRD_FLAGS old = rrd_flags_replace_atomic(&rm->flags, replacement);
321 + if(old & RRD_FLAG_COLLECTED)
322 + __atomic_sub_fetch(&rm->ri->rc->rrdhost->collected.metrics_count, 1, __ATOMIC_RELAXED);
323 +}
324 +
325 +static inline void rrdinstance_set_collected(RRDINSTANCE *ri) {
326 + RRD_FLAGS old = rrd_flag_set_collected(ri);
327 + if(!(old & RRD_FLAG_COLLECTED))
328 + __atomic_add_fetch(&ri->rc->rrdhost->collected.instances_count, 1, __ATOMIC_RELAXED);
329 +}
330 +
331 +static inline void rrdinstance_set_archived(RRDINSTANCE *ri) {
332 + RRD_FLAGS old = rrd_flag_set_archived(ri);
333 + if(old & RRD_FLAG_COLLECTED)
334 + __atomic_sub_fetch(&ri->rc->rrdhost->collected.instances_count, 1, __ATOMIC_RELAXED);
335 +}
336 +
337 +static inline void rrdinstance_set_deleted(RRDINSTANCE *ri, RRD_FLAGS reason) {
338 + RRD_FLAGS old = rrd_flag_set_deleted(ri, reason);
339 + if(old & RRD_FLAG_COLLECTED)
340 + __atomic_sub_fetch(&ri->rc->rrdhost->collected.instances_count, 1, __ATOMIC_RELAXED);
341 +}
342 +
343 +static inline void rrdinstance_set_deleted_overwrite(RRDINSTANCE *ri, RRD_FLAGS replacement) {
344 + replacement &= ~(RRD_FLAG_COLLECTED|RRD_FLAG_ARCHIVED|RRD_FLAG_DELETED);
345 + replacement |= RRD_FLAG_DELETED;
346 + RRD_FLAGS old = rrd_flags_replace_atomic(&ri->flags, replacement);
347 + if(old & RRD_FLAG_COLLECTED)
348 + __atomic_sub_fetch(&ri->rc->rrdhost->collected.instances_count, 1, __ATOMIC_RELAXED);
349 +}
350 +
351 +static inline void rrdcontext_set_collected(RRDCONTEXT *rc) {
352 + RRD_FLAGS old = rrd_flag_set_collected(rc);
353 + if(!(old & RRD_FLAG_COLLECTED))
354 + __atomic_add_fetch(&rc->rrdhost->collected.contexts_count, 1, __ATOMIC_RELAXED);
355 +}
356 +
357 +static inline void rrdcontext_set_archived(RRDCONTEXT *rc) {
358 + RRD_FLAGS old = rrd_flag_set_archived(rc);
359 + if(old & RRD_FLAG_COLLECTED)
360 + __atomic_sub_fetch(&rc->rrdhost->collected.contexts_count, 1, __ATOMIC_RELAXED);
361 +}
362 +
363 +static inline void rrdcontext_set_deleted(RRDCONTEXT *rc, RRD_FLAGS reason) {
364 + RRD_FLAGS old = rrd_flag_set_deleted(rc, reason);
365 + if(old & RRD_FLAG_COLLECTED)
366 + __atomic_sub_fetch(&rc->rrdhost->collected.contexts_count, 1, __ATOMIC_RELAXED);
367 +}
368
369 // ----------------------------------------------------------------------------
370 // helper one-liners for RRDMETRIC
src/database/contexts/metric.c
+12 -6
@@ -52,7 +52,7 @@ inline time_t rrdmetric_acquired_first_entry(RRDMETRIC_ACQUIRED *rma) {
52 inline time_t rrdmetric_acquired_last_entry(RRDMETRIC_ACQUIRED *rma) {
53 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
54
55 - if(rrd_flag_check(rm, RRD_FLAG_COLLECTED))
55 + if(rrd_flag_is_collected(rm))
56 return 0;
57
58 return rm->last_time_s;
@@ -83,6 +83,9 @@ static void rrdmetric_insert_callback(const DICTIONARY_ITEM *item __maybe_unused
83 // remove flags that we need to figure out at runtime
84 rm->flags = rm->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics
85
86 + // update the count of metrics
87 + __atomic_add_fetch(&rm->ri->rc->rrdhost->rrdctx.metrics_count, 1, __ATOMIC_RELAXED);
88 +
89 // signal the react callback to do the job
90 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_NEW_OBJECT);
91 }
@@ -94,6 +97,9 @@ static void rrdmetric_delete_callback(const DICTIONARY_ITEM *item __maybe_unused
97
98 internal_error(rm->rrddim, "RRDMETRIC: '%s' is freed but there is a RRDDIM linked to it.", string2str(rm->id));
99
100 + // update the count of metrics
101 + __atomic_sub_fetch(&rm->ri->rc->rrdhost->rrdctx.metrics_count, 1, __ATOMIC_RELAXED);
102 +
103 // free the resources
104 rrdmetric_free(rm);
105 }
@@ -181,7 +187,7 @@ static bool rrdmetric_conflict_callback(const DICTIONARY_ITEM *item __maybe_unus
187 rrd_flag_set(rm, rm_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no needs for atomics on rm_new
188
189 if(rrd_flag_is_collected(rm) && rrd_flag_is_archived(rm))
184 - rrd_flag_set_collected(rm);
190 + rrdmetric_set_collected(rm);
191
192 if(rrd_flag_check(rm, RRD_FLAG_UPDATED))
193 rrd_flag_set(rm, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT);
@@ -221,7 +227,7 @@ void rrdmetrics_destroy_from_rrdinstance(RRDINSTANCE *ri) {
227 // trigger post-processing of the rrdmetric, escalating changes to the rrdinstance it belongs
228 static void rrdmetric_trigger_updates(RRDMETRIC *rm, const char *function) {
229 if(unlikely(rrd_flag_is_collected(rm)) && (!rm->rrddim || rrd_flag_check(rm, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD)))
224 - rrd_flag_set_archived(rm);
230 + rrdmetric_set_archived(rm);
231
232 if(rrd_flag_is_updated(rm) || !rrd_flag_check(rm, RRD_FLAG_LIVE_RETENTION)) {
233 rrd_flag_set_updated(rm->ri, RRD_FLAG_UPDATE_REASON_TRIGGERED);
@@ -285,7 +291,7 @@ inline void rrdmetric_rrddim_is_freed(RRDDIM *rd) {
291 if(unlikely(!rm)) return;
292
293 if(unlikely(rrd_flag_is_collected(rm)))
288 - rrd_flag_set_archived(rm);
294 + rrdmetric_set_archived(rm);
295
296 rm->rrddim = NULL;
297 rrdmetric_trigger_updates(rm, __FUNCTION__ );
@@ -302,7 +308,7 @@ inline void rrdmetric_updated_rrddim_flags(RRDDIM *rd) {
308
309 if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED|RRDDIM_FLAG_OBSOLETE))) {
310 if(unlikely(rrd_flag_is_collected(rm)))
305 - rrd_flag_set_archived(rm);
311 + rrdmetric_set_archived(rm);
312 }
313
314 rrdmetric_trigger_updates(rm, __FUNCTION__ );
@@ -318,7 +324,7 @@ inline void rrdmetric_collected_rrddim(RRDDIM *rd) {
324 if(unlikely(!rm)) return;
325
326 if(unlikely(!rrd_flag_is_collected(rm)))
321 - rrd_flag_set_collected(rm);
327 + rrdmetric_set_collected(rm);
328
329 // we use this variable to detect BEGIN/END without SET
330 rm->ri->internal.collected_metrics_count++;
src/database/contexts/worker.c
+14 -28
@@ -288,7 +288,7 @@ bool rrdmetric_update_retention(RRDMETRIC *rm) {
288 }
289
290 if(unlikely(!rm->first_time_s && !rm->last_time_s))
291 - rrd_flag_set_deleted(rm, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
291 + rrdmetric_set_deleted(rm, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
292
293 rrd_flag_set(rm, RRD_FLAG_LIVE_RETENTION);
294
@@ -472,11 +472,11 @@ static void rrdmetric_process_updates(RRDMETRIC *rm, bool force, RRD_FLAGS reaso
472 worker_is_busy(WORKER_JOB_PP_METRIC);
473
474 if(reason & RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD) {
475 - rrd_flag_set_archived(rm);
475 + rrdmetric_set_archived(rm);
476 rrd_flag_set(rm, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD);
477 }
478 if(rrd_flag_is_deleted(rm) && (reason & RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION))
479 - rrd_flag_set_archived(rm);
479 + rrdmetric_set_archived(rm);
480
481 rrdmetric_update_retention(rm);
482
@@ -515,7 +515,7 @@ static void rrdinstance_post_process_updates(RRDINSTANCE *ri, bool force, RRD_FL
515 continue;
516 }
517
518 - if(!currently_collected && rrd_flag_check(rm, RRD_FLAG_COLLECTED) && rm->first_time_s)
518 + if(!currently_collected && rrd_flag_is_collected(rm) && rm->first_time_s)
519 currently_collected = true;
520
521 metrics_active++;
@@ -547,7 +547,7 @@ static void rrdinstance_post_process_updates(RRDINSTANCE *ri, bool force, RRD_FL
547 rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
548 }
549
550 - rrd_flag_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
550 + rrdinstance_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
551 }
552 else {
553 // we have active metrics...
@@ -567,7 +567,7 @@ static void rrdinstance_post_process_updates(RRDINSTANCE *ri, bool force, RRD_FL
567 }
568
569 if(likely(live_retention))
570 - rrd_flag_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
570 + rrdinstance_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
571 }
572 else {
573 rrd_flag_clear(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
@@ -583,9 +583,9 @@ static void rrdinstance_post_process_updates(RRDINSTANCE *ri, bool force, RRD_FL
583 }
584
585 if(likely(currently_collected))
586 - rrd_flag_set_collected(ri);
586 + rrdinstance_set_collected(ri);
587 else
588 - rrd_flag_set_archived(ri);
588 + rrdinstance_set_archived(ri);
589 }
590 }
591
@@ -603,7 +603,7 @@ static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAG
603 size_t min_priority_not_collected = LONG_MAX;
604 size_t min_priority = LONG_MAX;
605 time_t min_first_time_t = LONG_MAX, max_last_time_t = 0;
606 - size_t instances_active = 0, instances_deleted = 0, metrics = 0;
606 + size_t instances_active = 0, instances_deleted = 0;
607 bool live_retention = true, currently_collected = false, hidden = true;
608 if(dictionary_entries(rc->rrdinstances) > 0) {
609 RRDINSTANCE *ri;
@@ -643,10 +643,9 @@ static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAG
643 string2str(rc->units), string2str(ri->units));
644
645 instances_active++;
646 - metrics += dictionary_entries(ri->rrdmetrics);
646
647 if (ri->priority >= RRDCONTEXT_MINIMUM_ALLOWED_PRIORITY) {
649 - if(rrd_flag_check(ri, RRD_FLAG_COLLECTED)) {
648 + if(rrd_flag_is_collected(ri)) {
649 if(ri->priority < min_priority_collected)
650 min_priority_collected = ri->priority;
651 }
@@ -664,8 +663,6 @@ static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAG
663 }
664 dfe_done(ri);
665
667 - rc->stats.metrics = metrics;
668 -
666 if(min_priority_collected != LONG_MAX)
667 // use the collected priority
668 min_priority = min_priority_collected;
@@ -708,7 +705,7 @@ static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAG
705 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
706 }
707
711 - rrd_flag_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
708 + rrdcontext_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
709 }
710 else {
711 // we have some active instances...
@@ -727,7 +724,7 @@ static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAG
724 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
725 }
726
730 - rrd_flag_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
727 + rrdcontext_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
728 }
729 else {
730 rrd_flag_clear(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION);
@@ -743,9 +740,9 @@ static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAG
740 }
741
742 if(likely(currently_collected))
746 - rrd_flag_set_collected(rc);
743 + rrdcontext_set_collected(rc);
744 else
748 - rrd_flag_set_archived(rc);
745 + rrdcontext_set_archived(rc);
746 }
747
748 if (min_priority != LONG_MAX && rc->priority != min_priority) {
@@ -1138,17 +1135,6 @@ void *rrdcontext_main(void *ptr) {
1135
1136 if (host->rrdctx.contexts)
1137 dictionary_garbage_collect(host->rrdctx.contexts);
1141 -
1142 - // calculate the number of metrics and instances in the host
1143 - RRDCONTEXT *rc;
1144 - uint32_t metrics = 0, instances = 0;
1145 - dfe_start_read(host->rrdctx.contexts, rc) {
1146 - metrics += rc->stats.metrics;
1147 - instances += dictionary_entries(rc->rrdinstances);
1148 - }
1149 - dfe_done(rc);
1150 - host->rrdctx.metrics = metrics;
1151 - host->rrdctx.instances = instances;
1138 }
1139 dfe_done(host);
1140
src/database/engine/rrdengineapi.c
+3
@@ -1125,6 +1125,9 @@ void rrdeng_readiness_wait(struct rrdengine_instance *ctx) {
1125 ctx->loading.populate_mrg.array = NULL;
1126 ctx->loading.populate_mrg.size = 0;
1127
1128 + if(__atomic_load_n(&ctx->atomic.first_time_s, __ATOMIC_RELAXED) == LONG_MAX)
1129 + __atomic_store_n(&ctx->atomic.first_time_s, now_realtime_sec(), __ATOMIC_RELAXED);
1130 +
1131 netdata_log_info("DBENGINE: tier %d is ready for data collection and queries", ctx->config.tier);
1132 }
1133
src/database/rrd.h
+9 -11
@@ -323,7 +323,6 @@ struct rrddim {
323
324 int32_t multiplier; // the multiplier of the collected values
325 int32_t divisor; // the divider of the collected values
326 - int32_t dimension_id; // Dimension id
326
327 // ------------------------------------------------------------------------
328 // operational state members
@@ -1175,14 +1174,6 @@ struct rrdhost {
1174 int32_t rrd_update_every; // the update frequency of the host
1175 int32_t rrd_history_entries; // the number of history entries for the host's charts
1176
1178 - struct {
1179 - uint32_t dimension_count; // Dimension count for this host
1180 - uint32_t currently_collected; // Currectly collected metrics cache
1181 - time_t cache_timestamp;
1182 - Pvoid_t JudyL; // Store metrics collected -- link to rrddim
1183 - SPINLOCK spinlock;
1184 - } accounting;
1185 -
1177 RRD_MEMORY_MODE rrd_memory_mode; // the configured memory more for the charts of this host
1178 // the actual per tier is at .db[tier].mode
1179
@@ -1305,12 +1296,19 @@ struct rrdhost {
1296 DICTIONARY *rrdvars; // the host's chart variables index
1297 // this includes custom host variables
1298
1299 + struct {
1300 + uint32_t metrics_count; // atomic
1301 + uint32_t instances_count; // atomic
1302 + uint32_t contexts_count; // atomic
1303 + } collected;
1304 +
1305 struct {
1306 DICTIONARY *contexts;
1307 DICTIONARY *hub_queue;
1308 DICTIONARY *pp_queue;
1312 - uint32_t metrics;
1313 - uint32_t instances;
1309 + uint32_t metrics_count; // atomic
1310 + uint32_t instances_count; // atomic
1311 + uint32_t contexts_count; // atomic
1312 } rrdctx;
1313
1314 struct {
src/database/rrddim.c
-11
@@ -41,13 +41,6 @@ static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
41 RRDSET *st = ctr->st;
42 RRDHOST *host = st->rrdhost;
43
44 - rd->dimension_id = __atomic_add_fetch(&host->accounting.dimension_count, 1, __ATOMIC_RELAXED);
45 - spinlock_lock(&host->accounting.spinlock);
46 - Pvoid_t *Pvalue = JudyLIns(&host->accounting.JudyL, rd->dimension_id, PJE0);
47 - if (Pvalue)
48 - *Pvalue = rd;
49 - spinlock_unlock(&host->accounting.spinlock);
50 -
44 rd->flags = RRDDIM_FLAG_NONE;
45
46 rd->id = string_strdupz(ctr->id);
@@ -238,10 +231,6 @@ static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v
231 freez(rd->db.data);
232 }
233
241 - spinlock_lock(&host->accounting.spinlock);
242 - (void) JudyLDel(&host->accounting.JudyL, rd->dimension_id, PJE0);
243 - spinlock_unlock(&host->accounting.spinlock);
244 -
234 string_freez(rd->id);
235 string_freez(rd->name);
236 }
src/streaming/rrdhost-status.c
+7 -3
@@ -166,9 +166,9 @@ void rrdhost_status(RRDHOST *host, time_t now, RRDHOST_STATUS *s) {
166 bool online = rrdhost_is_online(host);
167
168 rrdhost_retention(host, now, online, &s->db.first_time_s, &s->db.last_time_s);
169 - s->db.metrics = host->rrdctx.metrics;
170 - s->db.instances = host->rrdctx.instances;
171 - s->db.contexts = dictionary_entries(host->rrdctx.contexts);
169 + s->db.metrics = __atomic_load_n(&host->rrdctx.metrics_count, __ATOMIC_RELAXED);
170 + s->db.instances = __atomic_load_n(&host->rrdctx.instances_count, __ATOMIC_RELAXED);
171 + s->db.contexts = __atomic_load_n(&host->rrdctx.contexts_count, __ATOMIC_RELAXED);
172 if(!s->db.first_time_s || !s->db.last_time_s || !s->db.metrics || !s->db.instances || !s->db.contexts ||
173 (flags & (RRDHOST_FLAG_PENDING_CONTEXT_LOAD)))
174 s->db.status = RRDHOST_DB_STATUS_INITIALIZING;
@@ -222,6 +222,10 @@ void rrdhost_status(RRDHOST *host, time_t now, RRDHOST_STATUS *s) {
222 s->ingest.status = RRDHOST_INGEST_STATUS_OFFLINE;
223 }
224
225 + s->ingest.collected.metrics = __atomic_load_n(&host->collected.metrics_count, __ATOMIC_RELAXED);
226 + s->ingest.collected.instances = __atomic_load_n(&host->collected.instances_count, __ATOMIC_RELAXED);
227 + s->ingest.collected.contexts = __atomic_load_n(&host->collected.contexts_count, __ATOMIC_RELAXED);
228 +
229 if(host == localhost)
230 s->ingest.type = RRDHOST_INGEST_TYPE_LOCALHOST;
231 else if(has_receiver)
src/streaming/rrdhost-status.h
+6
@@ -117,6 +117,12 @@ typedef struct {
117 time_t since;
118 STREAM_HANDSHAKE reason;
119
120 + struct {
121 + size_t metrics; // currently collected
122 + size_t instances; // currently collected
123 + size_t contexts; // currently collected
124 + } collected;
125 +
126 struct {
127 bool in_progress;
128 NETDATA_DOUBLE completion;
src/web/api/functions/function-streaming.c
+22
@@ -90,6 +90,10 @@ int function_streaming(BUFFER *wb, const char *function __maybe_unused, BUFFER *
90 buffer_json_add_array_item_string(wb, s.ingest.ssl ? "SSL" : "PLAIN"); // InSSL
91 stream_capabilities_to_json_array(wb, s.ingest.capabilities, NULL); // InCapabilities
92
93 + buffer_json_add_array_item_uint64(wb, s.ingest.collected.metrics); // CollectedMetrics
94 + buffer_json_add_array_item_uint64(wb, s.ingest.collected.instances); // CollectedInstances
95 + buffer_json_add_array_item_uint64(wb, s.ingest.collected.contexts); // CollectedContexts
96 +
97 // streaming
98 if(s.stream.since) {
99 buffer_json_add_array_item_uint64(wb, s.stream.since * MSEC_PER_SEC); // OutSince
@@ -316,6 +320,24 @@ int function_streaming(BUFFER *wb, const char *function __maybe_unused, BUFFER *
320 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
321 RRDF_FIELD_OPTS_NONE, NULL);
322
323 + buffer_rrdf_table_add_field(wb, field_id++, "CollectedMetrics", "Time-series Metrics Currently Collected",
324 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
325 + 0, NULL, (double)max_db_metrics, RRDF_FIELD_SORT_DESCENDING, NULL,
326 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
327 + RRDF_FIELD_OPTS_VISIBLE, NULL);
328 +
329 + buffer_rrdf_table_add_field(wb, field_id++, "CollectedInstances", "Instances Currently Collected",
330 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
331 + 0, NULL, (double)max_db_instances, RRDF_FIELD_SORT_DESCENDING, NULL,
332 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
333 + RRDF_FIELD_OPTS_VISIBLE, NULL);
334 +
335 + buffer_rrdf_table_add_field(wb, field_id++, "CollectedContexts", "Contexts Currently Collected",
336 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
337 + 0, NULL, (double)max_db_contexts, RRDF_FIELD_SORT_DESCENDING, NULL,
338 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
339 + RRDF_FIELD_OPTS_VISIBLE, NULL);
340 +
341 // --- streaming ---
342
343 buffer_rrdf_table_add_field(wb, field_id++, "OutSince", "Last Streaming Status Change",