rrddim.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #define NETDATA_RRD_INTERNALS
  3. #include "rrd.h"
  4. #include "storage_engine.h"
  5. // ----------------------------------------------------------------------------
  6. // RRDDIM index
  7. struct rrddim_constructor {
  8. RRDSET *st;
  9. const char *id;
  10. const char *name;
  11. collected_number multiplier;
  12. collected_number divisor;
  13. RRD_ALGORITHM algorithm;
  14. RRD_MEMORY_MODE memory_mode;
  15. enum {
  16. RRDDIM_REACT_NONE = 0,
  17. RRDDIM_REACT_NEW = (1 << 0),
  18. RRDDIM_REACT_UPDATED = (1 << 2),
  19. } react_action;
  20. };
  21. // isolated call to appear
  22. // separate in statistics
  23. static void *rrddim_alloc_db(size_t entries) {
  24. return callocz(entries, sizeof(storage_number));
  25. }
  26. static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *constructor_data) {
  27. struct rrddim_constructor *ctr = constructor_data;
  28. RRDDIM *rd = rrddim;
  29. RRDSET *st = ctr->st;
  30. RRDHOST *host = st->rrdhost;
  31. rd->flags = RRDDIM_FLAG_NONE;
  32. rd->id = string_strdupz(ctr->id);
  33. rd->name = (ctr->name && *ctr->name)?rrd_string_strdupz(ctr->name):string_dup(rd->id);
  34. rd->algorithm = ctr->algorithm;
  35. rd->multiplier = ctr->multiplier;
  36. rd->divisor = ctr->divisor;
  37. if(!rd->divisor) rd->divisor = 1;
  38. rd->update_every = st->update_every;
  39. rd->rrdset = st;
  40. if(rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST))
  41. rd->collections_counter = 1;
  42. if(ctr->memory_mode == RRD_MEMORY_MODE_MAP || ctr->memory_mode == RRD_MEMORY_MODE_SAVE) {
  43. if(!rrddim_memory_load_or_create_map_save(st, rd, ctr->memory_mode)) {
  44. info("Failed to use memory mode %s for chart '%s', dimension '%s', falling back to ram", (ctr->memory_mode == RRD_MEMORY_MODE_MAP)?"map":"save", rrdset_name(st), rrddim_name(rd));
  45. ctr->memory_mode = RRD_MEMORY_MODE_RAM;
  46. }
  47. }
  48. if(ctr->memory_mode == RRD_MEMORY_MODE_RAM) {
  49. size_t entries = st->entries;
  50. if(!entries) entries = 5;
  51. rd->db = netdata_mmap(NULL, entries * sizeof(storage_number), MAP_PRIVATE, 1, false, NULL);
  52. if(!rd->db) {
  53. info("Failed to use memory mode ram for chart '%s', dimension '%s', falling back to alloc", rrdset_name(st), rrddim_name(rd));
  54. ctr->memory_mode = RRD_MEMORY_MODE_ALLOC;
  55. }
  56. else {
  57. rd->memsize = entries * sizeof(storage_number);
  58. __atomic_add_fetch(&rrddim_db_memory_size, rd->memsize, __ATOMIC_RELAXED);
  59. }
  60. }
  61. if(ctr->memory_mode == RRD_MEMORY_MODE_ALLOC || ctr->memory_mode == RRD_MEMORY_MODE_NONE) {
  62. size_t entries = st->entries;
  63. if(entries < 5) entries = 5;
  64. rd->db = rrddim_alloc_db(entries);
  65. rd->memsize = entries * sizeof(storage_number);
  66. __atomic_add_fetch(&rrddim_db_memory_size, rd->memsize, __ATOMIC_RELAXED);
  67. }
  68. rd->rrd_memory_mode = ctr->memory_mode;
  69. if (unlikely(rrdcontext_find_dimension_uuid(st, rrddim_id(rd), &(rd->metric_uuid))))
  70. uuid_generate(rd->metric_uuid);
  71. // initialize the db tiers
  72. {
  73. size_t initialized = 0;
  74. for(size_t tier = 0; tier < storage_tiers ; tier++) {
  75. STORAGE_ENGINE *eng = host->db[tier].eng;
  76. rd->tiers[tier].tier_grouping = host->db[tier].tier_grouping;
  77. rd->tiers[tier].collect_ops = &eng->api.collect_ops;
  78. rd->tiers[tier].query_ops = &eng->api.query_ops;
  79. rd->tiers[tier].db_metric_handle = eng->api.metric_get_or_create(rd, host->db[tier].instance);
  80. storage_point_unset(rd->tiers[tier].virtual_point);
  81. initialized++;
  82. // 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);
  83. }
  84. if(!initialized)
  85. error("Failed to initialize all db tiers for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd));
  86. if(!rd->tiers[0].db_metric_handle)
  87. error("Failed to initialize the first db tier for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd));
  88. }
  89. // initialize data collection for all tiers
  90. {
  91. size_t initialized = 0;
  92. for (size_t tier = 0; tier < storage_tiers; tier++) {
  93. if (rd->tiers[tier].db_metric_handle) {
  94. rd->tiers[tier].db_collection_handle = rd->tiers[tier].collect_ops->init(rd->tiers[tier].db_metric_handle, st->rrdhost->db[tier].tier_grouping * st->update_every, rd->rrdset->storage_metrics_groups[tier]);
  95. initialized++;
  96. }
  97. }
  98. if(!initialized)
  99. error("Failed to initialize data collection for all db tiers for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd));
  100. }
  101. if(rrdset_number_of_dimensions(st) != 0) {
  102. RRDDIM *td;
  103. dfe_start_write(st->rrddim_root_index, td) {
  104. if(!td) break;
  105. }
  106. dfe_done(td);
  107. if(td && (td->algorithm != rd->algorithm || ABS(td->multiplier) != ABS(rd->multiplier) || ABS(td->divisor) != ABS(rd->divisor))) {
  108. if(!rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) {
  109. #ifdef NETDATA_INTERNAL_CHECKS
  110. info("Dimension '%s' added on chart '%s' of host '%s' is not homogeneous to other dimensions already present (algorithm is '%s' vs '%s', multiplier is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ", divisor is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ").",
  111. rrddim_name(rd),
  112. rrdset_name(st),
  113. rrdhost_hostname(host),
  114. rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(td->algorithm),
  115. rd->multiplier, td->multiplier,
  116. rd->divisor, td->divisor
  117. );
  118. #endif
  119. rrdset_flag_set(st, RRDSET_FLAG_HETEROGENEOUS);
  120. }
  121. }
  122. }
  123. rrddim_flag_set(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION);
  124. rrdset_flag_set(rd->rrdset, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION);
  125. rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION);
  126. // let the chart resync
  127. rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK);
  128. rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED);
  129. ml_dimension_new(rd);
  130. ctr->react_action = RRDDIM_REACT_NEW;
  131. internal_error(false, "RRDDIM: inserted dimension '%s' of chart '%s' of host '%s'",
  132. rrddim_name(rd), rrdset_name(st), rrdhost_hostname(st->rrdhost));
  133. }
  134. bool rrddim_finalize_collection_and_check_retention(RRDDIM *rd) {
  135. size_t tiers_available = 0, tiers_said_no_retention = 0;
  136. for(size_t tier = 0; tier < storage_tiers ;tier++) {
  137. if(!rd->tiers[tier].db_collection_handle)
  138. continue;
  139. tiers_available++;
  140. if(rd->tiers[tier].collect_ops->finalize(rd->tiers[tier].db_collection_handle))
  141. tiers_said_no_retention++;
  142. rd->tiers[tier].db_collection_handle = NULL;
  143. }
  144. // return true if the dimension has retention in the db
  145. return (!tiers_said_no_retention || tiers_available > tiers_said_no_retention);
  146. }
  147. static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *rrdset) {
  148. RRDDIM *rd = rrddim;
  149. RRDSET *st = rrdset;
  150. RRDHOST *host = st->rrdhost;
  151. internal_error(false, "RRDDIM: deleting dimension '%s' of chart '%s' of host '%s'",
  152. rrddim_name(rd), rrdset_name(st), rrdhost_hostname(host));
  153. rrdcontext_removed_rrddim(rd);
  154. ml_dimension_delete(rd);
  155. debug(D_RRD_CALLS, "rrddim_free() %s.%s", rrdset_name(st), rrddim_name(rd));
  156. if (!rrddim_finalize_collection_and_check_retention(rd) && rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
  157. /* This metric has no data and no references */
  158. metaqueue_delete_dimension_uuid(&rd->metric_uuid);
  159. }
  160. rrddimvar_delete_all(rd);
  161. // free(rd->annotations);
  162. //#ifdef ENABLE_ACLK
  163. // if (!netdata_exit)
  164. // aclk_send_dimension_update(rd);
  165. //#endif
  166. // this will free MEMORY_MODE_SAVE and MEMORY_MODE_MAP structures
  167. rrddim_memory_file_free(rd);
  168. for(size_t tier = 0; tier < storage_tiers ;tier++) {
  169. if(!rd->tiers[tier].db_metric_handle) continue;
  170. STORAGE_ENGINE* eng = host->db[tier].eng;
  171. eng->api.metric_release(rd->tiers[tier].db_metric_handle);
  172. rd->tiers[tier].db_metric_handle = NULL;
  173. }
  174. if(rd->db) {
  175. __atomic_sub_fetch(&rrddim_db_memory_size, rd->memsize, __ATOMIC_RELAXED);
  176. if(rd->rrd_memory_mode == RRD_MEMORY_MODE_RAM)
  177. netdata_munmap(rd->db, rd->memsize);
  178. else
  179. freez(rd->db);
  180. }
  181. string_freez(rd->id);
  182. string_freez(rd->name);
  183. }
  184. static bool rrddim_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *new_rrddim, void *constructor_data) {
  185. (void)new_rrddim; // it is NULL
  186. struct rrddim_constructor *ctr = constructor_data;
  187. RRDDIM *rd = rrddim;
  188. RRDSET *st = ctr->st;
  189. ctr->react_action = RRDDIM_REACT_NONE;
  190. int rc = rrddim_reset_name(st, rd, ctr->name);
  191. rc += rrddim_set_algorithm(st, rd, ctr->algorithm);
  192. rc += rrddim_set_multiplier(st, rd, ctr->multiplier);
  193. rc += rrddim_set_divisor(st, rd, ctr->divisor);
  194. for(size_t tier = 0; tier < storage_tiers ;tier++) {
  195. if (!rd->tiers[tier].db_collection_handle)
  196. rd->tiers[tier].db_collection_handle =
  197. rd->tiers[tier].collect_ops->init(rd->tiers[tier].db_metric_handle, st->rrdhost->db[tier].tier_grouping * st->update_every, rd->rrdset->storage_metrics_groups[tier]);
  198. }
  199. if(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) {
  200. rrddim_flag_clear(rd, RRDDIM_FLAG_ARCHIVED);
  201. rrddim_flag_set(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION);
  202. rrdset_flag_set(rd->rrdset, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION);
  203. rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION);
  204. }
  205. if(unlikely(rc))
  206. ctr->react_action = RRDDIM_REACT_UPDATED;
  207. return ctr->react_action == RRDDIM_REACT_UPDATED;
  208. }
  209. static void rrddim_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *constructor_data) {
  210. struct rrddim_constructor *ctr = constructor_data;
  211. RRDDIM *rd = rrddim;
  212. RRDSET *st = ctr->st;
  213. if(ctr->react_action & (RRDDIM_REACT_UPDATED | RRDDIM_REACT_NEW)) {
  214. rrddim_flag_set(rd, RRDDIM_FLAG_METADATA_UPDATE);
  215. rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
  216. }
  217. if(ctr->react_action == RRDDIM_REACT_UPDATED) {
  218. // the chart needs to be updated to the parent
  219. rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK);
  220. rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED);
  221. }
  222. rrdcontext_updated_rrddim(rd);
  223. }
  224. void rrddim_index_init(RRDSET *st) {
  225. if(!st->rrddim_root_index) {
  226. st->rrddim_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
  227. &dictionary_stats_category_rrdset_rrddim, sizeof(RRDDIM));
  228. dictionary_register_insert_callback(st->rrddim_root_index, rrddim_insert_callback, NULL);
  229. dictionary_register_conflict_callback(st->rrddim_root_index, rrddim_conflict_callback, NULL);
  230. dictionary_register_delete_callback(st->rrddim_root_index, rrddim_delete_callback, st);
  231. dictionary_register_react_callback(st->rrddim_root_index, rrddim_react_callback, st);
  232. }
  233. }
  234. void rrddim_index_destroy(RRDSET *st) {
  235. dictionary_destroy(st->rrddim_root_index);
  236. st->rrddim_root_index = NULL;
  237. }
  238. static inline RRDDIM *rrddim_index_find(RRDSET *st, const char *id) {
  239. return dictionary_get(st->rrddim_root_index, id);
  240. }
  241. // ----------------------------------------------------------------------------
  242. // RRDDIM - find a dimension
  243. inline RRDDIM *rrddim_find(RRDSET *st, const char *id) {
  244. debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", rrdset_name(st), id);
  245. return rrddim_index_find(st, id);
  246. }
  247. inline RRDDIM_ACQUIRED *rrddim_find_and_acquire(RRDSET *st, const char *id) {
  248. debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", rrdset_name(st), id);
  249. return (RRDDIM_ACQUIRED *)dictionary_get_and_acquire_item(st->rrddim_root_index, id);
  250. }
  251. RRDDIM *rrddim_acquired_to_rrddim(RRDDIM_ACQUIRED *rda) {
  252. if(unlikely(!rda))
  253. return NULL;
  254. return (RRDDIM *) dictionary_acquired_item_value((const DICTIONARY_ITEM *)rda);
  255. }
  256. void rrddim_acquired_release(RRDDIM_ACQUIRED *rda) {
  257. if(unlikely(!rda))
  258. return;
  259. RRDDIM *rd = rrddim_acquired_to_rrddim(rda);
  260. dictionary_acquired_item_release(rd->rrdset->rrddim_root_index, (const DICTIONARY_ITEM *)rda);
  261. }
  262. // This will not return dimensions that are archived
  263. RRDDIM *rrddim_find_active(RRDSET *st, const char *id) {
  264. RRDDIM *rd = rrddim_find(st, id);
  265. if (unlikely(rd && rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)))
  266. return NULL;
  267. return rd;
  268. }
  269. // ----------------------------------------------------------------------------
  270. // RRDDIM rename a dimension
  271. inline int rrddim_reset_name(RRDSET *st, RRDDIM *rd, const char *name) {
  272. if(unlikely(!name || !*name || !strcmp(rrddim_name(rd), name)))
  273. return 0;
  274. debug(D_RRD_CALLS, "rrddim_reset_name() from %s.%s to %s.%s", rrdset_name(st), rrddim_name(rd), rrdset_name(st), name);
  275. STRING *old = rd->name;
  276. rd->name = rrd_string_strdupz(name);
  277. string_freez(old);
  278. rrddimvar_rename_all(rd);
  279. rd->exposed = 0;
  280. rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED);
  281. return 1;
  282. }
  283. inline int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm) {
  284. if(unlikely(rd->algorithm == algorithm))
  285. return 0;
  286. 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));
  287. rd->algorithm = algorithm;
  288. rd->exposed = 0;
  289. rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED);
  290. rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK);
  291. rrdcontext_updated_rrddim_algorithm(rd);
  292. return 1;
  293. }
  294. inline int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, collected_number multiplier) {
  295. if(unlikely(rd->multiplier == multiplier))
  296. return 0;
  297. debug(D_RRD_CALLS, "Updating multiplier of dimension '%s/%s' from " COLLECTED_NUMBER_FORMAT " to " COLLECTED_NUMBER_FORMAT, rrdset_id(st), rrddim_name(rd), rd->multiplier, multiplier);
  298. rd->multiplier = multiplier;
  299. rd->exposed = 0;
  300. rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED);
  301. rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK);
  302. rrdcontext_updated_rrddim_multiplier(rd);
  303. return 1;
  304. }
  305. inline int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, collected_number divisor) {
  306. if(unlikely(rd->divisor == divisor))
  307. return 0;
  308. debug(D_RRD_CALLS, "Updating divisor of dimension '%s/%s' from " COLLECTED_NUMBER_FORMAT " to " COLLECTED_NUMBER_FORMAT, rrdset_id(st), rrddim_name(rd), rd->divisor, divisor);
  309. rd->divisor = divisor;
  310. rd->exposed = 0;
  311. rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED);
  312. rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK);
  313. rrdcontext_updated_rrddim_divisor(rd);
  314. return 1;
  315. }
  316. // ----------------------------------------------------------------------------
  317. time_t rrddim_last_entry_s_of_tier(RRDDIM *rd, size_t tier) {
  318. if(unlikely(tier > storage_tiers || !rd->tiers[tier].db_metric_handle))
  319. return 0;
  320. return rd->tiers[tier].query_ops->latest_time_s(rd->tiers[tier].db_metric_handle);
  321. }
  322. // get the timestamp of the last entry in the round-robin database
  323. time_t rrddim_last_entry_s(RRDDIM *rd) {
  324. time_t latest_time_s = rrddim_last_entry_s_of_tier(rd, 0);
  325. for(size_t tier = 1; tier < storage_tiers ;tier++) {
  326. if(unlikely(!rd->tiers[tier].db_metric_handle)) continue;
  327. time_t t = rrddim_last_entry_s_of_tier(rd, tier);
  328. if(t > latest_time_s)
  329. latest_time_s = t;
  330. }
  331. return latest_time_s;
  332. }
  333. time_t rrddim_first_entry_s_of_tier(RRDDIM *rd, size_t tier) {
  334. if(unlikely(tier > storage_tiers || !rd->tiers[tier].db_metric_handle))
  335. return 0;
  336. return rd->tiers[tier].query_ops->oldest_time_s(rd->tiers[tier].db_metric_handle);
  337. }
  338. time_t rrddim_first_entry_s(RRDDIM *rd) {
  339. time_t oldest_time_s = 0;
  340. for(size_t tier = 0; tier < storage_tiers ;tier++) {
  341. time_t t = rrddim_first_entry_s_of_tier(rd, tier);
  342. if(t != 0 && (oldest_time_s == 0 || t < oldest_time_s))
  343. oldest_time_s = t;
  344. }
  345. return oldest_time_s;
  346. }
  347. RRDDIM *rrddim_add_custom(RRDSET *st
  348. , const char *id
  349. , const char *name
  350. , collected_number multiplier
  351. , collected_number divisor
  352. , RRD_ALGORITHM algorithm
  353. , RRD_MEMORY_MODE memory_mode
  354. ) {
  355. struct rrddim_constructor tmp = {
  356. .st = st,
  357. .id = id,
  358. .name = name,
  359. .multiplier = multiplier,
  360. .divisor = divisor,
  361. .algorithm = algorithm,
  362. .memory_mode = memory_mode,
  363. };
  364. RRDDIM *rd = dictionary_set_advanced(st->rrddim_root_index, tmp.id, -1, NULL, sizeof(RRDDIM), &tmp);
  365. return(rd);
  366. }
  367. // ----------------------------------------------------------------------------
  368. // RRDDIM remove / free a dimension
  369. void rrddim_free(RRDSET *st, RRDDIM *rd) {
  370. dictionary_del(st->rrddim_root_index, string2str(rd->id));
  371. }
  372. // ----------------------------------------------------------------------------
  373. // RRDDIM - set dimension options
  374. int rrddim_hide(RRDSET *st, const char *id) {
  375. debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", rrdset_name(st), id);
  376. RRDHOST *host = st->rrdhost;
  377. RRDDIM *rd = rrddim_find(st, id);
  378. if(unlikely(!rd)) {
  379. error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host));
  380. return 1;
  381. }
  382. if (!rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) {
  383. rrddim_flag_set(rd, RRDDIM_FLAG_META_HIDDEN | RRDDIM_FLAG_METADATA_UPDATE);
  384. rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
  385. }
  386. rrddim_option_set(rd, RRDDIM_OPTION_HIDDEN);
  387. rrdcontext_updated_rrddim_flags(rd);
  388. return 0;
  389. }
  390. int rrddim_unhide(RRDSET *st, const char *id) {
  391. debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", rrdset_name(st), id);
  392. RRDHOST *host = st->rrdhost;
  393. RRDDIM *rd = rrddim_find(st, id);
  394. if(unlikely(!rd)) {
  395. error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host));
  396. return 1;
  397. }
  398. if (rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) {
  399. rrddim_flag_clear(rd, RRDDIM_FLAG_META_HIDDEN);
  400. rrddim_flag_set(rd, RRDDIM_FLAG_METADATA_UPDATE);
  401. rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
  402. }
  403. rrddim_option_clear(rd, RRDDIM_OPTION_HIDDEN);
  404. rrdcontext_updated_rrddim_flags(rd);
  405. return 0;
  406. }
  407. inline void rrddim_is_obsolete(RRDSET *st, RRDDIM *rd) {
  408. debug(D_RRD_CALLS, "rrddim_is_obsolete() for chart %s, dimension %s", rrdset_name(st), rrddim_name(rd));
  409. if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED))) {
  410. info("Cannot obsolete already archived dimension %s from chart %s", rrddim_name(rd), rrdset_name(st));
  411. return;
  412. }
  413. rrddim_flag_set(rd, RRDDIM_FLAG_OBSOLETE);
  414. rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS);
  415. rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_PENDING_OBSOLETE_DIMENSIONS);
  416. rrdcontext_updated_rrddim_flags(rd);
  417. }
  418. inline void rrddim_isnot_obsolete(RRDSET *st __maybe_unused, RRDDIM *rd) {
  419. debug(D_RRD_CALLS, "rrddim_isnot_obsolete() for chart %s, dimension %s", rrdset_name(st), rrddim_name(rd));
  420. rrddim_flag_clear(rd, RRDDIM_FLAG_OBSOLETE);
  421. rrdcontext_updated_rrddim_flags(rd);
  422. }
  423. // ----------------------------------------------------------------------------
  424. // RRDDIM - collect values for a dimension
  425. inline collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value) {
  426. struct timeval now;
  427. now_realtime_timeval(&now);
  428. return rrddim_timed_set_by_pointer(st, rd, now, value);
  429. }
  430. collected_number rrddim_timed_set_by_pointer(RRDSET *st __maybe_unused, RRDDIM *rd, struct timeval collected_time, collected_number value) {
  431. debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, rrdset_name(st), rrddim_name(rd), value);
  432. rd->last_collected_time = collected_time;
  433. rd->collected_value = value;
  434. rd->updated = 1;
  435. rd->collections_counter++;
  436. collected_number v = (value >= 0) ? value : -value;
  437. if (unlikely(v > rd->collected_value_max))
  438. rd->collected_value_max = v;
  439. return rd->last_collected_value;
  440. }
  441. collected_number rrddim_set(RRDSET *st, const char *id, collected_number value) {
  442. RRDHOST *host = st->rrdhost;
  443. RRDDIM *rd = rrddim_find(st, id);
  444. if(unlikely(!rd)) {
  445. error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host));
  446. return 0;
  447. }
  448. return rrddim_set_by_pointer(st, rd, value);
  449. }
  450. // ----------------------------------------------------------------------------
  451. // compatibility layer for RRDDIM files v019
  452. #define RRDDIMENSION_MAGIC_V019 "NETDATA RRD DIMENSION FILE V019"
  453. struct avl_element_v019 {
  454. void *avl_link[2];
  455. signed char avl_balance;
  456. };
  457. struct rrddim_map_save_v019 {
  458. struct avl_element_v019 avl; // ignored
  459. void *id; // ignored
  460. void *name; // ignored
  461. uint32_t algorithm; // print warning on mismatch - update on load
  462. uint32_t rrd_memory_mode; // ignored
  463. long long multiplier; // print warning on mismatch - update on load
  464. long long divisor; // print warning on mismatch - update on load
  465. uint32_t flags; // ignored
  466. uint32_t hash; // ignored
  467. uint32_t hash_name; // ignored
  468. void *cache_filename; // ignored - we use it to keep the filename to save back
  469. size_t collections_counter; // ignored
  470. void *state; // ignored
  471. size_t unused[8]; // ignored
  472. long long collected_value_max; // ignored
  473. unsigned int updated:1; // ignored
  474. unsigned int exposed:1; // ignored
  475. struct timeval last_collected_time; // check to reset all - ignored after load
  476. long double calculated_value; // ignored
  477. long double last_calculated_value; // ignored
  478. long double last_stored_value; // ignored
  479. long long collected_value; // ignored
  480. long long last_collected_value; // load and save
  481. long double collected_volume; // ignored
  482. long double stored_volume; // ignored
  483. void *next; // ignored
  484. void *rrdset; // ignored
  485. long entries; // check to reset all - update on load
  486. int update_every; // check to reset all - update on load
  487. size_t memsize; // check to reset all - update on load
  488. char magic[sizeof(RRDDIMENSION_MAGIC_V019) + 1];// check to reset all - update on load
  489. void *variables; // ignored
  490. storage_number values[]; // the array of values
  491. };
  492. size_t rrddim_memory_file_header_size(void) {
  493. return sizeof(struct rrddim_map_save_v019);
  494. }
  495. void rrddim_memory_file_update(RRDDIM *rd) {
  496. if(!rd || !rd->rd_on_file) return;
  497. struct rrddim_map_save_v019 *rd_on_file = rd->rd_on_file;
  498. rd_on_file->last_collected_time.tv_sec = rd->last_collected_time.tv_sec;
  499. rd_on_file->last_collected_time.tv_usec = rd->last_collected_time.tv_usec;
  500. rd_on_file->last_collected_value = rd->last_collected_value;
  501. }
  502. void rrddim_memory_file_free(RRDDIM *rd) {
  503. if(!rd || !rd->rd_on_file) return;
  504. // needed for memory mode map, to save the latest state
  505. rrddim_memory_file_update(rd);
  506. struct rrddim_map_save_v019 *rd_on_file = rd->rd_on_file;
  507. __atomic_sub_fetch(&rrddim_db_memory_size, rd_on_file->memsize + strlen(rd_on_file->cache_filename), __ATOMIC_RELAXED);
  508. freez(rd_on_file->cache_filename);
  509. netdata_munmap(rd_on_file, rd_on_file->memsize);
  510. // remove the pointers from the RRDDIM
  511. rd->rd_on_file = NULL;
  512. rd->db = NULL;
  513. }
  514. const char *rrddim_cache_filename(RRDDIM *rd) {
  515. if(!rd || !rd->rd_on_file) return NULL;
  516. struct rrddim_map_save_v019 *rd_on_file = rd->rd_on_file;
  517. return rd_on_file->cache_filename;
  518. }
  519. void rrddim_memory_file_save(RRDDIM *rd) {
  520. if(!rd || !rd->rd_on_file) return;
  521. rrddim_memory_file_update(rd);
  522. struct rrddim_map_save_v019 *rd_on_file = rd->rd_on_file;
  523. if(rd_on_file->rrd_memory_mode != RRD_MEMORY_MODE_SAVE) return;
  524. memory_file_save(rd_on_file->cache_filename, rd_on_file, rd_on_file->memsize);
  525. }
  526. bool rrddim_memory_load_or_create_map_save(RRDSET *st, RRDDIM *rd, RRD_MEMORY_MODE memory_mode) {
  527. if(memory_mode != RRD_MEMORY_MODE_SAVE && memory_mode != RRD_MEMORY_MODE_MAP)
  528. return false;
  529. struct rrddim_map_save_v019 *rd_on_file = NULL;
  530. unsigned long size = sizeof(struct rrddim_map_save_v019) + (st->entries * sizeof(storage_number));
  531. char filename[FILENAME_MAX + 1];
  532. char fullfilename[FILENAME_MAX + 1];
  533. rrdset_strncpyz_name(filename, rrddim_id(rd), FILENAME_MAX);
  534. snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", rrdset_cache_dir(st), filename);
  535. rd_on_file = (struct rrddim_map_save_v019 *)netdata_mmap(
  536. fullfilename, size, ((memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 1, false, NULL);
  537. if(unlikely(!rd_on_file)) return false;
  538. struct timeval now;
  539. now_realtime_timeval(&now);
  540. int reset = 0;
  541. rd_on_file->magic[sizeof(RRDDIMENSION_MAGIC_V019)] = '\0';
  542. if(strcmp(rd_on_file->magic, RRDDIMENSION_MAGIC_V019) != 0) {
  543. info("Initializing file %s.", fullfilename);
  544. memset(rd_on_file, 0, size);
  545. reset = 1;
  546. }
  547. else if(rd_on_file->memsize != size) {
  548. error("File %s does not have the desired size, expected %lu but found %lu. Clearing it.", fullfilename, size, (unsigned long int) rd_on_file->memsize);
  549. memset(rd_on_file, 0, size);
  550. reset = 1;
  551. }
  552. else if(rd_on_file->update_every != st->update_every) {
  553. error("File %s does not have the same update frequency, expected %d but found %d. Clearing it.", fullfilename, st->update_every, rd_on_file->update_every);
  554. memset(rd_on_file, 0, size);
  555. reset = 1;
  556. }
  557. else if(dt_usec(&now, &rd_on_file->last_collected_time) > (rd_on_file->entries * rd_on_file->update_every * USEC_PER_SEC)) {
  558. info("File %s is too old (last collected %llu seconds ago, but the database is %ld seconds). Clearing it.", fullfilename, dt_usec(&now, &rd_on_file->last_collected_time) / USEC_PER_SEC, rd_on_file->entries * rd_on_file->update_every);
  559. memset(rd_on_file, 0, size);
  560. reset = 1;
  561. }
  562. if(!reset) {
  563. rd->last_collected_value = rd_on_file->last_collected_value;
  564. if(rd_on_file->algorithm != rd->algorithm)
  565. info("File %s does not have the expected algorithm (expected %u '%s', found %u '%s'). Previous values may be wrong.",
  566. fullfilename, rd->algorithm, rrd_algorithm_name(rd->algorithm), rd_on_file->algorithm, rrd_algorithm_name(rd_on_file->algorithm));
  567. if(rd_on_file->multiplier != rd->multiplier)
  568. info("File %s does not have the expected multiplier (expected " COLLECTED_NUMBER_FORMAT ", found " COLLECTED_NUMBER_FORMAT "). Previous values may be wrong.", fullfilename, rd->multiplier, rd_on_file->multiplier);
  569. if(rd_on_file->divisor != rd->divisor)
  570. info("File %s does not have the expected divisor (expected " COLLECTED_NUMBER_FORMAT ", found " COLLECTED_NUMBER_FORMAT "). Previous values may be wrong.", fullfilename, rd->divisor, rd_on_file->divisor);
  571. }
  572. // zero the entire header
  573. memset(rd_on_file, 0, sizeof(struct rrddim_map_save_v019));
  574. // set the important fields
  575. strcpy(rd_on_file->magic, RRDDIMENSION_MAGIC_V019);
  576. rd_on_file->algorithm = rd->algorithm;
  577. rd_on_file->multiplier = rd->multiplier;
  578. rd_on_file->divisor = rd->divisor;
  579. rd_on_file->entries = st->entries;
  580. rd_on_file->update_every = rd->update_every;
  581. rd_on_file->memsize = size;
  582. rd_on_file->rrd_memory_mode = memory_mode;
  583. rd_on_file->cache_filename = strdupz(fullfilename);
  584. __atomic_add_fetch(&rrddim_db_memory_size, rd_on_file->memsize + strlen(rd_on_file->cache_filename), __ATOMIC_RELAXED);
  585. rd->db = &rd_on_file->values[0];
  586. rd->rd_on_file = rd_on_file;
  587. rd->memsize = size;
  588. rrddim_memory_file_update(rd);
  589. return true;
  590. }