master
c 272 lines 9.38 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "rrdcontext-internal.h"
4
5 inline const char *rrdcontext_acquired_id(RRDCONTEXT_ACQUIRED *rca) {
6 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
7 return string2str(rc->id);
8 }
9
10 inline bool rrdcontext_acquired_belongs_to_host(RRDCONTEXT_ACQUIRED *rca, RRDHOST *host) {
11 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
12 return rc->rrdhost == host;
13 }
14
15 // ----------------------------------------------------------------------------
16 // RRDCONTEXT
17
18 static void rrdcontext_freez(RRDCONTEXT *rc) {
19 string_freez(rc->id);
20 string_freez(rc->title);
21 string_freez(rc->units);
22 string_freez(rc->family);
23 }
24
25 static void rrdcontext_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdhost) {
26 RRDHOST *host = (RRDHOST *)rrdhost;
27 RRDCONTEXT *rc = (RRDCONTEXT *)value;
28
29 rc->rrdhost = host;
30 rc->flags = rc->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics at constructor
31
32 // Add the context to the registry to track unique contexts
33 rrdcontext_context_registry_add(rc->id);
34
35 if(rc->hub.version) {
36 // we are loading data from the SQL database
37
38 if(rc->version)
39 netdata_log_error("RRDCONTEXT: context '%s' is already initialized with version %"PRIu64", but it is loaded again from SQL with version %"PRIu64"", string2str(rc->id), rc->version, rc->hub.version);
40
41 // IMPORTANT
42 // replace all string pointers in rc->hub with our own versions
43 // the originals are coming from a tmp allocation of sqlite
44
45 string_freez(rc->id);
46 rc->id = string_strdupz(rc->hub.id);
47 rc->hub.id = string2str(rc->id);
48
49 string_freez(rc->title);
50 rc->title = string_strdupz(rc->hub.title);
51 rc->hub.title = string2str(rc->title);
52
53 string_freez(rc->units);
54 rc->units = string_strdupz(rc->hub.units);
55 rc->hub.units = string2str(rc->units);
56
57 string_freez(rc->family);
58 rc->family = string_strdupz(rc->hub.family);
59 rc->hub.family = string2str(rc->family);
60
61 rc->chart_type = rrdset_type_id(rc->hub.chart_type);
62 rc->hub.chart_type = rrdset_type_name(rc->chart_type);
63
64 rc->version = rc->hub.version;
65 rc->priority = rc->hub.priority;
66 rc->first_time_s = (time_t)rc->hub.first_time_s;
67 rc->last_time_s = (time_t)rc->hub.last_time_s;
68
69 if(rc->hub.deleted || !rc->hub.first_time_s)
70 rrdcontext_set_deleted(rc, RRD_FLAG_NONE);
71 else {
72 if (rc->last_time_s == 0)
73 rrdcontext_set_collected(rc);
74 else
75 rrdcontext_set_archived(rc);
76 }
77
78 rc->flags |= RRD_FLAG_UPDATE_REASON_LOAD_SQL; // no need for atomics at constructor
79 }
80 else {
81 // we are adding this context now for the first time
82 rc->version = now_realtime_sec();
83 }
84
85 rrdinstances_create_in_rrdcontext(rc);
86 spinlock_init(&rc->spinlock);
87
88 // update the count of contexts
89 __atomic_add_fetch(&rc->rrdhost->rrdctx.contexts_count, 1, __ATOMIC_RELAXED);
90
91 // signal the react callback to do the job
92 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_NEW_OBJECT);
93 }
94
95 static void rrdcontext_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdhost __maybe_unused) {
96
97 RRDCONTEXT *rc = (RRDCONTEXT *)value;
98
99 // update the count of contexts
100 __atomic_sub_fetch(&rc->rrdhost->rrdctx.contexts_count, 1, __ATOMIC_RELAXED);
101
102 // Remove the context from the registry
103 rrdcontext_context_registry_remove(rc->id);
104
105 rrdcontext_del_from_hub_queue(rc, false);
106 rrdcontext_del_from_pp_queue(rc, false);
107
108 rrdinstances_destroy_from_rrdcontext(rc);
109 rrdcontext_freez(rc);
110 }
111
112 typedef enum __attribute__((packed)) {
113 OLDNEW_KEEP_OLD,
114 OLDNEW_USE_NEW,
115 OLDNEW_MERGE,
116 } OLDNEW;
117
118 static inline OLDNEW oldnew_decide(bool archived, bool new_archived) {
119 if(archived && !new_archived)
120 return OLDNEW_USE_NEW;
121
122 if(!archived && new_archived)
123 return OLDNEW_KEEP_OLD;
124
125 return OLDNEW_MERGE;
126 }
127
128 static inline void string_replace(STRING **stringpp, STRING *new_string) {
129 STRING *old = *stringpp;
130 *stringpp = string_dup(new_string);
131 string_freez(old);
132 }
133
134 static inline void string_merge(STRING **stringpp, STRING *new_string) {
135 STRING *old = *stringpp;
136 *stringpp = string_2way_merge(*stringpp, new_string);
137 string_freez(old);
138 }
139
140 static void rrdcontext_merge_with(RRDCONTEXT *rc, bool archived, STRING *title, STRING *family, STRING *units, RRDSET_TYPE chart_type, uint32_t priority) {
141 OLDNEW oldnew = oldnew_decide(rrd_flag_is_archived(rc), archived);
142
143 switch(oldnew) {
144 case OLDNEW_KEEP_OLD:
145 break;
146
147 case OLDNEW_USE_NEW:
148 if(rc->title != title) {
149 string_replace(&rc->title, title);
150 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
151 }
152 if(rc->family != family) {
153 string_replace(&rc->family, family);
154 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
155 }
156 break;
157
158 case OLDNEW_MERGE:
159 if(rc->title != title) {
160 string_merge(&rc->title, title);
161 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
162 }
163 if(rc->family != family) {
164 string_merge(&rc->family, family);
165 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
166 }
167 break;
168 }
169
170 switch(oldnew) {
171 case OLDNEW_KEEP_OLD:
172 break;
173
174 case OLDNEW_USE_NEW:
175 case OLDNEW_MERGE:
176 if(rc->units != units) {
177 string_replace(&rc->units, units);
178 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
179 }
180
181 if(rc->chart_type != chart_type) {
182 rc->chart_type = chart_type;
183 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
184 }
185
186 if(rc->priority != priority) {
187 rc->priority = priority;
188 rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
189 }
190 break;
191 }
192 }
193
194 void rrdcontext_update_from_collected_rrdinstance(RRDINSTANCE *ri) {
195 rrdcontext_merge_with(ri->rc, rrd_flag_is_archived(ri),
196 ri->title, ri->family, ri->units, ri->chart_type, ri->priority);
197 }
198
199 static bool rrdcontext_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *rrdhost __maybe_unused) {
200 RRDCONTEXT *rc = (RRDCONTEXT *)old_value;
201 RRDCONTEXT *rc_new = (RRDCONTEXT *)new_value;
202
203 //current rc is not archived, new_rc is archived, don't merge
204 if (!rrd_flag_is_archived(rc) && rrd_flag_is_archived(rc_new)) {
205 rrdcontext_freez(rc_new);
206 return false;
207 }
208
209 rrdcontext_lock(rc);
210
211 rrdcontext_merge_with(rc, rrd_flag_is_archived(rc_new),
212 rc_new->title, rc_new->family, rc_new->units, rc_new->chart_type, rc_new->priority);
213
214 rrd_flag_set(rc, rc_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no need for atomics on rc_new
215
216 if(rrd_flag_is_collected(rc) && rrd_flag_is_archived(rc))
217 rrdcontext_set_collected(rc);
218
219 if(rrd_flag_is_updated(rc))
220 rrd_flag_set(rc, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT);
221
222 rrdcontext_unlock(rc);
223
224 // free the resources of the new one
225 rrdcontext_freez(rc_new);
226
227 // the react callback will continue from here
228 return rrd_flag_is_updated(rc);
229 }
230
231 static void rrdcontext_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdhost __maybe_unused) {
232 RRDCONTEXT *rc = (RRDCONTEXT *)value;
233 rrdcontext_trigger_updates(rc, __FUNCTION__ );
234 }
235
236 void rrdcontext_trigger_updates(RRDCONTEXT *rc, const char *function) {
237 if(rrd_flag_is_updated(rc) || !rrd_flag_check(rc, RRD_FLAG_LIVE_RETENTION))
238 rrdcontext_queue_for_post_processing(rc, function, rc->flags);
239 }
240
241 void rrdhost_create_rrdcontexts(RRDHOST *host) {
242 if(unlikely(!host)) return;
243 if(likely(host->rrdctx.contexts)) return;
244
245 host->rrdctx.contexts = dictionary_create_advanced(
246 DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
247 &dictionary_stats_category_rrdcontext, sizeof(RRDCONTEXT));
248
249 dictionary_register_insert_callback(host->rrdctx.contexts, rrdcontext_insert_callback, host);
250 dictionary_register_delete_callback(host->rrdctx.contexts, rrdcontext_delete_callback, host);
251 dictionary_register_conflict_callback(host->rrdctx.contexts, rrdcontext_conflict_callback, host);
252 dictionary_register_react_callback(host->rrdctx.contexts, rrdcontext_react_callback, host);
253
254 memset(&host->rrdctx.pp_queue, 0, sizeof(host->rrdctx.pp_queue));
255 RRDCONTEXT_QUEUE_INIT(&host->rrdctx.pp_queue);
256 spinlock_init(&host->rrdctx.pp_queue.spinlock);
257
258 memset(&host->rrdctx.hub_queue, 0, sizeof(host->rrdctx.hub_queue));
259 RRDCONTEXT_QUEUE_INIT(&host->rrdctx.hub_queue);
260 spinlock_init(&host->rrdctx.hub_queue.spinlock);
261 }
262
263 void rrdhost_destroy_rrdcontexts(RRDHOST *host) {
264 if(unlikely(!host)) return;
265 if(unlikely(!host->rrdctx.contexts)) return;
266
267 dictionary_destroy(host->rrdctx.contexts);
268 host->rrdctx.contexts = NULL;
269
270 RRDCONTEXT_QUEUE_FREE(&host->rrdctx.pp_queue, NULL, NULL);
271 RRDCONTEXT_QUEUE_FREE(&host->rrdctx.hub_queue, NULL, NULL);
272 }