| 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 | } |