metric.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "internal.h"
  3. static void rrdmetric_trigger_updates(RRDMETRIC *rm, const char *function);
  4. inline const char *rrdmetric_acquired_id(RRDMETRIC_ACQUIRED *rma) {
  5. RRDMETRIC *rm = rrdmetric_acquired_value(rma);
  6. return string2str(rm->id);
  7. }
  8. inline const char *rrdmetric_acquired_name(RRDMETRIC_ACQUIRED *rma) {
  9. RRDMETRIC *rm = rrdmetric_acquired_value(rma);
  10. return string2str(rm->name);
  11. }
  12. inline bool rrdmetric_acquired_has_name(RRDMETRIC_ACQUIRED *rma) {
  13. RRDMETRIC *rm = rrdmetric_acquired_value(rma);
  14. return (rm->name && rm->name != rm->id);
  15. }
  16. inline STRING *rrdmetric_acquired_id_dup(RRDMETRIC_ACQUIRED *rma) {
  17. RRDMETRIC *rm = rrdmetric_acquired_value(rma);
  18. return string_dup(rm->id);
  19. }
  20. inline STRING *rrdmetric_acquired_name_dup(RRDMETRIC_ACQUIRED *rma) {
  21. RRDMETRIC *rm = rrdmetric_acquired_value(rma);
  22. return string_dup(rm->name);
  23. }
  24. inline NETDATA_DOUBLE rrdmetric_acquired_last_stored_value(RRDMETRIC_ACQUIRED *rma) {
  25. RRDMETRIC *rm = rrdmetric_acquired_value(rma);
  26. if(rm->rrddim)
  27. return rm->rrddim->last_stored_value;
  28. return NAN;
  29. }
  30. inline bool rrdmetric_acquired_belongs_to_instance(RRDMETRIC_ACQUIRED *rma, RRDINSTANCE_ACQUIRED *ria) {
  31. RRDMETRIC *rm = rrdmetric_acquired_value(rma);
  32. RRDINSTANCE *ri = rrdinstance_acquired_value(ria);
  33. return rm->ri == ri;
  34. }
  35. inline time_t rrdmetric_acquired_first_entry(RRDMETRIC_ACQUIRED *rma) {
  36. RRDMETRIC *rm = rrdmetric_acquired_value(rma);
  37. return rm->first_time_s;
  38. }
  39. inline time_t rrdmetric_acquired_last_entry(RRDMETRIC_ACQUIRED *rma) {
  40. RRDMETRIC *rm = rrdmetric_acquired_value(rma);
  41. if(rrd_flag_check(rm, RRD_FLAG_COLLECTED))
  42. return 0;
  43. return rm->last_time_s;
  44. }
  45. // ----------------------------------------------------------------------------
  46. // RRDMETRIC
  47. // free the contents of RRDMETRIC.
  48. // RRDMETRIC itself is managed by DICTIONARY - no need to free it here.
  49. static void rrdmetric_free(RRDMETRIC *rm) {
  50. string_freez(rm->id);
  51. string_freez(rm->name);
  52. rm->id = NULL;
  53. rm->name = NULL;
  54. rm->ri = NULL;
  55. }
  56. // called when this rrdmetric is inserted to the rrdmetrics dictionary of a rrdinstance
  57. // the constructor of the rrdmetric object
  58. static void rrdmetric_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance) {
  59. RRDMETRIC *rm = value;
  60. // link it to its parent
  61. rm->ri = rrdinstance;
  62. // remove flags that we need to figure out at runtime
  63. rm->flags = rm->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics
  64. // signal the react callback to do the job
  65. rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_NEW_OBJECT);
  66. }
  67. // called when this rrdmetric is deleted from the rrdmetrics dictionary of a rrdinstance
  68. // the destructor of the rrdmetric object
  69. static void rrdmetric_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance __maybe_unused) {
  70. RRDMETRIC *rm = value;
  71. internal_error(rm->rrddim, "RRDMETRIC: '%s' is freed but there is a RRDDIM linked to it.", string2str(rm->id));
  72. // free the resources
  73. rrdmetric_free(rm);
  74. }
  75. // called when the same rrdmetric is inserted again to the rrdmetrics dictionary of a rrdinstance
  76. // while this is called, the dictionary is write locked, but there may be other users of the object
  77. static bool rrdmetric_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *rrdinstance __maybe_unused) {
  78. RRDMETRIC *rm = old_value;
  79. RRDMETRIC *rm_new = new_value;
  80. internal_error(rm->id != rm_new->id,
  81. "RRDMETRIC: '%s' cannot change id to '%s'",
  82. string2str(rm->id), string2str(rm_new->id));
  83. if(uuid_memcmp(&rm->uuid, &rm_new->uuid) != 0) {
  84. #ifdef NETDATA_INTERNAL_CHECKS
  85. char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
  86. uuid_unparse(rm->uuid, uuid1);
  87. uuid_unparse(rm_new->uuid, uuid2);
  88. time_t old_first_time_s = 0;
  89. time_t old_last_time_s = 0;
  90. if(rrdmetric_update_retention(rm)) {
  91. old_first_time_s = rm->first_time_s;
  92. old_last_time_s = rm->last_time_s;
  93. }
  94. uuid_copy(rm->uuid, rm_new->uuid);
  95. time_t new_first_time_s = 0;
  96. time_t new_last_time_s = 0;
  97. if(rrdmetric_update_retention(rm)) {
  98. new_first_time_s = rm->first_time_s;
  99. new_last_time_s = rm->last_time_s;
  100. }
  101. internal_error(true,
  102. "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)"
  103. , string2str(rm->id)
  104. , string2str(rm->ri->id)
  105. , rrdhost_hostname(rm->ri->rc->rrdhost)
  106. , uuid1, old_first_time_s, old_last_time_s, old_last_time_s - old_first_time_s
  107. , uuid2, new_first_time_s, new_last_time_s, new_last_time_s - new_first_time_s
  108. );
  109. #else
  110. uuid_copy(rm->uuid, rm_new->uuid);
  111. #endif
  112. rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
  113. }
  114. if(rm->rrddim && rm_new->rrddim && rm->rrddim != rm_new->rrddim) {
  115. rm->rrddim = rm_new->rrddim;
  116. rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING);
  117. }
  118. #ifdef NETDATA_INTERNAL_CHECKS
  119. if(rm->rrddim && uuid_memcmp(&rm->uuid, &rm->rrddim->metric_uuid) != 0) {
  120. char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN];
  121. uuid_unparse(rm->uuid, uuid1);
  122. uuid_unparse(rm_new->uuid, uuid2);
  123. internal_error(true, "RRDMETRIC: '%s' is linked to RRDDIM '%s' but they have different UUIDs. RRDMETRIC has '%s', RRDDIM has '%s'", string2str(rm->id), rrddim_id(rm->rrddim), uuid1, uuid2);
  124. }
  125. #endif
  126. if(rm->rrddim != rm_new->rrddim)
  127. rm->rrddim = rm_new->rrddim;
  128. if(rm->name != rm_new->name) {
  129. STRING *old = rm->name;
  130. rm->name = string_dup(rm_new->name);
  131. string_freez(old);
  132. rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA);
  133. }
  134. if(!rm->first_time_s || (rm_new->first_time_s && rm_new->first_time_s < rm->first_time_s)) {
  135. rm->first_time_s = rm_new->first_time_s;
  136. rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T);
  137. }
  138. if(!rm->last_time_s || (rm_new->last_time_s && rm_new->last_time_s > rm->last_time_s)) {
  139. rm->last_time_s = rm_new->last_time_s;
  140. rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T);
  141. }
  142. rrd_flag_set(rm, rm_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no needs for atomics on rm_new
  143. if(rrd_flag_is_collected(rm) && rrd_flag_is_archived(rm))
  144. rrd_flag_set_collected(rm);
  145. if(rrd_flag_check(rm, RRD_FLAG_UPDATED))
  146. rrd_flag_set(rm, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT);
  147. rrdmetric_free(rm_new);
  148. // the react callback will continue from here
  149. return rrd_flag_is_updated(rm);
  150. }
  151. // this is called after the insert or the conflict callbacks,
  152. // but the dictionary is now unlocked
  153. static void rrdmetric_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance __maybe_unused) {
  154. RRDMETRIC *rm = value;
  155. rrdmetric_trigger_updates(rm, __FUNCTION__ );
  156. }
  157. void rrdmetrics_create_in_rrdinstance(RRDINSTANCE *ri) {
  158. if(unlikely(!ri)) return;
  159. if(likely(ri->rrdmetrics)) return;
  160. ri->rrdmetrics = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
  161. &dictionary_stats_category_rrdcontext, sizeof(RRDMETRIC));
  162. dictionary_register_insert_callback(ri->rrdmetrics, rrdmetric_insert_callback, ri);
  163. dictionary_register_delete_callback(ri->rrdmetrics, rrdmetric_delete_callback, ri);
  164. dictionary_register_conflict_callback(ri->rrdmetrics, rrdmetric_conflict_callback, ri);
  165. dictionary_register_react_callback(ri->rrdmetrics, rrdmetric_react_callback, ri);
  166. }
  167. void rrdmetrics_destroy_from_rrdinstance(RRDINSTANCE *ri) {
  168. if(unlikely(!ri || !ri->rrdmetrics)) return;
  169. dictionary_destroy(ri->rrdmetrics);
  170. ri->rrdmetrics = NULL;
  171. }
  172. // trigger post-processing of the rrdmetric, escalating changes to the rrdinstance it belongs
  173. static void rrdmetric_trigger_updates(RRDMETRIC *rm, const char *function) {
  174. if(unlikely(rrd_flag_is_collected(rm)) && (!rm->rrddim || rrd_flag_check(rm, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD)))
  175. rrd_flag_set_archived(rm);
  176. if(rrd_flag_is_updated(rm) || !rrd_flag_check(rm, RRD_FLAG_LIVE_RETENTION)) {
  177. rrd_flag_set_updated(rm->ri, RRD_FLAG_UPDATE_REASON_TRIGGERED);
  178. rrdcontext_queue_for_post_processing(rm->ri->rc, function, rm->flags);
  179. }
  180. }
  181. // ----------------------------------------------------------------------------
  182. // RRDMETRIC HOOKS ON RRDDIM
  183. void rrdmetric_from_rrddim(RRDDIM *rd) {
  184. if(unlikely(!rd->rrdset))
  185. fatal("RRDMETRIC: rrddim '%s' does not have a rrdset.", rrddim_id(rd));
  186. if(unlikely(!rd->rrdset->rrdhost))
  187. fatal("RRDMETRIC: rrdset '%s' does not have a rrdhost", rrdset_id(rd->rrdset));
  188. if(unlikely(!rd->rrdset->rrdinstance))
  189. fatal("RRDMETRIC: rrdset '%s' does not have a rrdinstance", rrdset_id(rd->rrdset));
  190. RRDINSTANCE *ri = rrdinstance_acquired_value(rd->rrdset->rrdinstance);
  191. RRDMETRIC trm = {
  192. .id = string_dup(rd->id),
  193. .name = string_dup(rd->name),
  194. .flags = RRD_FLAG_NONE, // no need for atomics
  195. .rrddim = rd,
  196. };
  197. uuid_copy(trm.uuid, rd->metric_uuid);
  198. RRDMETRIC_ACQUIRED *rma = (RRDMETRIC_ACQUIRED *)dictionary_set_and_acquire_item(ri->rrdmetrics, string2str(trm.id), &trm, sizeof(trm));
  199. if(rd->rrdmetric)
  200. rrdmetric_release(rd->rrdmetric);
  201. rd->rrdmetric = rma;
  202. }
  203. #define rrddim_get_rrdmetric(rd) rrddim_get_rrdmetric_with_trace(rd, __FUNCTION__)
  204. static inline RRDMETRIC *rrddim_get_rrdmetric_with_trace(RRDDIM *rd, const char *function) {
  205. if(unlikely(!rd->rrdmetric)) {
  206. error("RRDMETRIC: RRDDIM '%s' is not linked to an RRDMETRIC at %s()", rrddim_id(rd), function);
  207. return NULL;
  208. }
  209. RRDMETRIC *rm = rrdmetric_acquired_value(rd->rrdmetric);
  210. if(unlikely(!rm)) {
  211. error("RRDMETRIC: RRDDIM '%s' lost the link to its RRDMETRIC at %s()", rrddim_id(rd), function);
  212. return NULL;
  213. }
  214. if(unlikely(rm->rrddim != rd))
  215. fatal("RRDMETRIC: '%s' is not linked to RRDDIM '%s' at %s()", string2str(rm->id), rrddim_id(rd), function);
  216. return rm;
  217. }
  218. inline void rrdmetric_rrddim_is_freed(RRDDIM *rd) {
  219. RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
  220. if(unlikely(!rm)) return;
  221. if(unlikely(rrd_flag_is_collected(rm)))
  222. rrd_flag_set_archived(rm);
  223. rm->rrddim = NULL;
  224. rrdmetric_trigger_updates(rm, __FUNCTION__ );
  225. rrdmetric_release(rd->rrdmetric);
  226. rd->rrdmetric = NULL;
  227. }
  228. inline void rrdmetric_updated_rrddim_flags(RRDDIM *rd) {
  229. RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
  230. if(unlikely(!rm)) return;
  231. if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED|RRDDIM_FLAG_OBSOLETE))) {
  232. if(unlikely(rrd_flag_is_collected(rm)))
  233. rrd_flag_set_archived(rm);
  234. }
  235. rrdmetric_trigger_updates(rm, __FUNCTION__ );
  236. }
  237. inline void rrdmetric_collected_rrddim(RRDDIM *rd) {
  238. RRDMETRIC *rm = rrddim_get_rrdmetric(rd);
  239. if(unlikely(!rm)) return;
  240. if(unlikely(!rrd_flag_is_collected(rm)))
  241. rrd_flag_set_collected(rm);
  242. // we use this variable to detect BEGIN/END without SET
  243. rm->ri->internal.collected_metrics_count++;
  244. rrdmetric_trigger_updates(rm, __FUNCTION__ );
  245. }