master
c 353 lines 12.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "rrdcontext-internal.h"
4
5 void rrdmetric_trigger_updates(RRDMETRIC *rm, const char *function);
6
7 inline const char *rrdmetric_acquired_id(RRDMETRIC_ACQUIRED *rma) {
8 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
9 return string2str(rm->id);
10 }
11
12 inline const char *rrdmetric_acquired_name(RRDMETRIC_ACQUIRED *rma) {
13 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
14 return string2str(rm->name);
15 }
16
17 inline bool rrdmetric_acquired_has_name(RRDMETRIC_ACQUIRED *rma) {
18 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
19 return (rm->name && rm->name != rm->id);
20 }
21
22 inline STRING *rrdmetric_acquired_id_dup(RRDMETRIC_ACQUIRED *rma) {
23 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
24 return string_dup(rm->id);
25 }
26
27 inline STRING *rrdmetric_acquired_name_dup(RRDMETRIC_ACQUIRED *rma) {
28 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
29 return string_dup(rm->name);
30 }
31
32 inline NETDATA_DOUBLE rrdmetric_acquired_last_stored_value(RRDMETRIC_ACQUIRED *rma) {
33 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
34 RRDDIM *rd = rrdmetric_rrddim_get_and_lock(rm);
35 if(rd) {
36 NETDATA_DOUBLE last_stored_value = rd->collector.last_stored_value;
37 rrdmetric_rrddim_unlock(rd);
38 return last_stored_value;
39 }
40
41 return NAN;
42 }
43
44 inline bool rrdmetric_acquired_belongs_to_instance(RRDMETRIC_ACQUIRED *rma, RRDINSTANCE_ACQUIRED *ria) {
45 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
46 RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
47 return rm->ri == ri;
48 }
49
50 inline time_t rrdmetric_acquired_first_entry(RRDMETRIC_ACQUIRED *rma) {
51 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
52 return rm->first_time_s;
53 }
54
55 inline time_t rrdmetric_acquired_last_entry(RRDMETRIC_ACQUIRED *rma) {
56 RRDMETRIC *rm = rrdmetric_acquired_value(rma);
57
58 if(rrd_flag_is_collected(rm))
59 return 0;
60
61 return rm->last_time_s;
62 }
63
64 // ----------------------------------------------------------------------------
65 // RRDMETRIC
66
67 // free the contents of RRDMETRIC.
68 // RRDMETRIC itself is managed by DICTIONARY - no need to free it here.
69 static void rrdmetric_free(RRDMETRIC *rm) {
70 string_freez(rm->id);
71 string_freez(rm->name);
72 uuidmap_free(rm->uuid);
73
74 rm->id = NULL;
75 rm->name = NULL;
76 rm->ri = NULL;
77 rm->uuid = 0;
78 }
79
80 // called when this rrdmetric is inserted to the rrdmetrics dictionary of a rrdinstance
81 // the constructor of the rrdmetric object
82 static void rrdmetric_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance) {
83 RRDMETRIC *rm = value;
84
85 // link it to its parent
86 rm->ri = rrdinstance;
87
88 // remove flags that we need to figure out at runtime
89 rm->flags = rm->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics
90
91 // update the count of metrics
92 __atomic_add_fetch(&rm->ri->rc->rrdhost->rrdctx.metrics_count, 1, __ATOMIC_RELAXED);
93
94 // signal the react callback to do the job
95 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_NEW_OBJECT);
96 }
97
98 // called when this rrdmetric is deleted from the rrdmetrics dictionary of a rrdinstance
99 // the destructor of the rrdmetric object
100 static void rrdmetric_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance __maybe_unused) {
101 RRDMETRIC *rm = value;
102
103 internal_error(rrdmetric_rrddim_atomic_load(rm), "RRDMETRIC: '%s' is freed but there is a RRDDIM linked to it.", string2str(rm->id));
104
105 // update the count of metrics
106 __atomic_sub_fetch(&rm->ri->rc->rrdhost->rrdctx.metrics_count, 1, __ATOMIC_RELAXED);
107
108 // free the resources
109 rrdmetric_free(rm);
110 }
111
112 // called when the same rrdmetric is inserted again to the rrdmetrics dictionary of a rrdinstance
113 // while this is called, the dictionary is write locked, but there may be other users of the object
114 static bool rrdmetric_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *rrdinstance __maybe_unused) {
115 RRDMETRIC *rm = old_value;
116 RRDMETRIC *rm_new = new_value;
117 rm_new->ri = rm->ri;
118
119 internal_error(rm->id != rm_new->id,
120 "RRDMETRIC: '%s' cannot change id to '%s'",
121 string2str(rm->id), string2str(rm_new->id));
122
123 if(rm->uuid != rm_new->uuid) {
124 #ifdef NETDATA_INTERNAL_CHECKS
125 char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
126 uuid_unparse(*uuidmap_uuid_ptr(rm->uuid), uuid1);
127 uuid_unparse(*uuidmap_uuid_ptr(rm_new->uuid), uuid2);
128
129 time_t old_first_time_s = 0;
130 time_t old_last_time_s = 0;
131 if(rrdmetric_update_retention(rm)) {
132 old_first_time_s = rm->first_time_s;
133 old_last_time_s = rm->last_time_s;
134 }
135
136 time_t new_first_time_s = 0;
137 time_t new_last_time_s = 0;
138 if(rrdmetric_update_retention(rm_new)) {
139 new_first_time_s = rm_new->first_time_s;
140 new_last_time_s = rm_new->last_time_s;
141 }
142
143 internal_error(true,
144 "RRDMETRIC: '%s' of instance '%s' of host '%s' changed UUID from '%s' (retention %ld to %ld, %ld secs) to '%s' (retention %ld to %ld, %ld secs)"
145 , string2str(rm->id)
146 , string2str(rm->ri->id)
147 , rrdhost_hostname(rm->ri->rc->rrdhost)
148 , uuid1, old_first_time_s, old_last_time_s, old_last_time_s - old_first_time_s
149 , uuid2, new_first_time_s, new_last_time_s, new_last_time_s - new_first_time_s
150 );
151 #endif
152
153 SWAP(rm->uuid, rm_new->uuid);
154 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
155 }
156
157 RRDDIM *rd = rrdmetric_rrddim_atomic_load(rm);
158 RRDDIM *rd_new = rrdmetric_rrddim_atomic_load(rm_new);
159
160 if(rd && rd_new && rd != rd_new) {
161 rrdmetric_rrddim_atomic_store(rm, rd_new);
162 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING);
163 }
164
165 if(rd != rd_new)
166 rrdmetric_rrddim_atomic_store(rm, rd_new);
167
168 if(rm->name != rm_new->name) {
169 SWAP(rm->name, rm_new->name);
170 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
171 }
172
173 if(rrdmetric_algorithm_atomic_load(rm) != rm_new->algorithm) {
174 rrdmetric_algorithm_atomic_store(rm, rm_new->algorithm);
175 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
176 }
177
178 if(!rm->first_time_s || (rm_new->first_time_s && rm_new->first_time_s < rm->first_time_s)) {
179 rm->first_time_s = rm_new->first_time_s;
180 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
181 }
182
183 if(!rm->last_time_s || (rm_new->last_time_s && rm_new->last_time_s > rm->last_time_s)) {
184 rm->last_time_s = rm_new->last_time_s;
185 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
186 }
187
188 rrd_flag_set(rm, rm_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no needs for atomics on rm_new
189
190 if(rrd_flag_is_collected(rm) && rrd_flag_is_archived(rm))
191 rrdmetric_set_collected(rm);
192
193 if(rrd_flag_check(rm, RRD_FLAG_UPDATED))
194 rrd_flag_set(rm, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT);
195
196 rrdmetric_free(rm_new);
197
198 // the react callback will continue from here
199 return rrd_flag_is_updated(rm);
200 }
201
202 // this is called after the insert or the conflict callbacks,
203 // but the dictionary is now unlocked
204 static void rrdmetric_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance __maybe_unused) {
205 RRDMETRIC *rm = value;
206 rrdmetric_trigger_updates(rm, __FUNCTION__ );
207 }
208
209 void rrdmetrics_create_in_rrdinstance(RRDINSTANCE *ri) {
210 if(unlikely(!ri)) return;
211 if(likely(ri->rrdmetrics)) return;
212
213 ri->rrdmetrics = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
214 &dictionary_stats_category_rrdcontext, sizeof(RRDMETRIC));
215
216 dictionary_register_insert_callback(ri->rrdmetrics, rrdmetric_insert_callback, ri);
217 dictionary_register_delete_callback(ri->rrdmetrics, rrdmetric_delete_callback, ri);
218 dictionary_register_conflict_callback(ri->rrdmetrics, rrdmetric_conflict_callback, ri);
219 dictionary_register_react_callback(ri->rrdmetrics, rrdmetric_react_callback, ri);
220 }
221
222 void rrdmetrics_destroy_from_rrdinstance(RRDINSTANCE *ri) {
223 if(unlikely(!ri || !ri->rrdmetrics)) return;
224 dictionary_destroy(ri->rrdmetrics);
225 ri->rrdmetrics = NULL;
226 }
227
228 // trigger post-processing of the rrdmetric, escalating changes to the rrdinstance it belongs
229 void rrdmetric_trigger_updates(RRDMETRIC *rm, const char *function) {
230 if(unlikely(rrd_flag_is_collected(rm)) && (!rrdmetric_rrddim_atomic_load(rm) || rrd_flag_check(rm, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD)))
231 rrdmetric_set_archived(rm);
232
233 if(rrd_flag_is_updated(rm) || !rrd_flag_check(rm, RRD_FLAG_LIVE_RETENTION)) {
234 rrd_flag_set_updated(rm->ri, RRD_FLAG_UPDATE_REASON_TRIGGERED);
235 rrdcontext_queue_for_post_processing(rm->ri->rc, function, rm->flags);
236 }
237 }
238
239 // ----------------------------------------------------------------------------
240 // RRDMETRIC HOOKS ON RRDDIM
241
242 ALWAYS_INLINE void rrdmetric_not_collected_rrddim(RRDDIM *rd) {
243 rd->rrdcontexts.collected = false;
244 }
245
246 void rrdmetric_from_rrddim(RRDDIM *rd) {
247 if(unlikely(!rd->rrdset))
248 fatal("RRDMETRIC: rrddim '%s' does not have a rrdset.", rrddim_id(rd));
249
250 if(unlikely(!rd->rrdset->rrdhost))
251 fatal("RRDMETRIC: rrdset '%s' does not have a rrdhost", rrdset_id(rd->rrdset));
252
253 if(unlikely(!rd->rrdset->rrdcontexts.rrdinstance))
254 fatal("RRDMETRIC: rrdset '%s' does not have a rrdinstance", rrdset_id(rd->rrdset));
255
256 RRDINSTANCE *ri = rrdinstance_acquired_value(rd->rrdset->rrdcontexts.rrdinstance);
257
258 RRDMETRIC trm = {
259 .uuid = uuidmap_dup(rd->uuid),
260 .id = string_dup(rd->id),
261 .name = string_dup(rd->name),
262 .flags = RRD_FLAG_NONE, // no need for atomics
263 .rrddim = rd,
264 .algorithm = rd->algorithm,
265 };
266
267 RRDMETRIC_ACQUIRED *rma = (RRDMETRIC_ACQUIRED *)dictionary_set_and_acquire_item(ri->rrdmetrics, string2str(trm.id), &trm, sizeof(trm));
268
269 if(rd->rrdcontexts.rrdmetric)
270 rrdmetric_release(rd->rrdcontexts.rrdmetric);
271
272 rd->rrdcontexts.rrdmetric = rma;
273 rrdmetric_not_collected_rrddim(rd);
274 }
275
276 #define rrddim_get_rrdmetric(rd) rrddim_get_rrdmetric_with_trace(rd, __FUNCTION__)
277 static ALWAYS_INLINE RRDMETRIC *rrddim_get_rrdmetric_with_trace(RRDDIM *rd, const char *function) {
278 if(unlikely(!rd->rrdcontexts.rrdmetric)) {
279 netdata_log_error("RRDMETRIC: RRDDIM '%s' is not linked to an RRDMETRIC at %s()", rrddim_id(rd), function);
280 return NULL;
281 }
282
283 RRDMETRIC *rm = rrdmetric_acquired_value(rd->rrdcontexts.rrdmetric);
284 if(unlikely(!rm)) {
285 netdata_log_error("RRDMETRIC: RRDDIM '%s' lost the link to its RRDMETRIC at %s()", rrddim_id(rd), function);
286 return NULL;
287 }
288
289 if(unlikely(rrdmetric_rrddim_atomic_load(rm) != rd))
290 fatal("RRDMETRIC: '%s' is not linked to RRDDIM '%s' at %s()", string2str(rm->id), rrddim_id(rd), function);
291
292 return rm;
293 }
294
295 inline void rrdmetric_rrddim_is_freed(RRDDIM *rd) {
296 RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
297 if(unlikely(!rm)) return;
298
299 if(unlikely(rrd_flag_is_collected(rm)))
300 rrdmetric_set_archived(rm);
301
302 rrdmetric_rrddim_atomic_store(rm, NULL);
303 rrdmetric_trigger_updates(rm, __FUNCTION__ );
304 rrdmetric_release(rd->rrdcontexts.rrdmetric);
305 rd->rrdcontexts.rrdmetric = NULL;
306 rrdmetric_not_collected_rrddim(rd);
307 }
308
309 inline void rrdmetric_updated_rrddim_flags(RRDDIM *rd) {
310 rrdmetric_not_collected_rrddim(rd);
311
312 RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
313 if(unlikely(!rm)) return;
314
315 if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))) {
316 if(unlikely(rrd_flag_is_collected(rm)))
317 rrdmetric_set_archived(rm);
318 }
319
320 rrdmetric_trigger_updates(rm, __FUNCTION__ );
321 }
322
323 inline void rrdmetric_updated_rrddim_algorithm(RRDDIM *rd) {
324 rrdmetric_not_collected_rrddim(rd);
325
326 RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
327 if(unlikely(!rm)) return;
328
329 if(rrdmetric_algorithm_atomic_load(rm) != rd->algorithm) {
330 rrdmetric_algorithm_atomic_store(rm, rd->algorithm);
331 rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
332 }
333
334 rrdmetric_trigger_updates(rm, __FUNCTION__ );
335 }
336
337 ALWAYS_INLINE void rrdmetric_collected_rrddim(RRDDIM *rd) {
338 if(rd->rrdcontexts.collected)
339 return;
340
341 rd->rrdcontexts.collected = true;
342
343 RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
344 if(unlikely(!rm)) return;
345
346 if(unlikely(!rrd_flag_is_collected(rm)))
347 rrdmetric_set_collected(rm);
348
349 // we use this variable to detect BEGIN/END without SET
350 rm->ri->internal.collected_metrics_count++;
351
352 rrdmetric_trigger_updates(rm, __FUNCTION__ );
353 }