master
c 691 lines 24.4 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "rrd.h"
4 #include "storage-engine.h"
5 #include "rrddim-collection.h"
6
7 void rrddim_metadata_updated(RRDDIM *rd) {
8 rrdcontext_updated_rrddim(rd);
9 rrdset_metadata_updated(rd->rrdset);
10 }
11
12 // ----------------------------------------------------------------------------
13 // RRDDIM index
14
15 struct rrddim_constructor {
16 RRDSET *st;
17 const char *id;
18 const char *name;
19 collected_number multiplier;
20 collected_number divisor;
21 RRD_ALGORITHM algorithm;
22 RRD_DB_MODE memory_mode;
23
24 enum {
25 RRDDIM_REACT_NONE = 0,
26 RRDDIM_REACT_NEW = (1 << 0),
27 RRDDIM_REACT_UPDATED = (1 << 2),
28 } react_action;
29
30 };
31
32 // isolated call to appear
33 // separate in statistics
34 static void *rrddim_alloc_db(size_t entries) {
35 return callocz(entries, sizeof(storage_number));
36 }
37
38 static void rrddim_reinitialize_collection(RRDDIM *rd) {
39 RRDSET *st = rd->rrdset;
40
41 for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
42 if (!rd->tiers[tier].sch)
43 rd->tiers[tier].sch =
44 storage_metric_store_init(rd->tiers[tier].seb, rd->tiers[tier].smh, st->rrdhost->db[tier].tier_grouping * st->update_every, rd->rrdset->smg[tier]);
45 }
46 }
47
48 static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *constructor_data) {
49 struct rrddim_constructor *ctr = constructor_data;
50 RRDDIM *rd = rrddim;
51 RRDSET *st = ctr->st;
52 RRDHOST *host = st->rrdhost;
53
54 spinlock_init(&rd->destroy_lock);
55
56 rd->flags = RRDDIM_FLAG_NONE;
57
58 rd->id = string_strdupz(ctr->id);
59 rd->name = (ctr->name && *ctr->name)?rrd_string_strdupz(ctr->name):string_dup(rd->id);
60
61 rd->algorithm = ctr->algorithm;
62 rd->multiplier = ctr->multiplier;
63 rd->divisor = ctr->divisor;
64 if(!rd->divisor) rd->divisor = 1;
65
66 rd->rrdset = st;
67
68 rd->stream.snd.dim_slot = __atomic_add_fetch(&st->stream.snd.dim_last_slot_used, 1, __ATOMIC_RELAXED);
69
70 if(rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST))
71 rd->collector.counter = 1;
72
73 if(ctr->memory_mode == RRD_DB_MODE_RAM) {
74 size_t entries = st->db.entries;
75 if(!entries) entries = 5;
76
77 rd->db.data = nd_mmap_advanced(NULL, entries * sizeof(storage_number), MAP_PRIVATE, 1, false, true, NULL);
78 if(rd->db.data) {
79 rd->db.memsize = entries * sizeof(storage_number);
80 pulse_db_rrd_memory_add(rd->db.memsize);
81 }
82 else {
83 netdata_log_info("Failed to use memory mode ram for chart '%s', dimension '%s', falling back to alloc", rrdset_name(st), rrddim_name(rd));
84 ctr->memory_mode = RRD_DB_MODE_ALLOC;
85 }
86 }
87
88 if(ctr->memory_mode == RRD_DB_MODE_ALLOC || ctr->memory_mode == RRD_DB_MODE_NONE) {
89 size_t entries = st->db.entries;
90 if(entries < 5) entries = 5;
91
92 rd->db.data = rrddim_alloc_db(entries);
93 rd->db.memsize = entries * sizeof(storage_number);
94 pulse_db_rrd_memory_add(rd->db.memsize);
95 }
96
97 rd->rrd_memory_mode = ctr->memory_mode;
98
99 nd_uuid_t uuid;
100 if (unlikely(rrdcontext_find_dimension_uuid(st, rrddim_id(rd), &uuid)))
101 uuid_generate(uuid);
102 rd->uuid = uuidmap_create(uuid);
103
104 // initialize the db tiers
105 {
106 size_t initialized = 0;
107 for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
108 STORAGE_ENGINE *eng = host->db[tier].eng;
109 rd->tiers[tier].seb = eng->seb;
110 rd->tiers[tier].tier_grouping = host->db[tier].tier_grouping;
111 rd->tiers[tier].smh = eng->api.metric_get_or_create(rd, host->db[tier].si);
112 spinlock_init(&rd->tiers[tier].spinlock);
113 storage_point_unset(rd->tiers[tier].virtual_point);
114 initialized++;
115
116 // internal_error(true, "TIER GROUPING of chart '%s', dimension '%s' for tier %d is set to %d", rd->rrdset->name, rd->name, tier, rd->tiers[tier]->tier_grouping);
117 }
118
119 if(!initialized)
120 netdata_log_error("Failed to initialize all db tiers for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd));
121
122 if(!rd->tiers[0].smh)
123 netdata_log_error("Failed to initialize the first db tier for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd));
124 }
125
126 // initialize data collection for all tiers
127 {
128 size_t initialized = 0;
129 for (size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
130 if (rd->tiers[tier].smh) {
131 uint32_t tier_update_every = st->rrdhost->db[tier].tier_grouping * st->update_every;
132
133 rd->tiers[tier].last_completed_point_flush_modulo = rrddim_collection_modulo(st, tier_update_every);
134
135 rd->tiers[tier].sch =
136 storage_metric_store_init(rd->tiers[tier].seb, rd->tiers[tier].smh, tier_update_every, rd->rrdset->smg[tier]);
137
138 initialized++;
139 }
140 }
141
142 if(!initialized)
143 netdata_log_error("Failed to initialize data collection for all db tiers for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd));
144 }
145
146 // NOTE: the heterogeneity check has moved to rrddim_react_callback().
147 // Running it here calls dfe_start_*() on st->rrddim_root_index while the
148 // index write-lock of that dict is still held by the inserter — AB-BA
149 // against svc_rrdset_archive_obsolete_dimensions() which holds items-write
150 // and then calls dictionary_del() that wants index-write.
151
152 // let the chart resync
153 rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK);
154
155 ml_dimension_new(rd);
156
157 ctr->react_action = RRDDIM_REACT_NEW;
158
159 internal_error(false, "RRDDIM: inserted dimension '%s' of chart '%s' of host '%s'",
160 rrddim_name(rd), rrdset_name(st), rrdhost_hostname(st->rrdhost));
161
162 }
163
164 bool rrddim_finalize_collection_and_check_retention(RRDDIM *rd) {
165 ND_LOG_STACK lgs[] = {
166 ND_LOG_FIELD_TXT(NDF_NIDL_NODE, rrdhost_hostname(rd->rrdset->rrdhost)),
167 ND_LOG_FIELD_TXT(NDF_NIDL_CONTEXT, rrdset_context(rd->rrdset)),
168 ND_LOG_FIELD_TXT(NDF_NIDL_INSTANCE, rrdset_name(rd->rrdset)),
169 ND_LOG_FIELD_TXT(NDF_NIDL_DIMENSION, rrddim_name(rd)),
170 ND_LOG_FIELD_END(),
171 };
172 ND_LOG_STACK_PUSH(lgs);
173
174 size_t tiers_available = 0, tiers_said_no_retention = 0;
175
176 for(size_t tier = 0; tier < nd_profile.storage_tiers ;tier++) {
177 spinlock_lock(&rd->tiers[tier].spinlock);
178
179 if(rd->tiers[tier].sch) {
180 tiers_available++;
181
182 if(tier > 0)
183 store_metric_at_tier_flush_last_completed(rd, tier, &rd->tiers[tier]);
184
185 if (storage_engine_store_finalize(rd->tiers[tier].sch))
186 tiers_said_no_retention++;
187
188 rd->tiers[tier].sch = NULL;
189 }
190
191 spinlock_unlock(&rd->tiers[tier].spinlock);
192 }
193
194 // return true if the dimension has retention in the db
195 return (!tiers_said_no_retention || tiers_available > tiers_said_no_retention);
196 }
197
198 static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *rrdset) {
199 RRDDIM *rd = rrddim;
200 RRDSET *st = rrdset;
201 RRDHOST *host = st->rrdhost;
202
203 internal_error(false, "RRDDIM: deleting dimension '%s' of chart '%s' of host '%s'",
204 rrddim_name(rd), rrdset_name(st), rrdhost_hostname(host));
205
206 rrdcontext_removed_rrddim(rd);
207
208 ml_dimension_delete(rd);
209
210 netdata_log_debug(D_RRD_CALLS, "rrddim_free() %s.%s", rrdset_name(st), rrddim_name(rd));
211
212 if (!rrddim_finalize_collection_and_check_retention(rd) && rd->rrd_memory_mode == RRD_DB_MODE_DBENGINE) {
213 /* This metric has no data and no references */
214 metaqueue_delete_dimension_uuid(uuidmap_uuid_ptr(rd->uuid));
215 }
216
217 for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
218 spinlock_lock(&rd->tiers[tier].spinlock);
219 if(rd->tiers[tier].smh) {
220 STORAGE_ENGINE *eng = host->db[tier].eng;
221 eng->api.metric_release(rd->tiers[tier].smh);
222 rd->tiers[tier].smh = NULL;
223 }
224 spinlock_unlock(&rd->tiers[tier].spinlock);
225 }
226
227 if(rd->db.data) {
228 pulse_db_rrd_memory_sub(rd->db.memsize);
229
230 if(rd->rrd_memory_mode == RRD_DB_MODE_RAM)
231 nd_munmap(rd->db.data, rd->db.memsize);
232 else
233 freez(rd->db.data);
234 }
235
236 string_freez(rd->id);
237 string_freez(rd->name);
238 uuidmap_free(rd->uuid);
239
240 if(rd->destroy_lock.locked)
241 spinlock_unlock(&rd->destroy_lock);
242
243 memset(rd, 0, sizeof(RRDDIM));
244 }
245
246 static bool rrddim_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *new_rrddim, void *constructor_data) {
247 (void)new_rrddim; // it is NULL
248
249 struct rrddim_constructor *ctr = constructor_data;
250 RRDDIM *rd = rrddim;
251 RRDSET *st = ctr->st;
252
253 ctr->react_action = RRDDIM_REACT_NONE;
254
255 int rc = rrddim_reset_name(st, rd, ctr->name);
256 rc += rrddim_set_algorithm(st, rd, ctr->algorithm);
257 rc += rrddim_set_multiplier(st, rd, ctr->multiplier);
258 rc += rrddim_set_divisor(st, rd, ctr->divisor);
259
260 rrddim_reinitialize_collection(rd);
261
262 if(unlikely(rc))
263 ctr->react_action = RRDDIM_REACT_UPDATED;
264
265 return ctr->react_action == RRDDIM_REACT_UPDATED;
266 }
267
268 static void rrddim_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *constructor_data) {
269 struct rrddim_constructor *ctr = constructor_data;
270 RRDDIM *rd = rrddim;
271 RRDSET *st = ctr->st;
272
273 if(ctr->react_action & (RRDDIM_REACT_UPDATED | RRDDIM_REACT_NEW)) {
274 rrddim_flag_set(rd, RRDDIM_FLAG_METADATA_UPDATE);
275 rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
276 }
277
278 if(ctr->react_action == RRDDIM_REACT_UPDATED) {
279 // the chart needs to be updated to the parent
280 rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK);
281 }
282
283 if(ctr->react_action & RRDDIM_REACT_NEW) {
284 // heterogeneity check (in react not insert: insert still holds index-write).
285 // compare and flag-set inside the loop body so td stays referenced via the
286 // dfe iterator (no UAF after dfe_done). rd is in the items list by react
287 // time, so skip td == rd.
288 RRDDIM *td;
289 dfe_start_read(st->rrddim_root_index, td) {
290 if(td == rd)
291 continue;
292
293 if(td->algorithm != rd->algorithm
294 || ABS(td->multiplier) != ABS(rd->multiplier)
295 || ABS(td->divisor) != ABS(rd->divisor)) {
296 if(!rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) {
297 #ifdef NETDATA_INTERNAL_CHECKS
298 netdata_log_info("Dimension '%s' added on chart '%s' of host '%s' is not homogeneous to other dimensions already "
299 "present (algorithm is '%s' vs '%s', multiplier is %d vs %d, "
300 "divisor is %d vs %d).",
301 rrddim_name(rd),
302 rrdset_name(st),
303 rrdhost_hostname(st->rrdhost),
304 rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(td->algorithm),
305 rd->multiplier, td->multiplier,
306 rd->divisor, td->divisor
307 );
308 #endif
309 rrdset_flag_set(st, RRDSET_FLAG_HETEROGENEOUS);
310 }
311 }
312 break;
313 }
314 dfe_done(td);
315 }
316
317 rrddim_metadata_updated(rd);
318 }
319
320 size_t rrddim_size(void) {
321 return sizeof(RRDDIM) + nd_profile.storage_tiers * sizeof(struct rrddim_tier);
322 }
323
324 void rrddim_index_init(RRDSET *st) {
325 if(!st->rrddim_root_index) {
326 st->rrddim_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
327 &dictionary_stats_category_rrddim, rrddim_size());
328
329 dictionary_register_insert_callback(st->rrddim_root_index, rrddim_insert_callback, NULL);
330 dictionary_register_conflict_callback(st->rrddim_root_index, rrddim_conflict_callback, NULL);
331 dictionary_register_delete_callback(st->rrddim_root_index, rrddim_delete_callback, st);
332 dictionary_register_react_callback(st->rrddim_root_index, rrddim_react_callback, st);
333 }
334 }
335
336 void rrddim_index_destroy(RRDSET *st) {
337 dictionary_destroy(st->rrddim_root_index);
338 st->rrddim_root_index = NULL;
339 }
340
341 static inline RRDDIM *rrddim_index_find(RRDSET *st, const char *id) {
342 return dictionary_get(st->rrddim_root_index, id);
343 }
344
345 // ----------------------------------------------------------------------------
346 // RRDDIM - find a dimension
347
348 inline RRDDIM *rrddim_find(RRDSET *st, const char *id, bool include_obsolete) {
349 netdata_log_debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", rrdset_name(st), id);
350
351 RRDDIM *rd = rrddim_index_find(st, id);
352 if(rd) {
353 if(!include_obsolete && rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE) && !rrdset_is_discoverable(st))
354 return NULL;
355
356 rd->rrdset->last_accessed_time_s = now_realtime_sec();
357 }
358
359 return rd;
360 }
361
362 inline RRDDIM_ACQUIRED *rrddim_find_and_acquire(RRDSET *st, const char *id, bool include_obsolete) {
363 netdata_log_debug(D_RRD_CALLS, "rrddim_find_and_acquire() for chart %s, dimension %s", rrdset_name(st), id);
364
365 RRDDIM_ACQUIRED *rda = (RRDDIM_ACQUIRED *)dictionary_get_and_acquire_item(st->rrddim_root_index, id);
366 if(rda) {
367 RRDDIM *rd = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rda);
368 if(!include_obsolete && rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE) && !rrdset_is_discoverable(st)) {
369 dictionary_acquired_item_release(st->rrddim_root_index, (const DICTIONARY_ITEM *)rda);
370 return NULL;
371 }
372
373 rd->rrdset->last_accessed_time_s = now_realtime_sec();
374 }
375
376 return rda;
377 }
378
379 RRDDIM *rrddim_acquired_to_rrddim(RRDDIM_ACQUIRED *rda) {
380 if(unlikely(!rda))
381 return NULL;
382
383 return (RRDDIM *) dictionary_acquired_item_value((const DICTIONARY_ITEM *)rda);
384 }
385
386 void rrddim_acquired_release(RRDDIM_ACQUIRED *rda) {
387 if(unlikely(!rda))
388 return;
389
390 RRDDIM *rd = rrddim_acquired_to_rrddim(rda);
391 dictionary_acquired_item_release(rd->rrdset->rrddim_root_index, (const DICTIONARY_ITEM *)rda);
392 }
393
394 // ----------------------------------------------------------------------------
395 // RRDDIM rename a dimension
396
397 inline int rrddim_reset_name(RRDSET *st __maybe_unused, RRDDIM *rd, const char *name) {
398 if(unlikely(!name || !*name || !strcmp(rrddim_name(rd), name)))
399 return 0;
400
401 netdata_log_debug(D_RRD_CALLS, "rrddim_reset_name() from %s.%s to %s.%s", rrdset_name(st), rrddim_name(rd), rrdset_name(st), name);
402
403 STRING *old = rd->name;
404 rd->name = rrd_string_strdupz(name);
405 string_freez(old);
406
407 rrddim_metadata_updated(rd);
408
409 return 1;
410 }
411
412 inline int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm) {
413 if(unlikely(rd->algorithm == algorithm))
414 return 0;
415
416 netdata_log_debug(D_RRD_CALLS, "Updating algorithm of dimension '%s/%s' from %s to %s", rrdset_id(st), rrddim_name(rd), rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(algorithm));
417 rd->algorithm = algorithm;
418 rrddim_metadata_updated(rd);
419 rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK);
420 rrdcontext_updated_rrddim_algorithm(rd);
421 return 1;
422 }
423
424 inline int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, int32_t multiplier) {
425 if(unlikely(rd->multiplier == multiplier))
426 return 0;
427
428 netdata_log_debug(D_RRD_CALLS, "Updating multiplier of dimension '%s/%s' from %d to %d",
429 rrdset_id(st), rrddim_name(rd), rd->multiplier, multiplier);
430 rd->multiplier = multiplier;
431 rrddim_metadata_updated(rd);
432 rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK);
433 rrdcontext_updated_rrddim_multiplier(rd);
434 return 1;
435 }
436
437 inline int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, int32_t divisor) {
438 if(unlikely(rd->divisor == divisor))
439 return 0;
440
441 netdata_log_debug(D_RRD_CALLS, "Updating divisor of dimension '%s/%s' from %d to %d",
442 rrdset_id(st), rrddim_name(rd), rd->divisor, divisor);
443 rd->divisor = divisor;
444 rrddim_metadata_updated(rd);
445 rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK);
446 rrdcontext_updated_rrddim_divisor(rd);
447 return 1;
448 }
449
450 // ----------------------------------------------------------------------------
451
452 time_t rrddim_last_entry_s_of_tier(RRDDIM *rd, size_t tier) {
453 if(unlikely(!rd))
454 return 0;
455
456 if(unlikely(tier >= nd_profile.storage_tiers || !rd->tiers[tier].smh))
457 return 0;
458
459 return storage_engine_latest_time_s(rd->tiers[tier].seb, rd->tiers[tier].smh);
460 }
461
462 // get the timestamp of the last entry in the round-robin database
463 time_t rrddim_last_entry_s(RRDDIM *rd) {
464 if(unlikely(!rd))
465 return 0;
466
467 time_t latest_time_s = rrddim_last_entry_s_of_tier(rd, 0);
468
469 for(size_t tier = 1; tier < nd_profile.storage_tiers;tier++) {
470 if(unlikely(!rd->tiers[tier].smh)) continue;
471
472 time_t t = rrddim_last_entry_s_of_tier(rd, tier);
473 if(t > latest_time_s)
474 latest_time_s = t;
475 }
476
477 return latest_time_s;
478 }
479
480 time_t rrddim_first_entry_s_of_tier(RRDDIM *rd, size_t tier) {
481 if(unlikely(!rd))
482 return 0;
483
484 if(unlikely(tier >= nd_profile.storage_tiers || !rd->tiers[tier].smh))
485 return 0;
486
487 return storage_engine_oldest_time_s(rd->tiers[tier].seb, rd->tiers[tier].smh);
488 }
489
490 time_t rrddim_first_entry_s(RRDDIM *rd) {
491 if(unlikely(!rd))
492 return 0;
493
494 time_t oldest_time_s = 0;
495
496 for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
497 time_t t = rrddim_first_entry_s_of_tier(rd, tier);
498 if(t != 0 && (oldest_time_s == 0 || t < oldest_time_s))
499 oldest_time_s = t;
500 }
501
502 return oldest_time_s;
503 }
504
505 RRDDIM *rrddim_add_custom(RRDSET *st
506 , const char *id
507 , const char *name
508 , collected_number multiplier
509 , collected_number divisor
510 , RRD_ALGORITHM algorithm
511 ,
512 RRD_DB_MODE memory_mode
513 ) {
514
515 RRDDIM *rd = NULL;
516 while(!rd) {
517 rd = rrddim_index_find(st, id);
518 if(rd) {
519 if(spinlock_trylock(&rd->destroy_lock)) {
520 rrddim_isnot_obsolete___safe_from_collector_thread(st, rd);
521 spinlock_unlock(&rd->destroy_lock);
522 }
523 else {
524 rd = NULL;
525 microsleep(1 * USEC_PER_MS);
526 continue;
527 }
528 }
529
530 struct rrddim_constructor tmp = {
531 .st = st,
532 .id = id,
533 .name = name,
534 .multiplier = multiplier,
535 .divisor = divisor,
536 .algorithm = algorithm,
537 .memory_mode = memory_mode,
538 };
539
540 rd = dictionary_set_advanced(st->rrddim_root_index, tmp.id, -1, NULL, rrddim_size(), &tmp);
541 }
542
543 return(rd);
544 }
545
546 // ----------------------------------------------------------------------------
547 // RRDDIM remove / free a dimension
548
549 void rrddim_free(RRDSET *st, RRDDIM *rd) {
550 dictionary_del(st->rrddim_root_index, string2str(rd->id));
551 }
552
553
554 // ----------------------------------------------------------------------------
555 // RRDDIM - set dimension options
556
557 int rrddim_hide(RRDSET *st, const char *id) {
558 netdata_log_debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", rrdset_name(st), id);
559
560 RRDHOST *host = st->rrdhost;
561
562 RRDDIM *rd = rrddim_find(st, id, true);
563 if(unlikely(!rd)) {
564 netdata_log_error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host));
565 return 1;
566 }
567 if (!rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) {
568 rrddim_flag_set(rd, RRDDIM_FLAG_META_HIDDEN | RRDDIM_FLAG_METADATA_UPDATE);
569 rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
570 }
571
572 rrddim_option_set(rd, RRDDIM_OPTION_HIDDEN);
573 rrdcontext_updated_rrddim_flags(rd);
574 rrdset_metadata_updated(st);
575 return 0;
576 }
577
578 int rrddim_unhide(RRDSET *st, const char *id) {
579 netdata_log_debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", rrdset_name(st), id);
580
581 RRDHOST *host = st->rrdhost;
582 RRDDIM *rd = rrddim_find(st, id, true);
583 if(unlikely(!rd)) {
584 netdata_log_error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host));
585 return 1;
586 }
587 if (rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) {
588 rrddim_flag_clear(rd, RRDDIM_FLAG_META_HIDDEN);
589 rrddim_flag_set(rd, RRDDIM_FLAG_METADATA_UPDATE);
590 rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
591 }
592
593 rrddim_option_clear(rd, RRDDIM_OPTION_HIDDEN);
594 rrdcontext_updated_rrddim_flags(rd);
595 rrdset_metadata_updated(st);
596 return 0;
597 }
598
599 inline void rrddim_is_obsolete___safe_from_collector_thread(RRDSET *st, RRDDIM *rd) {
600 netdata_log_debug(D_RRD_CALLS, "rrddim_is_obsolete___safe_from_collector_thread() for chart %s, dimension %s", rrdset_name(st), rrddim_name(rd));
601
602 rrddim_flag_set(rd, RRDDIM_FLAG_OBSOLETE);
603 rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS);
604 rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_PENDING_OBSOLETE_DIMENSIONS);
605 rrdcontext_updated_rrddim_flags(rd);
606 rrdset_metadata_updated(st);
607 }
608
609 inline void rrddim_isnot_obsolete___safe_from_collector_thread(RRDSET *st __maybe_unused, RRDDIM *rd) {
610 netdata_log_debug(D_RRD_CALLS, "rrddim_isnot_obsolete___safe_from_collector_thread() for chart %s, dimension %s", rrdset_name(st), rrddim_name(rd));
611
612 rrddim_flag_clear(rd, RRDDIM_FLAG_OBSOLETE);
613 rrddim_reinitialize_collection(rd);
614 rrdcontext_updated_rrddim_flags(rd);
615 rrdset_metadata_updated(st);
616 }
617
618 // ----------------------------------------------------------------------------
619 // RRDDIM - collect values for a dimension
620
621 inline collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value) {
622 struct timeval now;
623 now_realtime_timeval(&now);
624
625 return rrddim_timed_set_by_pointer(st, rd, now, value);
626 }
627
628 // Sets the collected value for this dimension.
629 // Returns the *previous* cycle's collected value (last_collected_value), not the current one.
630 NETDATA_DOUBLE rrddim_set_by_pointer_double(RRDSET *st, RRDDIM *rd, NETDATA_DOUBLE value) {
631 struct timeval now;
632 now_realtime_timeval(&now);
633
634 // For float dims, store exact; for int dims, keep int semantics
635 if(rrddim_is_float(rd)) {
636 rd->collector.last_collected_time = now;
637 rrddim_set_collected_float(rd, value);
638 rrddim_set_updated(rd);
639 rd->collector.counter++;
640
641 NETDATA_DOUBLE v = value >= 0 ? value : -value;
642 if(unlikely(v > rrddim_collected_max_as_double(rd)))
643 rrddim_set_collected_max_float(rd, v);
644
645 return rrddim_last_collected_as_double(rd);
646 }
647
648 return (NETDATA_DOUBLE)rrddim_timed_set_by_pointer(st, rd, now, (collected_number)value);
649 }
650
651 collected_number rrddim_timed_set_by_pointer(RRDSET *st __maybe_unused, RRDDIM *rd, struct timeval collected_time, collected_number value) {
652 netdata_log_debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, rrdset_name(st), rrddim_name(rd), value);
653
654 rd->collector.last_collected_time = collected_time;
655 if(rrddim_is_float(rd))
656 rrddim_set_collected_float(rd, (NETDATA_DOUBLE)value);
657 else
658 rrddim_set_collected_int(rd, value);
659 rrddim_set_updated(rd);
660 rd->collector.counter++;
661
662 // spinlock_lock(&st->rrdhost->accounting.spinlock);
663 // Pvoid_t *Pvalue = JudyLIns(&st->rrdhost->accounting.JudySecL, (Word_t) collected_time.tv_sec, PJE0);
664 // if (Pvalue)
665 // *((int64_t *)Pvalue) = *((int64_t *)Pvalue) + 1;
666 // spinlock_unlock(&st->rrdhost->accounting.spinlock);
667
668 NETDATA_DOUBLE v = value >= 0 ? (NETDATA_DOUBLE)value : (NETDATA_DOUBLE)(-value);
669 if (unlikely(v > rrddim_collected_max_as_double(rd))) {
670 if(rrddim_is_float(rd))
671 rrddim_set_collected_max_float(rd, v);
672 else
673 rrddim_set_collected_max_int(rd, (int64_t)v);
674 }
675 // For int dims return the last collected int; for float dims the integer return is meaningless, so return 0 to avoid truncation misuse.
676 if(rrddim_is_float(rd))
677 return 0;
678 return rd->collector.collected.i.last_collected_value;
679 }
680
681
682 collected_number rrddim_set(RRDSET *st, const char *id, collected_number value) {
683 RRDHOST *host = st->rrdhost;
684 RRDDIM *rd = rrddim_find_active(st, id);
685 if(unlikely(!rd)) {
686 netdata_log_error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host));
687 return 0;
688 }
689
690 return rrddim_set_by_pointer(st, rd, value);
691 }