| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #ifndef NETDATA_RRDDIM_H |
| 4 | #define NETDATA_RRDDIM_H |
| 5 | |
| 6 | #include "libnetdata/libnetdata.h" |
| 7 | #include "rrd-algorithm.h" |
| 8 | |
| 9 | typedef struct rrddim RRDDIM; |
| 10 | typedef struct rrddim_acquired RRDDIM_ACQUIRED; |
| 11 | typedef struct ml_dimension rrd_ml_dimension_t; |
| 12 | typedef struct rrdmetric_acquired RRDMETRIC_ACQUIRED; |
| 13 | |
| 14 | // gcc with libstdc++ may require this, |
| 15 | // but with libc++ it does not work correctly. |
| 16 | #if defined(__cplusplus) && !defined(_LIBCPP_VERSION) |
| 17 | #include <cmath> |
| 18 | using std::isnan; |
| 19 | #endif |
| 20 | |
| 21 | #include "rrdset.h" |
| 22 | |
| 23 | // options are permanent configuration options (no atomics to alter/access them) |
| 24 | typedef enum __attribute__ ((__packed__)) rrddim_options { |
| 25 | RRDDIM_OPTION_NONE = 0, |
| 26 | RRDDIM_OPTION_HIDDEN = (1 << 0), // this dimension will not be offered to callers |
| 27 | RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS = (1 << 1), // do not offer RESET or OVERFLOW info to callers |
| 28 | RRDDIM_OPTION_BACKFILLED_HIGH_TIERS = (1 << 2), // when set, we have backfilled higher tiers |
| 29 | RRDDIM_OPTION_UPDATED = (1 << 3), // single-threaded collector updated flag |
| 30 | RRDDIM_OPTION_VALUE_FLOAT = (1 << 4), // collected_value lane should be treated as float |
| 31 | |
| 32 | // this is 8-bit |
| 33 | } RRDDIM_OPTIONS; |
| 34 | |
| 35 | #define rrddim_option_check(rd, option) ((rd)->collector.options & (option)) |
| 36 | #define rrddim_option_set(rd, option) (rd)->collector.options |= (option) |
| 37 | #define rrddim_option_clear(rd, option) (rd)->collector.options &= ~(option) |
| 38 | #define rrddim_is_float(rd) ((rd)->collector.options & RRDDIM_OPTION_VALUE_FLOAT) |
| 39 | #define rrddim_is_int(rd) (!rrddim_is_float(rd)) |
| 40 | |
| 41 | // flags are runtime changing status flags (atomics are required to alter/access them) |
| 42 | typedef enum __attribute__ ((__packed__)) rrddim_flags { |
| 43 | RRDDIM_FLAG_NONE = 0, |
| 44 | |
| 45 | RRDDIM_FLAG_OBSOLETE = (1 << 0), // this is marked by the collector/module as obsolete |
| 46 | // No new values have been collected for this dimension since agent start, or it was marked RRDDIM_FLAG_OBSOLETE at |
| 47 | // least rrdset_free_obsolete_time seconds ago. |
| 48 | |
| 49 | RRDDIM_FLAG_METADATA_UPDATE = (1 << 1), // Metadata needs to go to the database |
| 50 | |
| 51 | RRDDIM_FLAG_META_HIDDEN = (1 << 2), // Status of hidden option in the metadata database |
| 52 | RRDDIM_FLAG_ML_MODEL_LOAD = (1 << 3), // Do ML LOAD for this dimension |
| 53 | |
| 54 | // this is 8 bit |
| 55 | } RRDDIM_FLAGS; |
| 56 | |
| 57 | #define rrddim_flag_get(rd) atomic_flags_get(&((rd)->flags)) |
| 58 | #define rrddim_flag_check(rd, flag) atomic_flags_check(&((rd)->flags), flag) |
| 59 | #define rrddim_flag_set(rd, flag) atomic_flags_set(&((rd)->flags), flag) |
| 60 | #define rrddim_flag_clear(rd, flag) atomic_flags_clear(&((rd)->flags), flag) |
| 61 | #define rrddim_flag_set_and_clear(rd, set, clear) atomic_flags_set_and_clear(&((rd)->flags), set, clear) |
| 62 | |
| 63 | struct rrddim { |
| 64 | UUIDMAP_ID uuid; |
| 65 | |
| 66 | // ------------------------------------------------------------------------ |
| 67 | // dimension definition |
| 68 | |
| 69 | STRING *id; // the id of this dimension (for internal identification) |
| 70 | STRING *name; // the name of this dimension (as presented to user) |
| 71 | |
| 72 | RRD_ALGORITHM algorithm; // the algorithm that is applied to add new collected values |
| 73 | RRD_DB_MODE rrd_memory_mode; // the memory mode for this dimension |
| 74 | RRDDIM_FLAGS flags; // run time changing status flags |
| 75 | |
| 76 | SPINLOCK destroy_lock; |
| 77 | int32_t multiplier; // the multiplier of the collected values |
| 78 | int32_t divisor; // the divider of the collected values |
| 79 | |
| 80 | // ------------------------------------------------------------------------ |
| 81 | // operational state members |
| 82 | |
| 83 | struct rrdset *rrdset; |
| 84 | rrd_ml_dimension_t *ml_dimension; // machine learning data about this dimension |
| 85 | |
| 86 | struct { |
| 87 | RRDMETRIC_ACQUIRED *rrdmetric; // the rrdmetric of this dimension |
| 88 | bool collected; |
| 89 | } rrdcontexts; |
| 90 | |
| 91 | #ifdef NETDATA_LOG_COLLECTION_ERRORS |
| 92 | usec_t rrddim_store_metric_last_ut; // the timestamp we last called rrddim_store_metric() |
| 93 | size_t rrddim_store_metric_count; // the rrddim_store_metric() counter |
| 94 | const char *rrddim_store_metric_last_caller; // the name of the function that last called rrddim_store_metric() |
| 95 | #endif |
| 96 | |
| 97 | // ------------------------------------------------------------------------ |
| 98 | // db mode RAM, ALLOC, NONE specifics |
| 99 | // TODO - they should be managed by storage engine |
| 100 | // (RRDDIM_DB_STATE ptr to an undefined structure, and a call to clean this up during destruction) |
| 101 | |
| 102 | struct { |
| 103 | size_t memsize; // the memory allocated for this dimension (without RRDDIM) |
| 104 | storage_number *data; // the array of values |
| 105 | } db; |
| 106 | |
| 107 | // ------------------------------------------------------------------------ |
| 108 | // streaming |
| 109 | |
| 110 | struct { |
| 111 | struct { |
| 112 | uint32_t sent_version; |
| 113 | uint32_t dim_slot; |
| 114 | } snd; |
| 115 | } stream; |
| 116 | |
| 117 | // ------------------------------------------------------------------------ |
| 118 | // data collection members |
| 119 | |
| 120 | struct { |
| 121 | RRDDIM_OPTIONS options; // permanent configuration options |
| 122 | |
| 123 | uint32_t counter; // the number of times we added values to this rrddim |
| 124 | |
| 125 | union { |
| 126 | struct { |
| 127 | collected_number collected_value; // legacy int lane |
| 128 | collected_number collected_value_max; // legacy int lane |
| 129 | collected_number last_collected_value; // legacy int lane |
| 130 | } i; |
| 131 | struct { |
| 132 | NETDATA_DOUBLE collected_value; |
| 133 | NETDATA_DOUBLE collected_value_max; |
| 134 | NETDATA_DOUBLE last_collected_value; |
| 135 | } f; |
| 136 | } collected; // type-aware storage (option bit selects lane) |
| 137 | |
| 138 | struct timeval last_collected_time; // when was this dimension last updated |
| 139 | // this is actual date time we updated the last_collected_value |
| 140 | // THIS IS DIFFERENT FROM THE SAME MEMBER OF RRDSET |
| 141 | |
| 142 | NETDATA_DOUBLE calculated_value; // the current calculated value, after applying the algorithm - resets to zero after being used |
| 143 | NETDATA_DOUBLE last_calculated_value; // the last calculated value processed |
| 144 | |
| 145 | NETDATA_DOUBLE last_stored_value; // the last value as stored in the database (after interpolation) |
| 146 | } collector; |
| 147 | |
| 148 | // ------------------------------------------------------------------------ |
| 149 | |
| 150 | struct rrddim_tier tiers[]; // our tiers of databases |
| 151 | }; |
| 152 | |
| 153 | size_t rrddim_size(void); |
| 154 | |
| 155 | #define rrddim_id(rd) string2str((rd)->id) |
| 156 | #define rrddim_name(rd) string2str((rd) ->name) |
| 157 | |
| 158 | #define rrddim_check_updated(rd) ((rd)->collector.options & RRDDIM_OPTION_UPDATED) |
| 159 | #define rrddim_set_updated(rd) (rd)->collector.options |= RRDDIM_OPTION_UPDATED |
| 160 | #define rrddim_clear_updated(rd) (rd)->collector.options &= ~RRDDIM_OPTION_UPDATED |
| 161 | |
| 162 | #define rrddim_foreach_read(rd, st) \ |
| 163 | dfe_start_read((st)->rrddim_root_index, rd) |
| 164 | |
| 165 | #define rrddim_foreach_done(rd) \ |
| 166 | dfe_done(rd) |
| 167 | |
| 168 | struct pluginsd_rrddim { |
| 169 | RRDDIM_ACQUIRED *rda; |
| 170 | RRDDIM *rd; |
| 171 | const char *id; |
| 172 | }; |
| 173 | |
| 174 | static inline uint32_t rrddim_metadata_version(RRDDIM *rd) { |
| 175 | // the metadata version of the dimension, is the version of the chart |
| 176 | return rrdset_metadata_version(rd->rrdset); |
| 177 | } |
| 178 | |
| 179 | static inline uint32_t rrddim_metadata_upstream_version(RRDDIM *rd) { |
| 180 | return __atomic_load_n(&rd->stream.snd.sent_version, __ATOMIC_RELAXED); |
| 181 | } |
| 182 | |
| 183 | void rrddim_metadata_updated(RRDDIM *rd); |
| 184 | |
| 185 | static inline void rrddim_metadata_exposed_upstream(RRDDIM *rd, uint32_t version) { |
| 186 | __atomic_store_n(&rd->stream.snd.sent_version, version, __ATOMIC_RELAXED); |
| 187 | } |
| 188 | |
| 189 | static inline void rrddim_metadata_exposed_upstream_clear(RRDDIM *rd) { |
| 190 | __atomic_store_n(&rd->stream.snd.sent_version, 0, __ATOMIC_RELAXED); |
| 191 | } |
| 192 | |
| 193 | static inline bool rrddim_check_upstream_exposed(RRDDIM *rd) { |
| 194 | return rrddim_metadata_upstream_version(rd) != 0; |
| 195 | } |
| 196 | |
| 197 | // the collector sets the exposed flag, but anyone can remove it |
| 198 | // still, it can be removed, after the collector has finished |
| 199 | // so, it is safe to check it without atomics |
| 200 | static inline bool rrddim_check_upstream_exposed_collector(RRDDIM *rd) { |
| 201 | return rd->rrdset->version == rd->stream.snd.sent_version; |
| 202 | } |
| 203 | |
| 204 | // ------------------------------------------------------------------------ |
| 205 | // Type-aware accessors (Phase 1: int-only behavior preserved) |
| 206 | |
| 207 | static inline void rrddim_set_collected_int(RRDDIM *rd, int64_t v) { |
| 208 | rd->collector.collected.i.collected_value = (collected_number)v; |
| 209 | } |
| 210 | |
| 211 | static inline void rrddim_set_collected_float(RRDDIM *rd, NETDATA_DOUBLE v) { |
| 212 | rd->collector.collected.f.collected_value = v; |
| 213 | } |
| 214 | |
| 215 | static inline void rrddim_set_collected_max_int(RRDDIM *rd, int64_t v) { |
| 216 | rd->collector.collected.i.collected_value_max = (collected_number)v; |
| 217 | } |
| 218 | |
| 219 | static inline void rrddim_set_collected_max_float(RRDDIM *rd, NETDATA_DOUBLE v) { |
| 220 | rd->collector.collected.f.collected_value_max = v; |
| 221 | } |
| 222 | |
| 223 | static inline void rrddim_set_last_collected_int(RRDDIM *rd, int64_t v) { |
| 224 | rd->collector.collected.i.last_collected_value = (collected_number)v; |
| 225 | } |
| 226 | |
| 227 | static inline void rrddim_set_last_collected_float(RRDDIM *rd, NETDATA_DOUBLE v) { |
| 228 | rd->collector.collected.f.last_collected_value = v; |
| 229 | } |
| 230 | |
| 231 | static inline NETDATA_DOUBLE rrddim_collected_as_double(RRDDIM *rd) { |
| 232 | return rrddim_is_float(rd) ? rd->collector.collected.f.collected_value |
| 233 | : (NETDATA_DOUBLE)rd->collector.collected.i.collected_value; |
| 234 | } |
| 235 | |
| 236 | static inline NETDATA_DOUBLE rrddim_last_collected_as_double(RRDDIM *rd) { |
| 237 | return rrddim_is_float(rd) ? rd->collector.collected.f.last_collected_value |
| 238 | : (NETDATA_DOUBLE)rd->collector.collected.i.last_collected_value; |
| 239 | } |
| 240 | |
| 241 | static inline NETDATA_DOUBLE rrddim_collected_max_as_double(RRDDIM *rd) { |
| 242 | return rrddim_is_float(rd) ? rd->collector.collected.f.collected_value_max |
| 243 | : (NETDATA_DOUBLE)rd->collector.collected.i.collected_value_max; |
| 244 | } |
| 245 | |
| 246 | static inline int64_t rrddim_last_collected_raw_int(RRDDIM *rd) { |
| 247 | return rrddim_is_float(rd) ? (int64_t)rd->collector.collected.f.last_collected_value |
| 248 | : (int64_t)rd->collector.collected.i.last_collected_value; |
| 249 | } |
| 250 | |
| 251 | void rrddim_index_init(RRDSET *st); |
| 252 | void rrddim_index_destroy(RRDSET *st); |
| 253 | |
| 254 | time_t rrddim_first_entry_s(RRDDIM *rd); |
| 255 | time_t rrddim_first_entry_s_of_tier(RRDDIM *rd, size_t tier); |
| 256 | time_t rrddim_last_entry_s(RRDDIM *rd); |
| 257 | time_t rrddim_last_entry_s_of_tier(RRDDIM *rd, size_t tier); |
| 258 | |
| 259 | RRDDIM *rrddim_add_custom(RRDSET *st |
| 260 | , const char *id |
| 261 | , const char *name |
| 262 | , collected_number multiplier |
| 263 | , collected_number divisor |
| 264 | , RRD_ALGORITHM algorithm |
| 265 | , RRD_DB_MODE memory_mode |
| 266 | ); |
| 267 | |
| 268 | #define rrddim_add(st, id, name, multiplier, divisor, algorithm) \ |
| 269 | rrddim_add_custom(st, id, name, multiplier, divisor, algorithm, (st)->rrd_memory_mode) |
| 270 | |
| 271 | int rrddim_reset_name(RRDSET *st, RRDDIM *rd, const char *name); |
| 272 | int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm); |
| 273 | int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, int32_t multiplier); |
| 274 | int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, int32_t divisor); |
| 275 | |
| 276 | RRDDIM *rrddim_find(RRDSET *st, const char *id, bool include_obsolete); |
| 277 | RRDDIM_ACQUIRED *rrddim_find_and_acquire(RRDSET *st, const char *id, bool include_obsolete); |
| 278 | RRDDIM *rrddim_acquired_to_rrddim(RRDDIM_ACQUIRED *rda); |
| 279 | void rrddim_acquired_release(RRDDIM_ACQUIRED *rda); |
| 280 | #define rrddim_find_active(st, id) rrddim_find(st, id, false) |
| 281 | |
| 282 | int rrddim_hide(RRDSET *st, const char *id); |
| 283 | int rrddim_unhide(RRDSET *st, const char *id); |
| 284 | |
| 285 | void rrddim_is_obsolete___safe_from_collector_thread(RRDSET *st, RRDDIM *rd); |
| 286 | void rrddim_isnot_obsolete___safe_from_collector_thread(RRDSET *st, RRDDIM *rd); |
| 287 | |
| 288 | collected_number rrddim_timed_set_by_pointer(RRDSET *st, RRDDIM *rd, struct timeval collected_time, collected_number value); |
| 289 | collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value); |
| 290 | NETDATA_DOUBLE rrddim_set_by_pointer_double(RRDSET *st, RRDDIM *rd, NETDATA_DOUBLE value); |
| 291 | collected_number rrddim_set(RRDSET *st, const char *id, collected_number value); |
| 292 | |
| 293 | bool rrddim_finalize_collection_and_check_retention(RRDDIM *rd); |
| 294 | |
| 295 | void rrddim_free(RRDSET *st, RRDDIM *rd); |
| 296 | |
| 297 | static inline double rrddim_get_last_stored_value(RRDDIM *rd_dim, double *max_value, double div) { |
| 298 | if (!rd_dim) |
| 299 | return NAN; |
| 300 | |
| 301 | if (isnan(div) || div == 0.0) |
| 302 | div = 1.0; |
| 303 | |
| 304 | double value = rd_dim->collector.last_stored_value / div; |
| 305 | value = ABS(value); |
| 306 | |
| 307 | *max_value = MAX(*max_value, value); |
| 308 | |
| 309 | return value; |
| 310 | } |
| 311 | |
| 312 | #endif //NETDATA_RRDDIM_H |