@cryptotaxi247 / netdata-1 / commits / 8d3c3356d

Streaming interpolated values (#14431)

* first commit - untested * fix wrong begin command * added set v2 too * debug to log stream buffer * debug to log stream buffer * faster streaming printing * mark charts and dimensions as collected * use stream points even if sender is not enabled * comment out stream debug log * parse null as nan * custom begin v2 * custom set v2; replication now copies the anomalous flag too * custom end v2 * enabled stream log test * renamed to BEGIN2, SET2, END2 * dont mix up replay and v2 members in user object * fix typo * cleanup * support to v2 to v1 proxying * mark updated dimensions as such * do not log unknown flags * comment out stream debug log * send also the chart id on BEGIN2, v2 to v2 * update the data collections counter * v2 values are transferred in hex * faster hex parsing * a little more generic hex and dec printing and parsing * fix hex parsing * minor optimization in dbengine api * turn debugging into info message * generalized the timings tracking, so that it can be used in more places * commented out debug info * renamed conflicting variable with macro * remove wrong edits * integrated ML and added cleanup in case parsing is interrupted * disable data collection locking during v2 * cleanup stale ML locks; send updated chart variables during v2; add info to find stale locks * inject an END2 between repeated BEGIN2 from rrdset_done() * test: remove lockless single-threaded logic from dictionary and aral and apply the right acquire/release memory order to reference counters * more fine grained dictionary atomics * remove unecessary return values * pointer validation under NETDATA_DICTIONARY_VALIDATE_POINTERS * Revert "pointer validation under NETDATA_DICTIONARY_VALIDATE_POINTERS" This reverts commit 846cdf2713e2a7ee2ff797f38db11714228800e9. * Revert "remove unecessary return values" This reverts commit 8c87d30f4d86f0f5d6b4562cf74fe7447138bbff. * Revert "more fine grained dictionary atomics" This reverts commit 984aec4234a340d197d45239ff9a10fd479fcf3c. * Revert "test: remove lockless single-threaded logic from dictionary and aral and apply the right acquire/release memory order to reference counters" This reverts commit c460b3d0ad497d2641bd0ea1d63cec7c052e74e4. * Apply again "pointer validation under NETDATA_DICTIONARY_VALIDATE_POINTERS" while keeping the improved atomic operations. This reverts commit f158d009 * fix last commit * fix last commit again * optimizations in dbengine * do not send anomaly bit on non-supporting agents (send it when the INTERPOLATED capability is available) * break long empty-points-loops in rrdset_done() * decide page alignment on new page allocation, not on every point collected * create max size pages but no smaller than 1/3 * Fix compilation when --disable-ml is specified * Return false * fixes for NETDATA_LOG_REPLICATION_REQUESTS * added compile option NETDATA_WITHOUT_WORKERS_LATENCY * put timings in BEGIN2, SET2, END2 * isolate begin2 ml * revert repositioning data collection lock * fixed multi-threading of statistics * do not lookup dimensions all the time if they come in the same order * update used on iteration, not on every points; also do better error handling --------- Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>

Costa Tsaousis committed Feb 7, 2023 at 22:26 UTC 8d3c3356ddeb6d62fa76b197e086e3e7fc5eb3dd
26 files changed +1305 -528
collectors/plugins.d/plugins_d.h
+4
@@ -34,6 +34,10 @@
34 #define PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE "RSSTATE"
35 #define PLUGINSD_KEYWORD_REPLAY_END "REND"
36
37 +#define PLUGINSD_KEYWORD_BEGIN_V2 "BEGIN2"
38 +#define PLUGINSD_KEYWORD_SET_V2 "SET2"
39 +#define PLUGINSD_KEYWORD_END_V2 "END2"
40 +
41 #define PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT 10 // seconds
42
43 #define PLUGINSD_LINE_MAX_SSL_READ 512
collectors/plugins.d/pluginsd_parser.c
+499 -139
@@ -71,20 +71,109 @@ static inline RRDSET *pluginsd_require_chart_from_parent(void *user, const char
71 return st;
72 }
73
74 -static inline RRDDIM_ACQUIRED *pluginsd_acquire_dimension(RRDHOST *host, RRDSET *st, const char *dimension, const char *cmd) {
74 +static inline RRDSET *pluginsd_get_chart_from_parent(void *user) {
75 + return ((PARSER_USER_OBJECT *) user)->st;
76 +}
77 +
78 +static inline void pluginsd_lock_rrdset_data_collection(void *user) {
79 + PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
80 + if(u->st && !u->v2.locked_data_collection) {
81 + netdata_spinlock_lock(&u->st->data_collection_lock);
82 + u->v2.locked_data_collection = true;
83 + }
84 +}
85 +
86 +static inline bool pluginsd_unlock_rrdset_data_collection(void *user) {
87 + PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
88 + if(u->st && u->v2.locked_data_collection) {
89 + netdata_spinlock_unlock(&u->st->data_collection_lock);
90 + u->v2.locked_data_collection = false;
91 + return true;
92 + }
93 +
94 + return false;
95 +}
96 +
97 +void pluginsd_rrdset_cleanup(RRDSET *st) {
98 + for(size_t i = 0; i < st->pluginsd.used ; i++) {
99 + if (st->pluginsd.rda[i]) {
100 + rrddim_acquired_release(st->pluginsd.rda[i]);
101 + st->pluginsd.rda[i] = NULL;
102 + }
103 + }
104 + freez(st->pluginsd.rda);
105 + st->pluginsd.rda = NULL;
106 + st->pluginsd.size = 0;
107 + st->pluginsd.used = 0;
108 + st->pluginsd.pos = 0;
109 +}
110 +
111 +static inline void pluginsd_set_chart_from_parent(void *user, RRDSET *st, const char *keyword) {
112 + PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
113 +
114 + if(unlikely(pluginsd_unlock_rrdset_data_collection(user))) {
115 + error("PLUGINSD: 'host:%s/chart:%s/' stale data collection lock found during %s; it has been unlocked",
116 + rrdhost_hostname(u->st->rrdhost), rrdset_id(u->st), keyword);
117 + }
118 +
119 + if(unlikely(u->v2.ml_locked)) {
120 + ml_chart_update_end(u->st);
121 + u->v2.ml_locked = false;
122 +
123 + error("PLUGINSD: 'host:%s/chart:%s/' stale ML lock found during %s, it has been unlocked",
124 + rrdhost_hostname(u->st->rrdhost), rrdset_id(u->st), keyword);
125 + }
126 +
127 + if(st) {
128 + size_t dims = dictionary_entries(st->rrddim_root_index);
129 + if(unlikely(st->pluginsd.size < dims)) {
130 + st->pluginsd.rda = reallocz(st->pluginsd.rda, dims * sizeof(RRDDIM_ACQUIRED *));
131 + st->pluginsd.size = dims;
132 + }
133 +
134 + if(st->pluginsd.pos > st->pluginsd.used && st->pluginsd.pos <= st->pluginsd.size)
135 + st->pluginsd.used = st->pluginsd.pos;
136 +
137 + st->pluginsd.pos = 0;
138 + }
139 +
140 + u->st = st;
141 +}
142 +
143 +static inline RRDDIM *pluginsd_acquire_dimension(RRDHOST *host, RRDSET *st, const char *dimension, const char *cmd) {
144 if (unlikely(!dimension || !*dimension)) {
145 error("PLUGINSD: 'host:%s/chart:%s' got a %s, without a dimension.",
146 rrdhost_hostname(host), rrdset_id(st), cmd);
147 return NULL;
148 }
149
81 - RRDDIM_ACQUIRED *rda = rrddim_find_and_acquire(st, dimension);
150 + RRDDIM_ACQUIRED *rda;
151
83 - if (unlikely(!rda))
152 + if(likely(st->pluginsd.pos < st->pluginsd.used)) {
153 + rda = st->pluginsd.rda[st->pluginsd.pos];
154 + RRDDIM *rd = rrddim_acquired_to_rrddim(rda);
155 + if (likely(rd && strcmp(rrddim_id(rd), dimension) == 0)) {
156 + st->pluginsd.pos++;
157 + return rd;
158 + }
159 + else {
160 + rrddim_acquired_release(rda);
161 + st->pluginsd.rda[st->pluginsd.pos] = NULL;
162 + }
163 + }
164 +
165 + rda = rrddim_find_and_acquire(st, dimension);
166 + if (unlikely(!rda)) {
167 error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a %s but dimension does not exist.",
168 rrdhost_hostname(host), rrdset_id(st), dimension, cmd);
169
87 - return rda;
170 + return NULL;
171 + }
172 +
173 + if(likely(st->pluginsd.pos < st->pluginsd.size))
174 + st->pluginsd.rda[st->pluginsd.pos++] = rda;
175 +
176 + return rrddim_acquired_to_rrddim(rda);
177 }
178
179 static inline RRDSET *pluginsd_find_chart(RRDHOST *host, const char *chart, const char *cmd) {
@@ -102,8 +191,14 @@ static inline RRDSET *pluginsd_find_chart(RRDHOST *host, const char *chart, cons
191 return st;
192 }
193
105 -static inline PARSER_RC PLUGINSD_DISABLE_PLUGIN(void *user) {
194 +static inline PARSER_RC PLUGINSD_DISABLE_PLUGIN(void *user, const char *keyword, const char *msg) {
195 ((PARSER_USER_OBJECT *) user)->enabled = 0;
196 +
197 + if(keyword && msg) {
198 + error_limit_static_global_var(erl, 1, 0);
199 + error_limit(&erl, "PLUGINSD: keyword %s: %s", keyword, msg);
200 + }
201 +
202 return PARSER_RC_ERROR;
203 }
204
@@ -113,24 +208,21 @@ PARSER_RC pluginsd_set(char **words, size_t num_words, void *user)
208 char *value = get_word(words, num_words, 2);
209
210 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_SET);
116 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
211 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
212
213 RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_SET, PLUGINSD_KEYWORD_CHART);
119 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
214 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
215
121 - RRDDIM_ACQUIRED *rda = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_SET);
122 - if(!rda) return PLUGINSD_DISABLE_PLUGIN(user);
123 -
124 - RRDDIM *rd = rrddim_acquired_to_rrddim(rda);
216 + RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_SET);
217 + if(!rd) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
218
219 if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
220 debug(D_PLUGINSD, "PLUGINSD: 'host:%s/chart:%s/dim:%s' SET is setting value to '%s'",
221 rrdhost_hostname(host), rrdset_id(st), dimension, value && *value ? value : "UNSET");
222
223 if (value && *value)
131 - rrddim_set_by_pointer(st, rd, strtoll(value, NULL, 0));
224 + rrddim_set_by_pointer(st, rd, str2ll_hex_or_dec(value));
225
133 - rrddim_acquired_release(rda);
226 return PARSER_RC_OK;
227 }
228
@@ -140,12 +232,12 @@ PARSER_RC pluginsd_begin(char **words, size_t num_words, void *user)
232 char *microseconds_txt = get_word(words, num_words, 2);
233
234 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_BEGIN);
143 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
235 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
236
237 RRDSET *st = pluginsd_find_chart(host, id, PLUGINSD_KEYWORD_BEGIN);
146 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
238 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
239
148 - ((PARSER_USER_OBJECT *)user)->st = st;
240 + pluginsd_set_chart_from_parent(user, st, PLUGINSD_KEYWORD_BEGIN);
241
242 usec_t microseconds = 0;
243 if (microseconds_txt && *microseconds_txt) {
@@ -187,16 +279,16 @@ PARSER_RC pluginsd_end(char **words, size_t num_words, void *user)
279 UNUSED(num_words);
280
281 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_END);
190 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
282 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
283
284 RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_END, PLUGINSD_KEYWORD_BEGIN);
193 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
285 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
286
287 if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
288 debug(D_PLUGINSD, "requested an END on chart '%s'", rrdset_id(st));
289
198 - ((PARSER_USER_OBJECT *) user)->st = NULL;
199 - ((PARSER_USER_OBJECT *) user)->count++;
290 + pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_END);
291 + ((PARSER_USER_OBJECT *) user)->data_collections_count++;
292
293 struct timeval now;
294 now_realtime_timeval(&now);
@@ -208,7 +300,7 @@ PARSER_RC pluginsd_end(char **words, size_t num_words, void *user)
300 PARSER_RC pluginsd_chart(char **words, size_t num_words, void *user)
301 {
302 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CHART);
211 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
303 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
304
305 char *type = get_word(words, num_words, 1);
306 char *name = get_word(words, num_words, 2);
@@ -231,19 +323,14 @@ PARSER_RC pluginsd_chart(char **words, size_t num_words, void *user)
323 }
324
325 // make sure we have the required variables
234 - if (unlikely((!type || !*type || !id || !*id))) {
235 - error("PLUGINSD: 'host:%s' requested a CHART, without a type.id. Disabling it.",
236 - rrdhost_hostname(host));
237 -
238 - ((PARSER_USER_OBJECT *) user)->enabled = 0;
239 - return PARSER_RC_ERROR;
240 - }
326 + if (unlikely((!type || !*type || !id || !*id)))
327 + return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_CHART, "missing parameters");
328
329 // parse the name, and make sure it does not include 'type.'
330 if (unlikely(name && *name)) {
331 // when data are streamed from child nodes
332 // name will be type.name
246 - // so we have to remove 'type.' from name too
333 + // so, we have to remove 'type.' from name too
334 size_t len = strlen(type);
335 if (strncmp(type, name, len) == 0 && name[len] == '.')
336 name = &name[len + 1];
@@ -320,7 +407,7 @@ PARSER_RC pluginsd_chart(char **words, size_t num_words, void *user)
407 rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
408 }
409 }
323 - ((PARSER_USER_OBJECT *)user)->st = st;
410 + pluginsd_set_chart_from_parent(user, st, PLUGINSD_KEYWORD_CHART);
411
412 return PARSER_RC_OK;
413 }
@@ -332,10 +419,10 @@ PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_words, void *us
419 const char *wall_clock_time_txt = get_word(words, num_words, 3);
420
421 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CHART_DEFINITION_END);
335 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
422 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
423
424 RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_CHART_DEFINITION_END, PLUGINSD_KEYWORD_CHART);
338 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
425 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
426
427 time_t first_entry_child = (first_entry_txt && *first_entry_txt) ? (time_t)str2ul(first_entry_txt) : 0;
428 time_t last_entry_child = (last_entry_txt && *last_entry_txt) ? (time_t)str2ul(last_entry_txt) : 0;
@@ -379,33 +466,24 @@ PARSER_RC pluginsd_dimension(char **words, size_t num_words, void *user)
466 char *options = get_word(words, num_words, 6);
467
468 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_DIMENSION);
382 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
469 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
470
471 RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_DIMENSION, PLUGINSD_KEYWORD_CHART);
385 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
472 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
473
387 - if (unlikely(!id)) {
388 - error("PLUGINSD: 'host:%s/chart:%s' got a DIMENSION, without an id. Disabling it.",
389 - rrdhost_hostname(host), st ? rrdset_id(st) : "UNSET");
390 - return PLUGINSD_DISABLE_PLUGIN(user);
391 - }
392 -
393 - if (unlikely(!st && !((PARSER_USER_OBJECT *) user)->st_exists)) {
394 - error("PLUGINSD: 'host:%s' got a DIMENSION, without a CHART. Disabling it.",
395 - rrdhost_hostname(host));
396 - return PLUGINSD_DISABLE_PLUGIN(user);
397 - }
474 + if (unlikely(!id))
475 + return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_DIMENSION, "missing dimension id");
476
477 long multiplier = 1;
478 if (multiplier_s && *multiplier_s) {
401 - multiplier = strtol(multiplier_s, NULL, 0);
479 + multiplier = str2ll_hex_or_dec(multiplier_s);
480 if (unlikely(!multiplier))
481 multiplier = 1;
482 }
483
484 long divisor = 1;
485 if (likely(divisor_s && *divisor_s)) {
408 - divisor = strtol(divisor_s, NULL, 0);
486 + divisor = str2ll_hex_or_dec(divisor_s);
487 if (unlikely(!divisor))
488 divisor = 1;
489 }
@@ -712,9 +790,9 @@ PARSER_RC pluginsd_variable(char **words, size_t num_words, void *user)
790 NETDATA_DOUBLE v;
791
792 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_VARIABLE);
715 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
793 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
794
717 - RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
795 + RRDSET *st = pluginsd_get_chart_from_parent(user);
796
797 int global = (st) ? 0 : 1;
798
@@ -730,13 +808,8 @@ PARSER_RC pluginsd_variable(char **words, size_t num_words, void *user)
808 }
809 }
810
733 - if (unlikely(!name || !*name)) {
734 - error("PLUGINSD: 'host:%s/chart:%s' got a VARIABLE without a variable name. Disabling it.",
735 - rrdhost_hostname(host), st ? rrdset_id(st):"UNSET");
736 -
737 - ((PARSER_USER_OBJECT *)user)->enabled = 0;
738 - return PLUGINSD_DISABLE_PLUGIN(user);
739 - }
811 + if (unlikely(!name || !*name))
812 + return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_VARIABLE, "missing variable name");
813
814 if (unlikely(!value || !*value))
815 value = NULL;
@@ -750,14 +823,8 @@ PARSER_RC pluginsd_variable(char **words, size_t num_words, void *user)
823 return PARSER_RC_OK;
824 }
825
753 - if (!global && !st) {
754 - error("PLUGINSD: 'host:%s/chart:%s' cannot update CHART VARIABLE '%s' without a chart",
755 - rrdhost_hostname(host),
756 - st ? rrdset_id(st):"UNSET",
757 - name
758 - );
759 - return PLUGINSD_DISABLE_PLUGIN(user);
760 - }
826 + if (!global && !st)
827 + return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_VARIABLE, "no chart is defined and no GLOBAL is given");
828
829 char *endptr = NULL;
830 v = (NETDATA_DOUBLE)str2ndd(value, &endptr);
@@ -803,8 +870,8 @@ PARSER_RC pluginsd_variable(char **words, size_t num_words, void *user)
870
871 PARSER_RC pluginsd_flush(char **words __maybe_unused, size_t num_words __maybe_unused, void *user)
872 {
806 - debug(D_PLUGINSD, "requested a FLUSH");
807 - ((PARSER_USER_OBJECT *) user)->st = NULL;
873 + debug(D_PLUGINSD, "requested a " PLUGINSD_KEYWORD_FLUSH);
874 + pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_FLUSH);
875 ((PARSER_USER_OBJECT *) user)->replay.start_time = 0;
876 ((PARSER_USER_OBJECT *) user)->replay.end_time = 0;
877 ((PARSER_USER_OBJECT *) user)->replay.start_time_ut = 0;
@@ -825,10 +892,8 @@ PARSER_RC pluginsd_label(char **words, size_t num_words, void *user)
892 const char *label_source = get_word(words, num_words, 2);
893 const char *value = get_word(words, num_words, 3);
894
828 - if (!name || !label_source || !value) {
829 - error("PLUGINSD: ignoring malformed or empty LABEL command.");
830 - return PLUGINSD_DISABLE_PLUGIN(user);
831 - }
895 + if (!name || !label_source || !value)
896 + return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_LABEL, "missing parameters");
897
898 char *store = (char *)value;
899 bool allocated_store = false;
@@ -874,7 +939,7 @@ PARSER_RC pluginsd_label(char **words, size_t num_words, void *user)
939 PARSER_RC pluginsd_overwrite(char **words __maybe_unused, size_t num_words __maybe_unused, void *user)
940 {
941 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_OVERWRITE);
877 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
942 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
943
944 debug(D_PLUGINSD, "requested to OVERWRITE host labels");
945
@@ -898,11 +963,12 @@ PARSER_RC pluginsd_clabel(char **words, size_t num_words, void *user)
963
964 if (!name || !value || !*label_source) {
965 error("Ignoring malformed or empty CHART LABEL command.");
901 - return PLUGINSD_DISABLE_PLUGIN(user);
966 + return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
967 }
968
969 if(unlikely(!((PARSER_USER_OBJECT *) user)->chart_rrdlabels_linked_temporarily)) {
905 - ((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily = ((PARSER_USER_OBJECT *)user)->st->rrdlabels;
970 + RRDSET *st = pluginsd_get_chart_from_parent(user);
971 + ((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily = st->rrdlabels;
972 rrdlabels_unmark_all(((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily);
973 }
974
@@ -915,17 +981,17 @@ PARSER_RC pluginsd_clabel(char **words, size_t num_words, void *user)
981 PARSER_RC pluginsd_clabel_commit(char **words __maybe_unused, size_t num_words __maybe_unused, void *user)
982 {
983 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CLABEL_COMMIT);
918 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
984 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
985
986 RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_CLABEL_COMMIT, PLUGINSD_KEYWORD_BEGIN);
921 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
987 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
988
989 debug(D_PLUGINSD, "requested to commit chart labels");
990
991 if(!((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily) {
992 error("PLUGINSD: 'host:%s' got CLABEL_COMMIT, without a CHART or BEGIN. Ignoring it.",
993 rrdhost_hostname(host));
928 - return PLUGINSD_DISABLE_PLUGIN(user);
994 + return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
995 }
996
997 rrdlabels_remove_all_unmarked(((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily);
@@ -937,15 +1003,14 @@ PARSER_RC pluginsd_clabel_commit(char **words __maybe_unused, size_t num_words _
1003 return PARSER_RC_OK;
1004 }
1005
940 -PARSER_RC pluginsd_replay_rrdset_begin(char **words, size_t num_words, void *user)
941 -{
1006 +PARSER_RC pluginsd_replay_begin(char **words, size_t num_words, void *user) {
1007 char *id = get_word(words, num_words, 1);
1008 char *start_time_str = get_word(words, num_words, 2);
1009 char *end_time_str = get_word(words, num_words, 3);
1010 char *child_now_str = get_word(words, num_words, 4);
1011
1012 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_BEGIN);
948 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
1013 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1014
1015 RRDSET *st;
1016 if (likely(!id || !*id))
@@ -953,8 +1018,8 @@ PARSER_RC pluginsd_replay_rrdset_begin(char **words, size_t num_words, void *use
1018 else
1019 st = pluginsd_find_chart(host, id, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1020
956 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
957 - ((PARSER_USER_OBJECT *) user)->st = st;
1021 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1022 + pluginsd_set_chart_from_parent(user, st, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1023
1024 if(start_time_str && end_time_str) {
1025 time_t start_time = (time_t)str2ul(start_time_str);
@@ -1016,7 +1081,9 @@ PARSER_RC pluginsd_replay_rrdset_begin(char **words, size_t num_words, void *use
1081 return PARSER_RC_OK;
1082 }
1083
1019 - error("PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN " from %ld to %ld, but timestamps are invalid (now is %ld [%s], tolerance %ld). Ignoring " PLUGINSD_KEYWORD_REPLAY_SET,
1084 + error("PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN
1085 + " from %ld to %ld, but timestamps are invalid "
1086 + "(now is %ld [%s], tolerance %ld). Ignoring " PLUGINSD_KEYWORD_REPLAY_SET,
1087 rrdhost_hostname(st->rrdhost), rrdset_id(st), start_time, end_time,
1088 wall_clock_time, wall_clock_comes_from_child ? "child wall clock" : "parent wall clock", tolerance);
1089 }
@@ -1033,6 +1100,33 @@ PARSER_RC pluginsd_replay_rrdset_begin(char **words, size_t num_words, void *use
1100 return PARSER_RC_OK;
1101 }
1102
1103 +static inline SN_FLAGS pluginsd_parse_storage_number_flags(const char *flags_str) {
1104 + SN_FLAGS flags = SN_FLAG_NONE;
1105 +
1106 + char c;
1107 + while ((c = *flags_str++)) {
1108 + switch (c) {
1109 + case 'A':
1110 + flags |= SN_FLAG_NOT_ANOMALOUS;
1111 + break;
1112 +
1113 + case 'R':
1114 + flags |= SN_FLAG_RESET;
1115 + break;
1116 +
1117 + case 'E':
1118 + flags = SN_EMPTY_SLOT;
1119 + return flags;
1120 +
1121 + default:
1122 + internal_error(true, "Unknown SN_FLAGS flag '%c'", c);
1123 + break;
1124 + }
1125 + }
1126 +
1127 + return flags;
1128 +}
1129 +
1130 PARSER_RC pluginsd_replay_set(char **words, size_t num_words, void *user)
1131 {
1132 char *dimension = get_word(words, num_words, 1);
@@ -1040,31 +1134,34 @@ PARSER_RC pluginsd_replay_set(char **words, size_t num_words, void *user)
1134 char *flags_str = get_word(words, num_words, 3);
1135
1136 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_SET);
1043 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
1137 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1138
1139 RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_SET, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1046 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
1140 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1141
1048 - if(!((PARSER_USER_OBJECT *) user)->replay.rset_enabled) {
1142 + PARSER_USER_OBJECT *u = user;
1143 + if(!u->replay.rset_enabled) {
1144 error_limit_static_thread_var(erl, 1, 0);
1050 - error_limit(&erl, "PLUGINSD: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_SET " but it is disabled by " PLUGINSD_KEYWORD_REPLAY_BEGIN " errors",
1051 - rrdhost_hostname(host), rrdset_id(st));
1145 + error_limit(&erl, "PLUGINSD: 'host:%s/chart:%s' got a %s but it is disabled by %s errors",
1146 + rrdhost_hostname(host), rrdset_id(st), PLUGINSD_KEYWORD_REPLAY_SET, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1147
1148 // we have to return OK here
1149 return PARSER_RC_OK;
1150 }
1151
1057 - RRDDIM_ACQUIRED *rda = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_REPLAY_SET);
1058 - if(!rda) return PLUGINSD_DISABLE_PLUGIN(user);
1152 + RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_REPLAY_SET);
1153 + if(!rd) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1154
1060 - if (unlikely(!((PARSER_USER_OBJECT *) user)->replay.start_time || !((PARSER_USER_OBJECT *) user)->replay.end_time)) {
1061 - error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a " PLUGINSD_KEYWORD_REPLAY_SET " with invalid timestamps %ld to %ld from a " PLUGINSD_KEYWORD_REPLAY_BEGIN ". Disabling it.",
1155 + if (unlikely(!u->replay.start_time || !u->replay.end_time)) {
1156 + error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a %s with invalid timestamps %ld to %ld from a %s. Disabling it.",
1157 rrdhost_hostname(host),
1158 rrdset_id(st),
1159 dimension,
1065 - ((PARSER_USER_OBJECT *) user)->replay.start_time,
1066 - ((PARSER_USER_OBJECT *) user)->replay.end_time);
1067 - return PLUGINSD_DISABLE_PLUGIN(user);
1160 + PLUGINSD_KEYWORD_REPLAY_SET,
1161 + u->replay.start_time,
1162 + u->replay.end_time,
1163 + PLUGINSD_KEYWORD_REPLAY_BEGIN);
1164 + return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1165 }
1166
1167 if (unlikely(!value_str || !*value_str))
@@ -1074,39 +1171,19 @@ PARSER_RC pluginsd_replay_set(char **words, size_t num_words, void *user)
1171 flags_str = "";
1172
1173 if (likely(value_str)) {
1077 - RRDDIM *rd = rrddim_acquired_to_rrddim(rda);
1078 -
1174 RRDDIM_FLAGS rd_flags = rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE | RRDDIM_FLAG_ARCHIVED);
1175
1176 if(!(rd_flags & RRDDIM_FLAG_ARCHIVED)) {
1177 NETDATA_DOUBLE value = strtondd(value_str, NULL);
1083 - SN_FLAGS flags = SN_FLAG_NONE;
1084 -
1085 - char c;
1086 - while ((c = *flags_str++)) {
1087 - switch (c) {
1088 - case 'R':
1089 - flags |= SN_FLAG_RESET;
1090 - break;
1091 -
1092 - case 'E':
1093 - flags |= SN_EMPTY_SLOT;
1094 - value = NAN;
1095 - break;
1096 -
1097 - default:
1098 - error("unknown flag '%c'", c);
1099 - break;
1100 - }
1101 - }
1178 + SN_FLAGS flags = pluginsd_parse_storage_number_flags(flags_str);
1179
1103 - if (!netdata_double_isnumber(value)) {
1180 + if (!netdata_double_isnumber(value) || (flags == SN_EMPTY_SLOT)) {
1181 value = NAN;
1182 flags = SN_EMPTY_SLOT;
1183 }
1184
1108 - rrddim_store_metric(rd, ((PARSER_USER_OBJECT *) user)->replay.end_time_ut, value, flags);
1109 - rd->last_collected_time.tv_sec = ((PARSER_USER_OBJECT *) user)->replay.end_time;
1185 + rrddim_store_metric(rd, u->replay.end_time_ut, value, flags);
1186 + rd->last_collected_time.tv_sec = u->replay.end_time;
1187 rd->last_collected_time.tv_usec = 0;
1188 rd->collections_counter++;
1189 }
@@ -1117,7 +1194,6 @@ PARSER_RC pluginsd_replay_set(char **words, size_t num_words, void *user)
1194 }
1195 }
1196
1120 - rrddim_acquired_release(rda);
1197 return PARSER_RC_OK;
1198 }
1199
@@ -1133,26 +1209,25 @@ PARSER_RC pluginsd_replay_rrddim_collection_state(char **words, size_t num_words
1209 char *last_stored_value_str = get_word(words, num_words, 5);
1210
1211 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE);
1136 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
1212 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1213
1214 RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1139 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
1215 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1216
1141 - RRDDIM_ACQUIRED *rda = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE);
1142 - if(!rda) return PLUGINSD_DISABLE_PLUGIN(user);
1217 + RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE);
1218 + if(!rd) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1219
1144 - RRDDIM *rd = rrddim_acquired_to_rrddim(rda);
1220 usec_t dim_last_collected_ut = (usec_t)rd->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)rd->last_collected_time.tv_usec;
1221 usec_t last_collected_ut = last_collected_ut_str ? str2ull(last_collected_ut_str) : 0;
1222 if(last_collected_ut > dim_last_collected_ut) {
1148 - rd->last_collected_time.tv_sec = last_collected_ut / USEC_PER_SEC;
1149 - rd->last_collected_time.tv_usec = last_collected_ut % USEC_PER_SEC;
1223 + rd->last_collected_time.tv_sec = (time_t)(last_collected_ut / USEC_PER_SEC);
1224 + rd->last_collected_time.tv_usec = (last_collected_ut % USEC_PER_SEC);
1225 }
1226
1227 rd->last_collected_value = last_collected_value_str ? str2ll(last_collected_value_str, NULL) : 0;
1228 rd->last_calculated_value = last_calculated_value_str ? str2ndd(last_calculated_value_str, NULL) : 0;
1229 rd->last_stored_value = last_stored_value_str ? str2ndd(last_stored_value_str, NULL) : 0.0;
1155 - rrddim_acquired_release(rda);
1230 +
1231 return PARSER_RC_OK;
1232 }
1233
@@ -1165,23 +1240,23 @@ PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words
1240 char *last_updated_ut_str = get_word(words, num_words, 2);
1241
1242 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE);
1168 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
1243 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1244
1245 RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1171 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
1246 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1247
1248 usec_t chart_last_collected_ut = (usec_t)st->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)st->last_collected_time.tv_usec;
1249 usec_t last_collected_ut = last_collected_ut_str ? str2ull(last_collected_ut_str) : 0;
1250 if(last_collected_ut > chart_last_collected_ut) {
1176 - st->last_collected_time.tv_sec = last_collected_ut / USEC_PER_SEC;
1177 - st->last_collected_time.tv_usec = last_collected_ut % USEC_PER_SEC;
1251 + st->last_collected_time.tv_sec = (time_t)(last_collected_ut / USEC_PER_SEC);
1252 + st->last_collected_time.tv_usec = (last_collected_ut % USEC_PER_SEC);
1253 }
1254
1255 usec_t chart_last_updated_ut = (usec_t)st->last_updated.tv_sec * USEC_PER_SEC + (usec_t)st->last_updated.tv_usec;
1256 usec_t last_updated_ut = last_updated_ut_str ? str2ull(last_updated_ut_str) : 0;
1257 if(last_updated_ut > chart_last_updated_ut) {
1183 - st->last_updated.tv_sec = last_updated_ut / USEC_PER_SEC;
1184 - st->last_updated.tv_usec = last_updated_ut % USEC_PER_SEC;
1258 + st->last_updated.tv_sec = (time_t)(last_updated_ut / USEC_PER_SEC);
1259 + st->last_updated.tv_usec = (last_updated_ut % USEC_PER_SEC);
1260 }
1261
1262 st->counter++;
@@ -1219,10 +1294,10 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user)
1294 PARSER_USER_OBJECT *user_object = user;
1295
1296 RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_END);
1222 - if(!host) return PLUGINSD_DISABLE_PLUGIN(user);
1297 + if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1298
1299 RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_END, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1225 - if(!st) return PLUGINSD_DISABLE_PLUGIN(user);
1300 + if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1301
1302 #ifdef NETDATA_LOG_REPLICATION_REQUESTS
1303 internal_error(true,
@@ -1235,8 +1310,7 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user)
1310 );
1311 #endif
1312
1238 - ((PARSER_USER_OBJECT *) user)->st = NULL;
1239 - ((PARSER_USER_OBJECT *) user)->count++;
1313 + ((PARSER_USER_OBJECT *) user)->data_collections_count++;
1314
1315 if(((PARSER_USER_OBJECT *) user)->replay.rset_enabled && st->rrdhost->receiver) {
1316 time_t now = now_realtime_sec();
@@ -1282,11 +1356,16 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user)
1356 internal_error(true, "REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_END " with enable_streaming = true, but there is no replication in progress for this chart.",
1357 rrdhost_hostname(host), rrdset_id(st));
1358 #endif
1359 +
1360 + pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_REPLAY_END);
1361 +
1362 worker_set_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION, 100.0);
1363
1364 return PARSER_RC_OK;
1365 }
1366
1367 + pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_REPLAY_END);
1368 +
1369 rrdcontext_updated_retention_rrdset(st);
1370
1371 bool ok = replicate_chart_request(send_to_plugin, user_object->parser, host, st,
@@ -1295,8 +1374,289 @@ PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user)
1374 return ok ? PARSER_RC_OK : PARSER_RC_ERROR;
1375 }
1376
1377 +PARSER_RC pluginsd_begin_v2(char **words, size_t num_words, void *user) {
1378 + timing_init();
1379 +
1380 + char *id = get_word(words, num_words, 1);
1381 + char *update_every_str = get_word(words, num_words, 2);
1382 + char *end_time_str = get_word(words, num_words, 3);
1383 + char *wall_clock_time_str = get_word(words, num_words, 4);
1384 +
1385 + if(unlikely(!id || !update_every_str || !end_time_str || !wall_clock_time_str))
1386 + return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_BEGIN_V2, "missing parameters");
1387 +
1388 + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_BEGIN_V2);
1389 + if(unlikely(!host)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1390 +
1391 + timing_step(TIMING_STEP_BEGIN2_PREPARE);
1392 +
1393 + RRDSET *st = pluginsd_find_chart(host, id, PLUGINSD_KEYWORD_BEGIN_V2);
1394 + if(unlikely(!st)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1395 +
1396 + pluginsd_set_chart_from_parent(user, st, PLUGINSD_KEYWORD_BEGIN_V2);
1397 +
1398 + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE | RRDSET_FLAG_ARCHIVED)))
1399 + rrdset_isnot_obsolete(st);
1400 +
1401 + timing_step(TIMING_STEP_BEGIN2_FIND_CHART);
1402 +
1403 + // ------------------------------------------------------------------------
1404 + // parse the parameters
1405 +
1406 + time_t update_every = (time_t)str2ull_hex_or_dec(update_every_str);
1407 + time_t end_time = (time_t)str2ull_hex_or_dec(end_time_str);
1408 +
1409 + time_t wall_clock_time;
1410 + if(likely(*wall_clock_time_str == '#'))
1411 + wall_clock_time = end_time;
1412 + else
1413 + wall_clock_time = (time_t)str2ull_hex_or_dec(wall_clock_time_str);
1414 +
1415 + if (unlikely(update_every != st->update_every))
1416 + rrdset_set_update_every_s(st, update_every);
1417 +
1418 + timing_step(TIMING_STEP_BEGIN2_PARSE);
1419 +
1420 + // ------------------------------------------------------------------------
1421 + // prepare our state
1422 +
1423 + pluginsd_lock_rrdset_data_collection(user);
1424 +
1425 + PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
1426 + u->v2.update_every = update_every;
1427 + u->v2.end_time = end_time;
1428 + u->v2.wall_clock_time = wall_clock_time;
1429 + u->v2.ml_locked = ml_chart_update_begin(st);
1430 +
1431 + timing_step(TIMING_STEP_BEGIN2_ML);
1432 +
1433 + // ------------------------------------------------------------------------
1434 + // propagate it forward in v2
1435 +
1436 + if(!u->v2.stream_buffer.wb && rrdhost_has_rrdpush_sender_enabled(st->rrdhost))
1437 + u->v2.stream_buffer = rrdset_push_metric_initialize(u->st, wall_clock_time);
1438 +
1439 + if(u->v2.stream_buffer.v2 && u->v2.stream_buffer.wb) {
1440 + BUFFER *wb = u->v2.stream_buffer.wb;
1441 +
1442 + buffer_need_bytes(wb, 1024);
1443 +
1444 + if(unlikely(u->v2.stream_buffer.begin_v2_added))
1445 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_END_V2 "\n", sizeof(PLUGINSD_KEYWORD_END_V2) - 1 + 1);
1446 +
1447 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_BEGIN_V2 " '", sizeof(PLUGINSD_KEYWORD_BEGIN_V2) - 1 + 2);
1448 + buffer_fast_strcat(wb, rrdset_id(st), string_strlen(st->id));
1449 + buffer_fast_strcat(wb, "' ", 2);
1450 + buffer_strcat(wb, update_every_str);
1451 + buffer_fast_strcat(wb, " ", 1);
1452 + buffer_strcat(wb, end_time_str);
1453 + buffer_fast_strcat(wb, " ", 1);
1454 + buffer_strcat(wb, wall_clock_time_str);
1455 + buffer_fast_strcat(wb, "\n", 1);
1456 +
1457 + u->v2.stream_buffer.last_point_end_time_s = end_time;
1458 + u->v2.stream_buffer.begin_v2_added = true;
1459 + }
1460 +
1461 + timing_step(TIMING_STEP_BEGIN2_PROPAGATE);
1462 +
1463 + // ------------------------------------------------------------------------
1464 + // store it
1465 +
1466 + st->last_collected_time.tv_sec = end_time;
1467 + st->last_collected_time.tv_usec = 0;
1468 + st->last_updated.tv_sec = end_time;
1469 + st->last_updated.tv_usec = 0;
1470 + st->counter++;
1471 + st->counter_done++;
1472 +
1473 + // these are only needed for db mode RAM, SAVE, MAP, ALLOC
1474 + st->current_entry++;
1475 + if(st->current_entry >= st->entries)
1476 + st->current_entry -= st->entries;
1477 +
1478 + timing_step(TIMING_STEP_BEGIN2_STORE);
1479 +
1480 + return PARSER_RC_OK;
1481 +}
1482 +
1483 +PARSER_RC pluginsd_set_v2(char **words, size_t num_words, void *user) {
1484 + timing_init();
1485 +
1486 + char *dimension = get_word(words, num_words, 1);
1487 + char *collected_str = get_word(words, num_words, 2);
1488 + char *value_str = get_word(words, num_words, 3);
1489 + char *flags_str = get_word(words, num_words, 4);
1490 +
1491 + if(unlikely(!dimension || !collected_str || !value_str || !flags_str))
1492 + return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_SET_V2, "missing parameters");
1493 +
1494 + PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
1495 +
1496 + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_SET_V2);
1497 + if(unlikely(!host)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1498 +
1499 + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_SET_V2, PLUGINSD_KEYWORD_BEGIN_V2);
1500 + if(unlikely(!st)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1501 +
1502 + timing_step(TIMING_STEP_SET2_PREPARE);
1503 +
1504 + RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_SET_V2);
1505 + if(unlikely(!rd)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1506 +
1507 + if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE | RRDDIM_FLAG_ARCHIVED)))
1508 + rrddim_isnot_obsolete(st, rd);
1509 +
1510 + timing_step(TIMING_STEP_SET2_LOOKUP_DIMENSION);
1511 +
1512 + // ------------------------------------------------------------------------
1513 + // parse the parameters
1514 +
1515 + collected_number collected_value = (collected_number)str2ll_hex_or_dec(collected_str);
1516 +
1517 + NETDATA_DOUBLE value;
1518 + if(*value_str == '#')
1519 + value = (NETDATA_DOUBLE)collected_value;
1520 + else
1521 + value = strtondd(value_str, NULL);
1522 +
1523 + SN_FLAGS flags = pluginsd_parse_storage_number_flags(flags_str);
1524 +
1525 + timing_step(TIMING_STEP_SET2_PARSE);
1526 +
1527 + // ------------------------------------------------------------------------
1528 + // check value and ML
1529 +
1530 + if (unlikely(!netdata_double_isnumber(value) || (flags == SN_EMPTY_SLOT))) {
1531 + value = NAN;
1532 + flags = SN_EMPTY_SLOT;
1533 +
1534 + if(u->v2.ml_locked)
1535 + ml_is_anomalous(rd, u->v2.end_time, 0, false);
1536 + }
1537 + else if(u->v2.ml_locked) {
1538 + if (ml_is_anomalous(rd, u->v2.end_time, value, true)) {
1539 + // clear anomaly bit: 0 -> is anomalous, 1 -> not anomalous
1540 + flags &= ~((storage_number) SN_FLAG_NOT_ANOMALOUS);
1541 + }
1542 + else
1543 + flags |= SN_FLAG_NOT_ANOMALOUS;
1544 + }
1545 +
1546 + timing_step(TIMING_STEP_SET2_ML);
1547 +
1548 + // ------------------------------------------------------------------------
1549 + // propagate it forward in v2
1550 +
1551 + if(u->v2.stream_buffer.v2 && u->v2.stream_buffer.begin_v2_added && u->v2.stream_buffer.wb) {
1552 + BUFFER *wb = u->v2.stream_buffer.wb;
1553 + buffer_need_bytes(wb, 1024);
1554 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_SET_V2 " '", sizeof(PLUGINSD_KEYWORD_SET_V2) - 1 + 2);
1555 + buffer_fast_strcat(wb, rrddim_id(rd), string_strlen(rd->id));
1556 + buffer_fast_strcat(wb, "' ", 2);
1557 + buffer_strcat(wb, collected_str);
1558 + buffer_fast_strcat(wb, " ", 1);
1559 + buffer_strcat(wb, value_str);
1560 + buffer_fast_strcat(wb, " ", 1);
1561 + buffer_print_sn_flags(wb, flags, true);
1562 + buffer_fast_strcat(wb, "\n", 1);
1563 + }
1564 +
1565 + timing_step(TIMING_STEP_SET2_PROPAGATE);
1566 +
1567 + // ------------------------------------------------------------------------
1568 + // store it
1569 +
1570 + rrddim_store_metric(rd, u->v2.end_time * USEC_PER_SEC, value, flags);
1571 + rd->last_collected_time.tv_sec = u->v2.end_time;
1572 + rd->last_collected_time.tv_usec = 0;
1573 + rd->last_collected_value = collected_value;
1574 + rd->last_stored_value = value;
1575 + rd->last_calculated_value = value;
1576 + rd->collections_counter++;
1577 + rd->updated = true;
1578 +
1579 + timing_step(TIMING_STEP_SET2_STORE);
1580 +
1581 + return PARSER_RC_OK;
1582 +}
1583 +
1584 +void pluginsd_cleanup_v2(void *user) {
1585 + // this is called when the thread is stopped while processing
1586 + pluginsd_set_chart_from_parent(user, NULL, "THREAD CLEANUP");
1587 +}
1588 +
1589 +PARSER_RC pluginsd_end_v2(char **words __maybe_unused, size_t num_words __maybe_unused, void *user) {
1590 + timing_init();
1591 +
1592 + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_END_V2);
1593 + if(unlikely(!host)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1594 +
1595 + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_END_V2, PLUGINSD_KEYWORD_BEGIN_V2);
1596 + if(unlikely(!st)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
1597 +
1598 + PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
1599 + u->data_collections_count++;
1600 +
1601 + timing_step(TIMING_STEP_END2_PREPARE);
1602 +
1603 + // ------------------------------------------------------------------------
1604 + // propagate the whole chart update in v1
1605 +
1606 + if(unlikely(!u->v2.stream_buffer.v2 && !u->v2.stream_buffer.begin_v2_added && u->v2.stream_buffer.wb))
1607 + rrdset_push_metrics_v1(&u->v2.stream_buffer, st);
1608 +
1609 + timing_step(TIMING_STEP_END2_PUSH_V1);
1610 +
1611 + // ------------------------------------------------------------------------
1612 + // unblock data collection
1613 +
1614 + ml_chart_update_end(st);
1615 + u->v2.ml_locked = false;
1616 +
1617 + timing_step(TIMING_STEP_END2_ML);
1618 +
1619 + pluginsd_unlock_rrdset_data_collection(user);
1620 + rrdcontext_collected_rrdset(st);
1621 + store_metric_collection_completed();
1622 +
1623 + timing_step(TIMING_STEP_END2_RRDSET);
1624 +
1625 + // ------------------------------------------------------------------------
1626 + // propagate it forward
1627 +
1628 + rrdset_push_metrics_finished(&u->v2.stream_buffer, st);
1629 +
1630 + timing_step(TIMING_STEP_END2_PROPAGATE);
1631 +
1632 + // ------------------------------------------------------------------------
1633 + // cleanup RRDSET / RRDDIM
1634 +
1635 + RRDDIM *rd;
1636 + rrddim_foreach_read(rd, st) {
1637 + rd->calculated_value = 0;
1638 + rd->collected_value = 0;
1639 + rd->updated = false;
1640 + }
1641 + rrddim_foreach_done(rd);
1642 +
1643 + // ------------------------------------------------------------------------
1644 + // reset state
1645 +
1646 + u->v2 = (struct parser_user_object_v2){ 0 };
1647 +
1648 + timing_step(TIMING_STEP_END2_STORE);
1649 + timing_report();
1650 +
1651 + return PARSER_RC_OK;
1652 +}
1653 +
1654 static void pluginsd_process_thread_cleanup(void *ptr) {
1655 PARSER *parser = (PARSER *)ptr;
1656 +
1657 + if(parser->user_cleanup_cb)
1658 + parser->user_cleanup_cb(parser->user);
1659 +
1660 rrd_collector_finished();
1661 parser_destroy(parser);
1662 }
@@ -1335,7 +1695,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp_plugi
1695 };
1696
1697 // fp_plugin_output = our input; fp_plugin_input = our output
1338 - PARSER *parser = parser_init(host, &user, fp_plugin_output, fp_plugin_input, -1, PARSER_INPUT_SPLIT, NULL);
1698 + PARSER *parser = parser_init(host, &user, NULL, fp_plugin_output, fp_plugin_input, -1, PARSER_INPUT_SPLIT, NULL);
1699
1700 rrd_collector_started();
1701
@@ -1354,7 +1714,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp_plugi
1714 netdata_thread_cleanup_pop(1);
1715
1716 cd->unsafe.enabled = user.enabled;
1357 - size_t count = user.count;
1717 + size_t count = user.data_collections_count;
1718
1719 if (likely(count)) {
1720 cd->successful_collections += count;
collectors/plugins.d/pluginsd_parser.h
+11 -5
@@ -14,13 +14,10 @@ typedef struct parser_user_object {
14 int trust_durations;
15 DICTIONARY *new_host_labels;
16 DICTIONARY *chart_rrdlabels_linked_temporarily;
17 - size_t count;
17 + size_t data_collections_count;
18 int enabled;
19 - uint8_t st_exists;
20 - uint8_t host_exists;
21 - void *private; // the user can set this for private use
19
23 - struct {
20 + struct parser_user_object_replay {
21 time_t start_time;
22 time_t end_time;
23
@@ -31,6 +28,15 @@ typedef struct parser_user_object {
28
29 bool rset_enabled;
30 } replay;
31 +
32 + struct parser_user_object_v2 {
33 + bool locked_data_collection;
34 + RRDSET_STREAM_BUFFER stream_buffer;
35 + time_t update_every;
36 + time_t end_time;
37 + time_t wall_clock_time;
38 + bool ml_locked;
39 + } v2;
40 } PARSER_USER_OBJECT;
41
42 PARSER_RC pluginsd_function(char **words, size_t num_words, void *user);
database/engine/pagecache.h
-2
@@ -45,9 +45,7 @@ struct rrdeng_page_info {
45 };
46
47 struct pg_alignment {
48 - uint32_t page_position;
48 uint32_t refcount;
50 - uint16_t initial_slots;
49 };
50
51 struct rrdeng_query_handle;
database/engine/rrdengine.h
+3 -3
@@ -160,9 +160,7 @@ struct jv2_page_info {
160 };
161
162 typedef enum __attribute__ ((__packed__)) {
163 - RRDENG_CHO_UNALIGNED = (1 << 0), // set when this metric is not page aligned according to page alignment
164 - RRDENG_FIRST_PAGE_ALLOCATED = (1 << 1), // set when this metric has allocated its first page
165 - RRDENG_1ST_METRIC_WRITER = (1 << 2),
163 + RRDENG_1ST_METRIC_WRITER = (1 << 0),
164 } RRDENG_COLLECT_HANDLE_OPTIONS;
165
166 typedef enum __attribute__ ((__packed__)) {
@@ -185,6 +183,8 @@ typedef enum __attribute__ ((__packed__)) {
183 struct rrdeng_collect_handle {
184 struct metric *metric;
185 struct pgc_page *page;
186 + void *data;
187 + size_t data_size;
188 struct pg_alignment *alignment;
189 RRDENG_COLLECT_HANDLE_OPTIONS options;
190 uint8_t type;
database/engine/rrdengineapi.c
+107 -142
@@ -257,6 +257,8 @@ STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metri
257 handle = callocz(1, sizeof(struct rrdeng_collect_handle));
258 handle->metric = metric;
259 handle->page = NULL;
260 + handle->data = NULL;
261 + handle->data_size = 0;
262 handle->page_position = 0;
263 handle->page_entries_max = 0;
264 handle->update_every_ut = (usec_t)update_every * USEC_PER_SEC;
@@ -340,6 +342,8 @@ void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_h
342 handle->page_flags = 0;
343 handle->page_position = 0;
344 handle->page_entries_max = 0;
345 + handle->data = NULL;
346 + handle->data_size = 0;
347
348 // important!
349 // we should never zero page end time ut, because this will allow
@@ -348,6 +352,8 @@ void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_h
352 // handle->page_start_time_ut;
353
354 check_and_fix_mrg_update_every(handle);
355 +
356 + timing_step(TIMING_STEP_DBENGINE_FLUSH_PAGE);
357 }
358
359 static void rrdeng_store_metric_create_new_page(struct rrdeng_collect_handle *handle,
@@ -414,62 +420,57 @@ static void rrdeng_store_metric_create_new_page(struct rrdeng_collect_handle *ha
420 handle->page_flags |= RRDENG_PAGE_CREATED_IN_FUTURE;
421
422 check_and_fix_mrg_update_every(handle);
423 +
424 + timing_step(TIMING_STEP_DBENGINE_CREATE_NEW_PAGE);
425 }
426
419 -static void *rrdeng_alloc_new_metric_data(struct rrdeng_collect_handle *handle, size_t *data_size, usec_t point_in_time_ut) {
420 - struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
421 - size_t size;
427 +static size_t aligned_allocation_entries(size_t max_slots, size_t target_slot, time_t now_s) {
428 + size_t slots = target_slot;
429 + size_t pos = (now_s % max_slots);
430
423 - if(handle->options & RRDENG_FIRST_PAGE_ALLOCATED) {
424 - // any page except the first
425 - size = tier_page_size[ctx->config.tier];
426 - }
427 - else {
428 - size_t final_slots = 0;
431 + if(pos > slots)
432 + slots += max_slots - pos;
433
430 - // the first page
431 - handle->options |= RRDENG_FIRST_PAGE_ALLOCATED;
432 - size_t max_size = tier_page_size[ctx->config.tier];
433 - size_t max_slots = max_size / CTX_POINT_SIZE_BYTES(ctx);
434 + else if(pos < slots)
435 + slots -= pos;
436
435 - if(handle->alignment->initial_slots) {
436 - final_slots = handle->alignment->initial_slots;
437 - }
438 - else {
439 - max_slots -= 3;
437 + else
438 + slots = max_slots;
439
441 - size_t smaller_slot = indexing_partition((Word_t)handle->alignment, max_slots);
442 - final_slots = smaller_slot;
440 + return slots;
441 +}
442
444 - time_t now_s = (time_t)(point_in_time_ut / USEC_PER_SEC);
445 - size_t current_pos = (now_s % max_slots);
443 +static void *rrdeng_alloc_new_metric_data(struct rrdeng_collect_handle *handle, size_t *data_size, usec_t point_in_time_ut) {
444 + struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
445
447 - if(current_pos > final_slots)
448 - final_slots += max_slots - current_pos;
446 + size_t max_size = tier_page_size[ctx->config.tier];
447 + size_t max_slots = max_size / CTX_POINT_SIZE_BYTES(ctx);
448
450 - else if(current_pos < final_slots)
451 - final_slots -= current_pos;
449 + size_t slots = aligned_allocation_entries(
450 + max_slots,
451 + indexing_partition((Word_t) handle->alignment, max_slots),
452 + (time_t) (point_in_time_ut / USEC_PER_SEC)
453 + );
454
453 - if(final_slots < 3) {
454 - final_slots += 3;
455 - smaller_slot += 3;
455 + if(slots < max_slots / 3)
456 + slots = max_slots / 3;
457
457 - if(smaller_slot >= max_slots)
458 - smaller_slot -= max_slots;
459 - }
458 + if(slots < 3)
459 + slots = 3;
460
461 - max_slots += 3;
462 - handle->alignment->initial_slots = smaller_slot + 3;
461 + size_t size = slots * CTX_POINT_SIZE_BYTES(ctx);
462
464 - internal_fatal(handle->alignment->initial_slots < 3 || handle->alignment->initial_slots >= max_slots, "ooops! wrong distribution of metrics across time");
465 - internal_fatal(final_slots < 3 || final_slots >= max_slots, "ooops! wrong distribution of metrics across time");
466 - }
463 + // internal_error(true, "PAGE ALLOC %zu bytes (%zu max)", size, max_size);
464
468 - size = final_slots * CTX_POINT_SIZE_BYTES(ctx);
469 - }
465 + internal_fatal(slots < 3 || slots > max_slots, "ooops! wrong distribution of metrics across time");
466 + internal_fatal(size > tier_page_size[ctx->config.tier] || size < CTX_POINT_SIZE_BYTES(ctx) * 2, "ooops! wrong page size");
467
468 *data_size = size;
472 - return dbengine_page_alloc(size);
469 + void *d = dbengine_page_alloc(size);
470 +
471 + timing_step(TIMING_STEP_DBENGINE_PAGE_ALLOC);
472 +
473 + return d;
474 }
475
476 static void rrdeng_store_metric_append_point(STORAGE_COLLECT_HANDLE *collection_handle,
@@ -484,75 +485,33 @@ static void rrdeng_store_metric_append_point(STORAGE_COLLECT_HANDLE *collection_
485 struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle;
486 struct rrdengine_instance *ctx = mrg_metric_ctx(handle->metric);
487
487 - bool perfect_page_alignment = false;
488 - void *data;
489 - size_t data_size;
488 + if(unlikely(!handle->data))
489 + handle->data = rrdeng_alloc_new_metric_data(handle, &handle->data_size, point_in_time_ut);
490
491 - if(likely(handle->page)) {
492 - /* Make alignment decisions */
493 - if (handle->page_position == handle->alignment->page_position) {
494 - /* this is the leading dimension that defines chart alignment */
495 - perfect_page_alignment = true;
496 - }
497 -
498 - /* is the metric far enough out of alignment with the others? */
499 - if (unlikely(handle->page_position + 1 < handle->alignment->page_position))
500 - handle->options |= RRDENG_CHO_UNALIGNED;
491 + timing_step(TIMING_STEP_DBENGINE_CHECK_DATA);
492
502 - if (unlikely((handle->options & RRDENG_CHO_UNALIGNED) &&
503 - /* did the other metrics change page? */
504 - handle->alignment->page_position <= 1)) {
505 - handle->options &= ~RRDENG_CHO_UNALIGNED;
506 - handle->page_flags |= RRDENG_PAGE_UNALIGNED;
507 - rrdeng_store_metric_flush_current_page(collection_handle);
508 -
509 - data = rrdeng_alloc_new_metric_data(handle, &data_size, point_in_time_ut);
510 - }
511 - else {
512 - data = pgc_page_data(handle->page);
513 - data_size = pgc_page_data_size(main_cache, handle->page);
514 - }
493 + if(likely(ctx->config.page_type == PAGE_METRICS)) {
494 + storage_number *tier0_metric_data = handle->data;
495 + tier0_metric_data[handle->page_position] = pack_storage_number(n, flags);
496 + }
497 + else if(likely(ctx->config.page_type == PAGE_TIER)) {
498 + storage_number_tier1_t *tier12_metric_data = handle->data;
499 + storage_number_tier1_t number_tier1;
500 + number_tier1.sum_value = (float) n;
501 + number_tier1.min_value = (float) min_value;
502 + number_tier1.max_value = (float) max_value;
503 + number_tier1.anomaly_count = anomaly_count;
504 + number_tier1.count = count;
505 + tier12_metric_data[handle->page_position] = number_tier1;
506 }
507 else
517 - data = rrdeng_alloc_new_metric_data(handle, &data_size, point_in_time_ut);
518 -
519 - switch (ctx->config.page_type) {
520 - case PAGE_METRICS: {
521 - storage_number *tier0_metric_data = data;
522 - tier0_metric_data[handle->page_position] = pack_storage_number(n, flags);
523 - }
524 - break;
508 + fatal("DBENGINE: cannot store metric on unknown page type id %d", ctx->config.page_type);
509
526 - case PAGE_TIER: {
527 - storage_number_tier1_t *tier12_metric_data = data;
528 - storage_number_tier1_t number_tier1;
529 - number_tier1.sum_value = (float)n;
530 - number_tier1.min_value = (float)min_value;
531 - number_tier1.max_value = (float)max_value;
532 - number_tier1.anomaly_count = anomaly_count;
533 - number_tier1.count = count;
534 - tier12_metric_data[handle->page_position] = number_tier1;
535 - }
536 - break;
537 -
538 - default: {
539 - static bool logged = false;
540 - if(!logged) {
541 - error("DBENGINE: cannot store metric on unknown page type id %d", ctx->config.page_type);
542 - logged = true;
543 - }
544 - }
545 - break;
546 - }
510 + timing_step(TIMING_STEP_DBENGINE_PACK);
511
512 if(unlikely(!handle->page)){
549 - rrdeng_store_metric_create_new_page(handle, ctx, point_in_time_ut, data, data_size);
513 + rrdeng_store_metric_create_new_page(handle, ctx, point_in_time_ut, handle->data, handle->data_size);
514 // handle->position is set to 1 already
551 -
552 - if (0 == handle->alignment->page_position) {
553 - /* this is the leading dimension that defines chart alignment */
554 - perfect_page_alignment = true;
555 - }
515 }
516 else {
517 // update an existing page
@@ -566,11 +525,12 @@ static void rrdeng_store_metric_append_point(STORAGE_COLLECT_HANDLE *collection_
525 }
526 }
527
569 - if (perfect_page_alignment)
570 - handle->alignment->page_position = handle->page_position;
528 + timing_step(TIMING_STEP_DBENGINE_PAGE_FIN);
529
530 // update the metric information
531 mrg_metric_set_hot_latest_time_s(main_mrg, handle->metric, (time_t) (point_in_time_ut / USEC_PER_SEC));
532 +
533 + timing_step(TIMING_STEP_DBENGINE_MRG_UPDATE);
534 }
535
536 static void store_metric_next_error_log(struct rrdeng_collect_handle *handle, usec_t point_in_time_ut, const char *msg) {
@@ -612,6 +572,8 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle,
572 const uint16_t anomaly_count,
573 const SN_FLAGS flags)
574 {
575 + timing_step(TIMING_STEP_RRDSET_STORE_METRIC);
576 +
577 struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle;
578
579 #ifdef NETDATA_INTERNAL_CHECKS
@@ -619,59 +581,62 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle,
581 handle->page_flags |= RRDENG_PAGE_FUTURE_POINT;
582 #endif
583
622 - if(likely(handle->page_end_time_ut + handle->update_every_ut == point_in_time_ut)) {
584 + usec_t delta_ut = point_in_time_ut - handle->page_end_time_ut;
585 +
586 + if(likely(delta_ut == handle->update_every_ut)) {
587 // happy path
588 ;
589 }
590 + else if(unlikely(point_in_time_ut > handle->page_end_time_ut)) {
591 + if(handle->page) {
592 + if (unlikely(delta_ut < handle->update_every_ut)) {
593 + handle->page_flags |= RRDENG_PAGE_STEP_TOO_SMALL;
594 + rrdeng_store_metric_flush_current_page(collection_handle);
595 + }
596 + else if (unlikely(delta_ut % handle->update_every_ut)) {
597 + handle->page_flags |= RRDENG_PAGE_STEP_UNALIGNED;
598 + rrdeng_store_metric_flush_current_page(collection_handle);
599 + }
600 + else {
601 + size_t points_gap = delta_ut / handle->update_every_ut;
602 + size_t page_remaining_points = handle->page_entries_max - handle->page_position;
603 +
604 + if (points_gap >= page_remaining_points) {
605 + handle->page_flags |= RRDENG_PAGE_BIG_GAP;
606 + rrdeng_store_metric_flush_current_page(collection_handle);
607 + }
608 + else {
609 + // loop to fill the gap
610 + handle->page_flags |= RRDENG_PAGE_GAP;
611 +
612 + usec_t stop_ut = point_in_time_ut - handle->update_every_ut;
613 + for (usec_t this_ut = handle->page_end_time_ut + handle->update_every_ut;
614 + this_ut <= stop_ut;
615 + this_ut = handle->page_end_time_ut + handle->update_every_ut) {
616 + rrdeng_store_metric_append_point(
617 + collection_handle,
618 + this_ut,
619 + NAN, NAN, NAN,
620 + 1, 0,
621 + SN_EMPTY_SLOT);
622 + }
623 + }
624 + }
625 + }
626 + }
627 else if(unlikely(point_in_time_ut < handle->page_end_time_ut)) {
628 handle->page_flags |= RRDENG_PAGE_PAST_COLLECTION;
629 store_metric_next_error_log(handle, point_in_time_ut, "is older than the");
630 return;
631 }
632
632 - else if(unlikely(point_in_time_ut == handle->page_end_time_ut)) {
633 + else /* if(unlikely(point_in_time_ut == handle->page_end_time_ut)) */ {
634 handle->page_flags |= RRDENG_PAGE_REPEATED_COLLECTION;
635 store_metric_next_error_log(handle, point_in_time_ut, "is at the same time as the");
636 return;
637 }
638
638 - else if(handle->page) {
639 - usec_t delta_ut = point_in_time_ut - handle->page_end_time_ut;
640 -
641 - if(unlikely(delta_ut < handle->update_every_ut)) {
642 - handle->page_flags |= RRDENG_PAGE_STEP_TOO_SMALL;
643 - rrdeng_store_metric_flush_current_page(collection_handle);
644 - }
645 - else if(unlikely(delta_ut % handle->update_every_ut)) {
646 - handle->page_flags |= RRDENG_PAGE_STEP_UNALIGNED;
647 - rrdeng_store_metric_flush_current_page(collection_handle);
648 - }
649 - else {
650 - size_t points_gap = delta_ut / handle->update_every_ut;
651 - size_t page_remaining_points = handle->page_entries_max - handle->page_position;
652 -
653 - if(points_gap >= page_remaining_points) {
654 - handle->page_flags |= RRDENG_PAGE_BIG_GAP;
655 - rrdeng_store_metric_flush_current_page(collection_handle);
656 - }
657 - else {
658 - // loop to fill the gap
659 - handle->page_flags |= RRDENG_PAGE_GAP;
660 -
661 - usec_t stop_ut = point_in_time_ut - handle->update_every_ut;
662 - for(usec_t this_ut = handle->page_end_time_ut + handle->update_every_ut;
663 - this_ut <= stop_ut ;
664 - this_ut = handle->page_end_time_ut + handle->update_every_ut) {
665 - rrdeng_store_metric_append_point(
666 - collection_handle,
667 - this_ut,
668 - NAN, NAN, NAN,
669 - 1, 0,
670 - SN_EMPTY_SLOT);
671 - }
672 - }
673 - }
674 - }
639 + timing_step(TIMING_STEP_DBENGINE_FIRST_CHECK);
640
641 rrdeng_store_metric_append_point(collection_handle,
642 point_in_time_ut,
database/rrd.h
+7
@@ -702,6 +702,13 @@ struct rrdset {
702 RRDCALC *base; // double linked list of RRDCALC related to this RRDSET
703 } alerts;
704
705 + struct {
706 + size_t pos;
707 + size_t size;
708 + size_t used;
709 + RRDDIM_ACQUIRED **rda;
710 + } pluginsd;
711 +
712 #ifdef NETDATA_LOG_REPLICATION_REQUESTS
713 struct {
714 bool log_next_data_collection;
database/rrdset.c
+30 -14
@@ -184,6 +184,8 @@ static void rrdset_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
184 ml_chart_new(st);
185 }
186
187 +void pluginsd_rrdset_cleanup(RRDSET *st);
188 +
189 void rrdset_finalize_collection(RRDSET *st, bool dimensions_too) {
190 RRDHOST *host = st->rrdhost;
191
@@ -205,6 +207,8 @@ void rrdset_finalize_collection(RRDSET *st, bool dimensions_too) {
207 st->storage_metrics_groups[tier] = NULL;
208 }
209 }
210 +
211 + pluginsd_rrdset_cleanup(st);
212 }
213
214 // the destructor - the dictionary is write locked while this runs
@@ -1229,6 +1233,8 @@ void rrddim_store_metric(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n,
1233
1234 store_metric_at_tier(rd, tier, t, sp, point_end_time_ut);
1235 }
1236 +
1237 + rrdcontext_collected_rrddim(rd);
1238 }
1239
1240 void store_metric_collection_completed() {
@@ -1269,7 +1275,8 @@ void rrdset_thread_rda_free(void) {
1275 }
1276
1277 static inline size_t rrdset_done_interpolate(
1272 - RRDSET *st
1278 + RRDSET_STREAM_BUFFER *rsb
1279 + , RRDSET *st
1280 , struct rda_item *rda_base
1281 , size_t rda_slots
1282 , usec_t update_every_ut
@@ -1401,8 +1408,10 @@ static inline size_t rrdset_done_interpolate(
1408 if(unlikely(!store_this_entry)) {
1409 (void) ml_is_anomalous(rd, current_time_s, 0, false);
1410
1411 + if(rsb->wb && rsb->v2)
1412 + rrddim_push_metrics_v2(rsb, rd, next_store_ut, NAN, SN_FLAG_NONE);
1413 +
1414 rrddim_store_metric(rd, next_store_ut, NAN, SN_FLAG_NONE);
1405 - rrdcontext_collected_rrddim(rd);
1415 continue;
1416 }
1417
@@ -1414,8 +1423,10 @@ static inline size_t rrdset_done_interpolate(
1423 dim_storage_flags &= ~((storage_number)SN_FLAG_NOT_ANOMALOUS);
1424 }
1425
1426 + if(rsb->wb && rsb->v2)
1427 + rrddim_push_metrics_v2(rsb, rd, next_store_ut, new_value, dim_storage_flags);
1428 +
1429 rrddim_store_metric(rd, next_store_ut, new_value, dim_storage_flags);
1418 - rrdcontext_collected_rrddim(rd);
1430 rd->last_stored_value = new_value;
1431 }
1432 else {
@@ -1423,8 +1434,10 @@ static inline size_t rrdset_done_interpolate(
1434
1435 rrdset_debug(st, "%s: STORE[%ld] = NON EXISTING ", rrddim_name(rd), current_entry);
1436
1437 + if(rsb->wb && rsb->v2)
1438 + rrddim_push_metrics_v2(rsb, rd, next_store_ut, NAN, SN_FLAG_NONE);
1439 +
1440 rrddim_store_metric(rd, next_store_ut, NAN, SN_FLAG_NONE);
1427 - rrdcontext_collected_rrddim(rd);
1441 rd->last_stored_value = NAN;
1442 }
1443
@@ -1468,6 +1481,10 @@ void rrdset_done(RRDSET *st) {
1481 void rrdset_timed_done(RRDSET *st, struct timeval now, bool pending_rrdset_next) {
1482 if(unlikely(!service_running(SERVICE_COLLECTORS))) return;
1483
1484 + RRDSET_STREAM_BUFFER stream_buffer = { .wb = NULL, };
1485 + if(unlikely(rrdhost_has_rrdpush_sender_enabled(st->rrdhost)))
1486 + stream_buffer = rrdset_push_metric_initialize(st, now.tv_sec);
1487 +
1488 netdata_spinlock_lock(&st->data_collection_lock);
1489
1490 if (pending_rrdset_next)
@@ -1489,10 +1506,10 @@ void rrdset_timed_done(RRDSET *st, struct timeval now, bool pending_rrdset_next)
1506 update_every_ut = st->update_every * USEC_PER_SEC; // st->update_every in microseconds
1507
1508 RRDSET_FLAGS rrdset_flags = rrdset_flag_check(st, ~0);
1492 - if(unlikely(rrdset_flags & RRDSET_FLAG_COLLECTION_FINISHED))
1509 + if(unlikely(rrdset_flags & RRDSET_FLAG_COLLECTION_FINISHED)) {
1510 + netdata_spinlock_unlock(&st->data_collection_lock);
1511 return;
1494 -
1495 - netdata_thread_disable_cancelability();
1512 + }
1513
1514 if (unlikely(rrdset_flags & RRDSET_FLAG_OBSOLETE)) {
1515 error("Chart '%s' has the OBSOLETE flag set, but it is collected.", rrdset_id(st));
@@ -1500,8 +1517,7 @@ void rrdset_timed_done(RRDSET *st, struct timeval now, bool pending_rrdset_next)
1517 }
1518
1519 // check if the chart has a long time to be updated
1503 - if(unlikely(st->usec_since_last_update > st->entries * update_every_ut &&
1504 - st->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE && st->rrd_memory_mode != RRD_MEMORY_MODE_NONE)) {
1520 + if(unlikely(st->usec_since_last_update > MAX(st->entries, 60) * update_every_ut)) {
1521 info("host '%s', chart '%s': took too long to be updated (counter #%zu, update #%zu, %0.3" NETDATA_DOUBLE_MODIFIER
1522 " secs). Resetting it.", rrdhost_hostname(st->rrdhost), rrdset_id(st), st->counter, st->counter_done, (NETDATA_DOUBLE)st->usec_since_last_update / USEC_PER_SEC);
1523 rrdset_reset(st);
@@ -1595,8 +1611,8 @@ void rrdset_timed_done(RRDSET *st, struct timeval now, bool pending_rrdset_next)
1611 after_first_database_work:
1612 st->counter_done++;
1613
1598 - if(unlikely(rrdhost_has_rrdpush_sender_enabled(st->rrdhost)))
1599 - rrdset_done_push(st);
1614 + if(stream_buffer.wb && !stream_buffer.v2)
1615 + rrdset_push_metrics_v1(&stream_buffer, st);
1616
1617 uint32_t has_reset_value = 0;
1618
@@ -1857,7 +1873,8 @@ after_first_database_work:
1873 // #endif
1874
1875 rrdset_done_interpolate(
1860 - st
1876 + &stream_buffer
1877 + , st
1878 , rda_base
1879 , rda_slots
1880 , update_every_ut
@@ -1928,6 +1945,7 @@ after_second_database_work:
1945 }
1946
1947 netdata_spinlock_unlock(&st->data_collection_lock);
1948 + rrdset_push_metrics_finished(&stream_buffer, st);
1949
1950 // ALL DONE ABOUT THE DATA UPDATE
1951 // --------------------------------------------------------------------
@@ -1955,8 +1973,6 @@ after_second_database_work:
1973
1974 rrdcontext_collected_rrdset(st);
1975
1958 - netdata_thread_enable_cancelability();
1959 -
1976 store_metric_collection_completed();
1977 }
1978
libnetdata/buffer/buffer.c
+71 -17
@@ -108,21 +108,44 @@ void buffer_print_llu(BUFFER *wb, unsigned long long uvalue)
108 {
109 buffer_need_bytes(wb, 50);
110
111 + switch(uvalue) {
112 + case 0:
113 + buffer_fast_strcat(wb, "0", 1);
114 + return;
115 +
116 + case 1:
117 + buffer_fast_strcat(wb, "1", 1);
118 + return;
119 +
120 + case 5:
121 + buffer_fast_strcat(wb, "5", 1);
122 + return;
123 +
124 + case 10:
125 + buffer_fast_strcat(wb, "10", 2);
126 + return;
127 +
128 + default:
129 + break;
130 + }
131 +
132 char *str = &wb->buffer[wb->len];
133 char *wstr = str;
134
135 switch (sizeof(void *)) {
115 - case 4:
116 - wstr = (uvalue > (unsigned long long) 0xffffffff) ? print_number_llu_r(wstr, uvalue) :
117 - print_number_lu_r(wstr, uvalue);
118 - break;
119 - case 8:
120 - do {
121 - *wstr++ = (char) ('0' + (uvalue % 10));
122 - } while (uvalue /= 10);
123 - break;
124 - default:
125 - fatal("Netdata supports only 32-bit & 64-bit systems.");
136 + case 8:
137 + do {
138 + *wstr++ = (char) ('0' + (uvalue % 10));
139 + } while (uvalue /= 10);
140 + break;
141 +
142 + case 4:
143 + wstr = (uvalue > (unsigned long long) 0xffffffff) ? print_number_llu_r(wstr, uvalue) :
144 + print_number_lu_r(wstr, uvalue);
145 + break;
146 +
147 + default:
148 + fatal("Netdata supports only 32-bit & 64-bit systems.");
149 }
150
151 // terminate it
@@ -132,8 +155,9 @@ void buffer_print_llu(BUFFER *wb, unsigned long long uvalue)
155 char *begin = str, *end = wstr - 1, aux;
156 while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
157
158 + size_t len = wstr - str;
159 // return the buffer length
136 - wb->len += wstr - str;
160 + wb->len += len;
161 }
162
163 void buffer_print_ll(BUFFER *wb, long long value)
@@ -167,7 +191,7 @@ static unsigned char bits03_to_hex[16] = {
191 [15] = 'F'
192 };
193
170 -void buffer_print_llu_hex(BUFFER *wb, unsigned long long value)
194 +inline void buffer_print_llu_hex(BUFFER *wb, unsigned long long value)
195 {
196 unsigned char buffer[sizeof(unsigned long long) * 2 + 2 + 1]; // 8 bytes * 2 + '0x' + '\0'
197 unsigned char *e = &buffer[sizeof(unsigned long long) * 2 + 2];
@@ -196,7 +220,16 @@ void buffer_print_llu_hex(BUFFER *wb, unsigned long long value)
220 buffer_fast_strcat(wb, (char *)p, e - p);
221 }
222
199 -void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
223 +void buffer_print_ll_hex(BUFFER *wb, long long value) {
224 + if(value < 0) {
225 + buffer_fast_strcat(wb, "-", 1);
226 + value = -value;
227 + }
228 +
229 + buffer_print_llu_hex(wb, value);
230 +}
231 +
232 +inline void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
233 if(unlikely(!txt || !*txt)) return;
234
235 buffer_need_bytes(wb, len + 1);
@@ -214,6 +247,27 @@ void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
247 wb->buffer[wb->len] = '\0';
248 }
249
250 +void buffer_print_sn_flags(BUFFER *wb, SN_FLAGS flags, bool send_anomaly_bit) {
251 + if(unlikely(flags == SN_EMPTY_SLOT)) {
252 + buffer_fast_strcat(wb, "E", 1);
253 + return;
254 + }
255 +
256 + size_t printed = 0;
257 + if(likely(send_anomaly_bit && (flags & SN_FLAG_NOT_ANOMALOUS))) {
258 + buffer_fast_strcat(wb, "A", 1);
259 + printed++;
260 + }
261 +
262 + if(unlikely(flags & SN_FLAG_RESET)) {
263 + buffer_fast_strcat(wb, "R", 1);
264 + printed++;
265 + }
266 +
267 + if(!printed)
268 + buffer_fast_strcat(wb, "''", 2);
269 +}
270 +
271 void buffer_strcat(BUFFER *wb, const char *txt)
272 {
273 // buffer_sprintf(wb, "%s", txt);
@@ -234,7 +288,7 @@ void buffer_strcat(BUFFER *wb, const char *txt)
288 wb->len = len;
289 buffer_overflow_check(wb);
290
237 - if(*txt) {
291 + if(unlikely(*txt)) {
292 debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
293 len = strlen(txt);
294 buffer_fast_strcat(wb, txt, len);
@@ -242,7 +296,7 @@ void buffer_strcat(BUFFER *wb, const char *txt)
296 else {
297 // terminate the string
298 // without increasing the length
245 - buffer_need_bytes(wb, (size_t)1);
299 + buffer_need_bytes(wb, 1);
300 wb->buffer[wb->len] = '\0';
301 }
302 }
@@ -361,7 +415,7 @@ void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
415
416 void buffer_rrd_value(BUFFER *wb, NETDATA_DOUBLE value)
417 {
364 - buffer_need_bytes(wb, 50);
418 + buffer_need_bytes(wb, 512);
419
420 if(isnan(value) || isinf(value)) {
421 buffer_strcat(wb, "null");
libnetdata/buffer/buffer.h
+3
@@ -74,6 +74,8 @@ void buffer_strcat_htmlescape(BUFFER *wb, const char *txt);
74
75 void buffer_char_replace(BUFFER *wb, char from, char to);
76
77 +void buffer_print_sn_flags(BUFFER *wb, SN_FLAGS flags, bool send_anomaly_bit);
78 +
79 char *print_number_lu_r(char *str, unsigned long uvalue);
80 char *print_number_llu_r(char *str, unsigned long long uvalue);
81 char *print_number_llu_r_smart(char *str, unsigned long long uvalue);
@@ -81,6 +83,7 @@ char *print_number_llu_r_smart(char *str, unsigned long long uvalue);
83 void buffer_print_llu(BUFFER *wb, unsigned long long uvalue);
84 void buffer_print_ll(BUFFER *wb, long long value);
85 void buffer_print_llu_hex(BUFFER *wb, unsigned long long value);
86 +void buffer_print_ll_hex(BUFFER *wb, long long value);
87
88 static inline void buffer_need_bytes(BUFFER *buffer, size_t needed_free_size) {
89 if(unlikely(buffer->size - buffer->len < needed_free_size))
libnetdata/dictionary/dictionary.c
+113 -130
@@ -10,9 +10,9 @@ typedef enum __attribute__ ((__packed__)) {
10 DICT_FLAG_DESTROYED = (1 << 0), // this dictionary has been destroyed
11 } DICT_FLAGS;
12
13 -#define dict_flag_check(dict, flag) (__atomic_load_n(&((dict)->flags), __ATOMIC_SEQ_CST) & (flag))
14 -#define dict_flag_set(dict, flag) __atomic_or_fetch(&((dict)->flags), flag, __ATOMIC_SEQ_CST)
15 -#define dict_flag_clear(dict, flag) __atomic_and_fetch(&((dict)->flags), ~(flag), __ATOMIC_SEQ_CST)
13 +#define dict_flag_check(dict, flag) (__atomic_load_n(&((dict)->flags), __ATOMIC_RELAXED) & (flag))
14 +#define dict_flag_set(dict, flag) __atomic_or_fetch(&((dict)->flags), flag, __ATOMIC_RELAXED)
15 +#define dict_flag_clear(dict, flag) __atomic_and_fetch(&((dict)->flags), ~(flag), __ATOMIC_RELAXED)
16
17 // flags macros
18 #define is_dictionary_destroyed(dict) dict_flag_check(dict, DICT_FLAG_DESTROYED)
@@ -37,13 +37,13 @@ typedef enum __attribute__ ((__packed__)) item_flags {
37 // IMPORTANT: This is 8-bit
38 } ITEM_FLAGS;
39
40 -#define item_flag_check(item, flag) (__atomic_load_n(&((item)->flags), __ATOMIC_SEQ_CST) & (flag))
41 -#define item_flag_set(item, flag) __atomic_or_fetch(&((item)->flags), flag, __ATOMIC_SEQ_CST)
42 -#define item_flag_clear(item, flag) __atomic_and_fetch(&((item)->flags), ~(flag), __ATOMIC_SEQ_CST)
40 +#define item_flag_check(item, flag) (__atomic_load_n(&((item)->flags), __ATOMIC_RELAXED) & (flag))
41 +#define item_flag_set(item, flag) __atomic_or_fetch(&((item)->flags), flag, __ATOMIC_RELAXED)
42 +#define item_flag_clear(item, flag) __atomic_and_fetch(&((item)->flags), ~(flag), __ATOMIC_RELAXED)
43
44 -#define item_shared_flag_check(item, flag) (__atomic_load_n(&((item)->shared->flags), __ATOMIC_SEQ_CST) & (flag))
45 -#define item_shared_flag_set(item, flag) __atomic_or_fetch(&((item)->shared->flags), flag, __ATOMIC_SEQ_CST)
46 -#define item_shared_flag_clear(item, flag) __atomic_and_fetch(&((item)->shared->flags), ~(flag), __ATOMIC_SEQ_CST)
44 +#define item_shared_flag_check(item, flag) (__atomic_load_n(&((item)->shared->flags), __ATOMIC_RELAXED) & (flag))
45 +#define item_shared_flag_set(item, flag) __atomic_or_fetch(&((item)->shared->flags), flag, __ATOMIC_RELAXED)
46 +#define item_shared_flag_clear(item, flag) __atomic_and_fetch(&((item)->shared->flags), ~(flag), __ATOMIC_RELAXED)
47
48 #define REFCOUNT_DELETING (-100)
49
@@ -175,7 +175,7 @@ struct dictionary {
175 long int referenced_items; // how many items of the dictionary are currently being used by 3rd parties
176 long int pending_deletion_items; // how many items of the dictionary have been deleted, but have not been removed yet
177
178 -#ifdef NETDATA_INTERNAL_CHECKS
178 +#ifdef NETDATA_DICTIONARY_VALIDATE_POINTERS
179 netdata_mutex_t global_pointer_registry_mutex;
180 Pvoid_t global_pointer_registry;
181 #endif
@@ -205,59 +205,47 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY
205 // ----------------------------------------------------------------------------
206 // validate each pointer is indexed once - internal checks only
207
208 +#ifdef NETDATA_DICTIONARY_VALIDATE_POINTERS
209 static inline void pointer_index_init(DICTIONARY *dict __maybe_unused) {
209 -#ifdef NETDATA_INTERNAL_CHECKS
210 netdata_mutex_init(&dict->global_pointer_registry_mutex);
211 -#else
212 - ;
213 -#endif
211 }
212
213 static inline void pointer_destroy_index(DICTIONARY *dict __maybe_unused) {
217 -#ifdef NETDATA_INTERNAL_CHECKS
214 netdata_mutex_lock(&dict->global_pointer_registry_mutex);
215 JudyHSFreeArray(&dict->global_pointer_registry, PJE0);
216 netdata_mutex_unlock(&dict->global_pointer_registry_mutex);
221 -#else
222 - ;
223 -#endif
217 }
218 static inline void pointer_add(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) {
226 -#ifdef NETDATA_INTERNAL_CHECKS
219 netdata_mutex_lock(&dict->global_pointer_registry_mutex);
220 Pvoid_t *PValue = JudyHSIns(&dict->global_pointer_registry, &item, sizeof(void *), PJE0);
221 if(*PValue != NULL)
222 fatal("pointer already exists in registry");
223 *PValue = item;
224 netdata_mutex_unlock(&dict->global_pointer_registry_mutex);
233 -#else
234 - ;
235 -#endif
225 }
226
227 static inline void pointer_check(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) {
239 -#ifdef NETDATA_INTERNAL_CHECKS
228 netdata_mutex_lock(&dict->global_pointer_registry_mutex);
229 Pvoid_t *PValue = JudyHSGet(dict->global_pointer_registry, &item, sizeof(void *));
230 if(PValue == NULL)
231 fatal("pointer is not found in registry");
232 netdata_mutex_unlock(&dict->global_pointer_registry_mutex);
245 -#else
246 - ;
247 -#endif
233 }
234
235 static inline void pointer_del(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) {
251 -#ifdef NETDATA_INTERNAL_CHECKS
236 netdata_mutex_lock(&dict->global_pointer_registry_mutex);
237 int ret = JudyHSDel(&dict->global_pointer_registry, &item, sizeof(void *), PJE0);
238 if(!ret)
239 fatal("pointer to be deleted does not exist in registry");
240 netdata_mutex_unlock(&dict->global_pointer_registry_mutex);
257 -#else
258 - ;
259 -#endif
241 }
242 +#else // !NETDATA_DICTIONARY_VALIDATE_POINTERS
243 +#define pointer_index_init(dict) debug_dummy()
244 +#define pointer_destroy_index(dict) debug_dummy()
245 +#define pointer_add(dict, item) debug_dummy()
246 +#define pointer_check(dict, item) debug_dummy()
247 +#define pointer_del(dict, item) debug_dummy()
248 +#endif // !NETDATA_DICTIONARY_VALIDATE_POINTERS
249
250 // ----------------------------------------------------------------------------
251 // memory statistics
@@ -298,7 +286,7 @@ static inline void dictionary_hooks_allocate(DICTIONARY *dict) {
286 static inline size_t dictionary_hooks_free(DICTIONARY *dict) {
287 if(!dict->hooks) return 0;
288
301 - REFCOUNT links = __atomic_sub_fetch(&dict->hooks->links, 1, __ATOMIC_SEQ_CST);
289 + REFCOUNT links = __atomic_sub_fetch(&dict->hooks->links, 1, __ATOMIC_ACQUIRE);
290 if(links == 0) {
291 freez(dict->hooks);
292 dict->hooks = NULL;
@@ -358,7 +346,7 @@ size_t dictionary_version(DICTIONARY *dict) {
346 // this is required for views to return the right number
347 garbage_collect_pending_deletes(dict);
348
361 - return __atomic_load_n(&dict->version, __ATOMIC_SEQ_CST);
349 + return __atomic_load_n(&dict->version, __ATOMIC_RELAXED);
350 }
351 size_t dictionary_entries(DICTIONARY *dict) {
352 if(unlikely(!dict)) return 0;
@@ -366,7 +354,7 @@ size_t dictionary_entries(DICTIONARY *dict) {
354 // this is required for views to return the right number
355 garbage_collect_pending_deletes(dict);
356
369 - long int entries = __atomic_load_n(&dict->entries, __ATOMIC_SEQ_CST);
357 + long int entries = __atomic_load_n(&dict->entries, __ATOMIC_RELAXED);
358 if(entries < 0)
359 fatal("DICTIONARY: entries is negative: %ld", entries);
360
@@ -375,7 +363,7 @@ size_t dictionary_entries(DICTIONARY *dict) {
363 size_t dictionary_referenced_items(DICTIONARY *dict) {
364 if(unlikely(!dict)) return 0;
365
378 - long int referenced_items = __atomic_load_n(&dict->referenced_items, __ATOMIC_SEQ_CST);
366 + long int referenced_items = __atomic_load_n(&dict->referenced_items, __ATOMIC_RELAXED);
367 if(referenced_items < 0)
368 fatal("DICTIONARY: referenced items is negative: %ld", referenced_items);
369
@@ -387,7 +375,7 @@ long int dictionary_stats_for_registry(DICTIONARY *dict) {
375 return (dict->stats->memory.index + dict->stats->memory.dict);
376 }
377 void dictionary_version_increment(DICTIONARY *dict) {
390 - __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST);
378 + __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
379 }
380
381 // ----------------------------------------------------------------------------
@@ -409,9 +397,9 @@ static inline void DICTIONARY_ENTRIES_PLUS1(DICTIONARY *dict) {
397
398 }
399 else {
412 - __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST);
413 - __atomic_fetch_add(&dict->entries, 1, __ATOMIC_SEQ_CST);
414 - __atomic_fetch_add(&dict->referenced_items, 1, __ATOMIC_SEQ_CST);
400 + __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
401 + __atomic_fetch_add(&dict->entries, 1, __ATOMIC_RELAXED);
402 + __atomic_fetch_add(&dict->referenced_items, 1, __ATOMIC_RELAXED);
403 }
404 }
405 static inline void DICTIONARY_ENTRIES_MINUS1(DICTIONARY *dict) {
@@ -425,17 +413,15 @@ static inline void DICTIONARY_ENTRIES_MINUS1(DICTIONARY *dict) {
413 entries = dict->entries++;
414 }
415 else {
428 - __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST);
429 - entries = __atomic_fetch_sub(&dict->entries, 1, __ATOMIC_SEQ_CST);
416 + __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
417 + entries = __atomic_fetch_sub(&dict->entries, 1, __ATOMIC_RELAXED);
418 }
419
432 -#ifdef NETDATA_INTERNAL_CHECKS
433 - if(unlikely(entries == 0))
434 - fatal("DICT: negative number of entries in dictionary created from %s() (%zu@%s)",
435 - dict->creation_function,
436 - dict->creation_line,
437 - dict->creation_file);
438 -#endif
420 + internal_fatal(entries == 0,
421 + "DICT: negative number of entries in dictionary created from %s() (%zu@%s)",
422 + dict->creation_function,
423 + dict->creation_line,
424 + dict->creation_file);
425 }
426 static inline void DICTIONARY_VALUE_RESETS_PLUS1(DICTIONARY *dict) {
427 __atomic_fetch_add(&dict->stats->ops.resets, 1, __ATOMIC_RELAXED);
@@ -443,7 +429,7 @@ static inline void DICTIONARY_VALUE_RESETS_PLUS1(DICTIONARY *dict) {
429 if(unlikely(is_dictionary_single_threaded(dict)))
430 dict->version++;
431 else
446 - __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST);
432 + __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
433 }
434 static inline void DICTIONARY_STATS_TRAVERSALS_PLUS1(DICTIONARY *dict) {
435 __atomic_fetch_add(&dict->stats->ops.traversals, 1, __ATOMIC_RELAXED);
@@ -464,16 +450,16 @@ static inline void DICTIONARY_STATS_SEARCH_IGNORES_PLUS1(DICTIONARY *dict) {
450 __atomic_fetch_add(&dict->stats->spin_locks.search_spins, 1, __ATOMIC_RELAXED);
451 }
452 static inline void DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(DICTIONARY *dict) {
467 - __atomic_fetch_add(&dict->stats->callbacks.inserts, 1, __ATOMIC_RELAXED);
453 + __atomic_fetch_add(&dict->stats->callbacks.inserts, 1, __ATOMIC_RELEASE);
454 }
455 static inline void DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(DICTIONARY *dict) {
470 - __atomic_fetch_add(&dict->stats->callbacks.conflicts, 1, __ATOMIC_RELAXED);
456 + __atomic_fetch_add(&dict->stats->callbacks.conflicts, 1, __ATOMIC_RELEASE);
457 }
458 static inline void DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(DICTIONARY *dict) {
473 - __atomic_fetch_add(&dict->stats->callbacks.reacts, 1, __ATOMIC_RELAXED);
459 + __atomic_fetch_add(&dict->stats->callbacks.reacts, 1, __ATOMIC_RELEASE);
460 }
461 static inline void DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(DICTIONARY *dict) {
476 - __atomic_fetch_add(&dict->stats->callbacks.deletes, 1, __ATOMIC_RELAXED);
462 + __atomic_fetch_add(&dict->stats->callbacks.deletes, 1, __ATOMIC_RELEASE);
463 }
464 static inline void DICTIONARY_STATS_GARBAGE_COLLECTIONS_PLUS1(DICTIONARY *dict) {
465 __atomic_fetch_add(&dict->stats->ops.garbage_collections, 1, __ATOMIC_RELAXED);
@@ -496,52 +482,48 @@ static inline void DICTIONARY_STATS_DICT_FLUSHES_PLUS1(DICTIONARY *dict) {
482 __atomic_fetch_add(&dict->stats->ops.flushes, 1, __ATOMIC_RELAXED);
483 }
484
499 -static inline long int DICTIONARY_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) {
485 +static inline void DICTIONARY_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) {
486 __atomic_fetch_add(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED);
487
488 if(unlikely(is_dictionary_single_threaded(dict)))
503 - return ++dict->referenced_items;
489 + ++dict->referenced_items;
490 else
505 - return __atomic_add_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST);
491 + __atomic_add_fetch(&dict->referenced_items, 1, __ATOMIC_RELAXED);
492 }
493
508 -static inline long int DICTIONARY_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) {
494 +static inline void DICTIONARY_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) {
495 __atomic_fetch_sub(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED);
496
511 - long int referenced_items;
497 + long int referenced_items; (void)referenced_items;
498 if(unlikely(is_dictionary_single_threaded(dict)))
499 referenced_items = --dict->referenced_items;
500 else
501 referenced_items = __atomic_sub_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST);
502
517 -#ifdef NETDATA_INTERNAL_CHECKS
518 - if(unlikely(referenced_items < 0))
519 - fatal("DICT: negative number of referenced items (%ld) in dictionary created from %s() (%zu@%s)",
520 - referenced_items,
521 - dict->creation_function,
522 - dict->creation_line,
523 - dict->creation_file);
524 -#endif
525 -
526 - return referenced_items;
503 + internal_fatal(referenced_items < 0,
504 + "DICT: negative number of referenced items (%ld) in dictionary created from %s() (%zu@%s)",
505 + referenced_items,
506 + dict->creation_function,
507 + dict->creation_line,
508 + dict->creation_file);
509 }
510
529 -static inline long int DICTIONARY_PENDING_DELETES_PLUS1(DICTIONARY *dict) {
511 +static inline void DICTIONARY_PENDING_DELETES_PLUS1(DICTIONARY *dict) {
512 __atomic_fetch_add(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELAXED);
513
514 if(unlikely(is_dictionary_single_threaded(dict)))
533 - return ++dict->pending_deletion_items;
515 + ++dict->pending_deletion_items;
516 else
535 - return __atomic_add_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST);
517 + __atomic_add_fetch(&dict->pending_deletion_items, 1, __ATOMIC_RELEASE);
518 }
519
520 static inline long int DICTIONARY_PENDING_DELETES_MINUS1(DICTIONARY *dict) {
539 - __atomic_fetch_sub(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELAXED);
521 + __atomic_fetch_sub(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELEASE);
522
523 if(unlikely(is_dictionary_single_threaded(dict)))
524 return --dict->pending_deletion_items;
525 else
544 - return __atomic_sub_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST);
526 + return __atomic_sub_fetch(&dict->pending_deletion_items, 1, __ATOMIC_ACQUIRE);
527 }
528
529 static inline long int DICTIONARY_PENDING_DELETES_GET(DICTIONARY *dict) {
@@ -555,11 +537,11 @@ static inline REFCOUNT DICTIONARY_ITEM_REFCOUNT_GET(DICTIONARY *dict, DICTIONARY
537 if(unlikely(dict && is_dictionary_single_threaded(dict))) // this is an exception, dict can be null
538 return item->refcount;
539 else
558 - return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_SEQ_CST);
540 + return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_ACQUIRE);
541 }
542
543 static inline REFCOUNT DICTIONARY_ITEM_REFCOUNT_GET_SOLE(DICTIONARY_ITEM *item) {
562 - return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_SEQ_CST);
544 + return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_ACQUIRE);
545 }
546
547 // ----------------------------------------------------------------------------
@@ -579,8 +561,8 @@ static void dictionary_execute_insert_callback(DICTIONARY *dict, DICTIONARY_ITEM
561 dict->creation_line,
562 dict->creation_file);
563
582 - DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(dict);
564 dict->hooks->ins_callback(item, item->shared->value, constructor_data?constructor_data:dict->hooks->ins_callback_data);
565 + DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(dict);
566 }
567
568 static bool dictionary_execute_conflict_callback(DICTIONARY *dict, DICTIONARY_ITEM *item, void *new_value, void *constructor_data) {
@@ -597,10 +579,13 @@ static bool dictionary_execute_conflict_callback(DICTIONARY *dict, DICTIONARY_IT
579 dict->creation_line,
580 dict->creation_file);
581
600 - DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(dict);
601 - return dict->hooks->conflict_callback(
582 + bool ret = dict->hooks->conflict_callback(
583 item, item->shared->value, new_value,
584 constructor_data ? constructor_data : dict->hooks->conflict_callback_data);
585 +
586 + DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(dict);
587 +
588 + return ret;
589 }
590
591 static void dictionary_execute_react_callback(DICTIONARY *dict, DICTIONARY_ITEM *item, void *constructor_data) {
@@ -617,9 +602,10 @@ static void dictionary_execute_react_callback(DICTIONARY *dict, DICTIONARY_ITEM
602 dict->creation_line,
603 dict->creation_file);
604
620 - DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(dict);
605 dict->hooks->react_callback(item, item->shared->value,
606 constructor_data?constructor_data:dict->hooks->react_callback_data);
607 +
608 + DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(dict);
609 }
610
611 static void dictionary_execute_delete_callback(DICTIONARY *dict, DICTIONARY_ITEM *item) {
@@ -637,8 +623,9 @@ static void dictionary_execute_delete_callback(DICTIONARY *dict, DICTIONARY_ITEM
623 dict->creation_line,
624 dict->creation_file);
625
640 - DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(dict);
626 dict->hooks->del_callback(item, item->shared->value, dict->hooks->del_callback_data);
627 +
628 + DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(dict);
629 }
630
631 // ----------------------------------------------------------------------------
@@ -648,8 +635,8 @@ static inline size_t dictionary_locks_init(DICTIONARY *dict) {
635 if(likely(!is_dictionary_single_threaded(dict))) {
636 netdata_rwlock_init(&dict->index.rwlock);
637 netdata_rwlock_init(&dict->items.rwlock);
651 - return 0;
638 }
639 +
640 return 0;
641 }
642
@@ -657,29 +644,29 @@ static inline size_t dictionary_locks_destroy(DICTIONARY *dict) {
644 if(likely(!is_dictionary_single_threaded(dict))) {
645 netdata_rwlock_destroy(&dict->index.rwlock);
646 netdata_rwlock_destroy(&dict->items.rwlock);
660 - return 0;
647 }
648 +
649 return 0;
650 }
651
652 static inline void ll_recursive_lock_set_thread_as_writer(DICTIONARY *dict) {
653 pid_t expected = 0, desired = gettid();
667 - if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
668 - fatal("DICTIONARY: Cannot set thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_SEQ_CST));
654 + if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED))
655 + fatal("DICTIONARY: Cannot set thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_RELAXED));
656 }
657
658 static inline void ll_recursive_unlock_unset_thread_writer(DICTIONARY *dict) {
659 pid_t expected = gettid(), desired = 0;
673 - if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
674 - fatal("DICTIONARY: Cannot unset thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_SEQ_CST));
660 + if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED))
661 + fatal("DICTIONARY: Cannot unset thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_RELAXED));
662 }
663
664 static inline bool ll_recursive_lock_is_thread_the_writer(DICTIONARY *dict) {
665 pid_t tid = gettid();
679 - return tid > 0 && tid == __atomic_load_n(&dict->items.writer_pid, __ATOMIC_SEQ_CST);
666 + return tid > 0 && tid == __atomic_load_n(&dict->items.writer_pid, __ATOMIC_RELAXED);
667 }
668
682 -static void ll_recursive_lock(DICTIONARY *dict, char rw) {
669 +static inline void ll_recursive_lock(DICTIONARY *dict, char rw) {
670 if(unlikely(is_dictionary_single_threaded(dict)))
671 return;
672
@@ -699,7 +686,7 @@ static void ll_recursive_lock(DICTIONARY *dict, char rw) {
686 }
687 }
688
702 -static void ll_recursive_unlock(DICTIONARY *dict, char rw) {
689 +static inline void ll_recursive_unlock(DICTIONARY *dict, char rw) {
690 if(unlikely(is_dictionary_single_threaded(dict)))
691 return;
692
@@ -722,10 +709,10 @@ static void ll_recursive_unlock(DICTIONARY *dict, char rw) {
709 }
710 }
711
725 -void dictionary_write_lock(DICTIONARY *dict) {
712 +inline void dictionary_write_lock(DICTIONARY *dict) {
713 ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE);
714 }
728 -void dictionary_write_unlock(DICTIONARY *dict) {
715 +inline void dictionary_write_unlock(DICTIONARY *dict) {
716 ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE);
717 }
718
@@ -760,8 +747,8 @@ static inline void dictionary_index_wrlock_unlock(DICTIONARY *dict) {
747 // items garbage collector
748
749 static void garbage_collect_pending_deletes(DICTIONARY *dict) {
763 - usec_t last_master_deletion_us = dict->hooks?__atomic_load_n(&dict->hooks->last_master_deletion_us, __ATOMIC_SEQ_CST):0;
764 - usec_t last_gc_run_us = __atomic_load_n(&dict->last_gc_run_us, __ATOMIC_SEQ_CST);
750 + usec_t last_master_deletion_us = dict->hooks?__atomic_load_n(&dict->hooks->last_master_deletion_us, __ATOMIC_RELAXED):0;
751 + usec_t last_gc_run_us = __atomic_load_n(&dict->last_gc_run_us, __ATOMIC_RELAXED);
752
753 bool is_view = is_view_dictionary(dict);
754
@@ -773,7 +760,7 @@ static void garbage_collect_pending_deletes(DICTIONARY *dict) {
760
761 ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE);
762
776 - __atomic_store_n(&dict->last_gc_run_us, now_realtime_usec(), __ATOMIC_SEQ_CST);
763 + __atomic_store_n(&dict->last_gc_run_us, now_realtime_usec(), __ATOMIC_RELAXED);
764
765 if(is_view)
766 dictionary_index_lock_wrlock(dict);
@@ -819,25 +806,22 @@ static void garbage_collect_pending_deletes(DICTIONARY *dict) {
806 (void)deleted;
807 (void)examined;
808
822 - internal_error(false, "DICTIONARY: garbage collected dictionary created by %s (%zu@%s), examined %zu items, deleted %zu items, still pending %zu items",
823 - dict->creation_function, dict->creation_line, dict->creation_file, examined, deleted, pending);
824 -
809 + internal_error(false, "DICTIONARY: garbage collected dictionary created by %s (%zu@%s), "
810 + "examined %zu items, deleted %zu items, still pending %zu items",
811 + dict->creation_function, dict->creation_line, dict->creation_file,
812 + examined, deleted, pending);
813 }
814
815 // ----------------------------------------------------------------------------
816 // reference counters
817
830 -static inline size_t reference_counter_init(DICTIONARY *dict) {
831 - (void)dict;
832 -
818 +static inline size_t reference_counter_init(DICTIONARY *dict __maybe_unused) {
819 // allocate memory required for reference counters
820 // return number of bytes
821 return 0;
822 }
823
838 -static inline size_t reference_counter_free(DICTIONARY *dict) {
839 - (void)dict;
840 -
824 +static inline size_t reference_counter_free(DICTIONARY *dict __maybe_unused) {
825 // free memory required for reference counters
826 // return number of bytes
827 return 0;
@@ -846,13 +830,13 @@ static inline size_t reference_counter_free(DICTIONARY *dict) {
830 static void item_acquire(DICTIONARY *dict, DICTIONARY_ITEM *item) {
831 REFCOUNT refcount;
832
849 - if(unlikely(is_dictionary_single_threaded(dict))) {
833 + if(unlikely(is_dictionary_single_threaded(dict)))
834 refcount = ++item->refcount;
851 - }
852 - else {
835 +
836 + else
837 // increment the refcount
838 refcount = __atomic_add_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST);
855 - }
839 +
840
841 if(refcount <= 0) {
842 internal_error(
@@ -900,7 +884,7 @@ static void item_release(DICTIONARY *dict, DICTIONARY_ITEM *item) {
884 is_deleted = item_flag_check(item, ITEM_FLAG_DELETED);
885
886 // decrement the refcount
903 - refcount = __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST);
887 + refcount = __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_RELEASE);
888 }
889
890 if(refcount < 0) {
@@ -956,14 +940,14 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it
940
941 desired = refcount + 1;
942
959 - } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
943 + } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
944
945 // if ret == ITEM_OK, we acquired the item
946
947 if(ret == RC_ITEM_OK) {
964 - if (is_view_dictionary(dict) &&
948 + if (unlikely(is_view_dictionary(dict) &&
949 item_shared_flag_check(item, ITEM_FLAG_DELETED) &&
966 - !item_flag_check(item, ITEM_FLAG_DELETED)) {
950 + !item_flag_check(item, ITEM_FLAG_DELETED))) {
951 // but, we can't use this item
952
953 if (having_index_lock) {
@@ -979,7 +963,7 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it
963 dict_item_set_deleted(dict, item);
964
965 // decrement the refcount we incremented above
982 - if (__atomic_sub_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST) == 0) {
966 + if (__atomic_sub_fetch(&item->refcount, 1, __ATOMIC_RELEASE) == 0) {
967 // this is a deleted item, and we are the last one
968 DICTIONARY_PENDING_DELETES_PLUS1(dict);
969 }
@@ -988,7 +972,7 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it
972 } else {
973 // this is traversal / walkthrough
974 // decrement the refcount we incremented above
991 - __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST);
975 + __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_RELEASE);
976 }
977
978 return RC_ITEM_MARKED_FOR_DELETION;
@@ -998,7 +982,6 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it
982 DICTIONARY_REFERENCED_ITEMS_PLUS1(dict);
983 }
984
1001 -
985 if(unlikely(spins > 1 && dict->stats))
986 DICTIONARY_STATS_CHECK_SPINS_PLUS(dict, spins - 1);
987
@@ -1037,7 +1020,7 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY
1020 ret = RC_ITEM_IS_CURRENTLY_BEING_CREATED;
1021 break;
1022 }
1040 - } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
1023 + } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
1024
1025 #ifdef NETDATA_INTERNAL_CHECKS
1026 if(ret == RC_ITEM_OK)
@@ -1055,8 +1038,8 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY
1038 static inline bool item_shared_release_and_check_if_it_can_be_freed(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item) {
1039 // if we can set refcount to REFCOUNT_DELETING, we can delete this item
1040
1058 - REFCOUNT links = __atomic_sub_fetch(&item->shared->links, 1, __ATOMIC_SEQ_CST);
1059 - if(links == 0 && __atomic_compare_exchange_n(&item->shared->links, &links, REFCOUNT_DELETING, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
1041 + REFCOUNT links = __atomic_sub_fetch(&item->shared->links, 1, __ATOMIC_RELEASE);
1042 + if(links == 0 && __atomic_compare_exchange_n(&item->shared->links, &links, REFCOUNT_DELETING, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
1043
1044 // we can delete it
1045 return true;
@@ -1290,7 +1273,7 @@ static DICTIONARY_ITEM *dict_item_create(DICTIONARY *dict __maybe_unused, size_t
1273 if(master_item) {
1274 item->shared = master_item->shared;
1275
1293 - if(unlikely(__atomic_add_fetch(&item->shared->links, 1, __ATOMIC_SEQ_CST) <= 1))
1276 + if(unlikely(__atomic_add_fetch(&item->shared->links, 1, __ATOMIC_ACQUIRE) <= 1))
1277 fatal("DICTIONARY: attempted to link to a shared item structure that had zero references");
1278 }
1279 else {
@@ -1478,7 +1461,7 @@ static void dict_item_shared_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item
1461 item_shared_flag_set(item, ITEM_FLAG_DELETED);
1462
1463 if(dict->hooks)
1481 - __atomic_store_n(&dict->hooks->last_master_deletion_us, now_realtime_usec(), __ATOMIC_SEQ_CST);
1464 + __atomic_store_n(&dict->hooks->last_master_deletion_us, now_realtime_usec(), __ATOMIC_RELAXED);
1465 }
1466 }
1467
@@ -1486,7 +1469,7 @@ static void dict_item_shared_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item
1469 static bool dict_item_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1470 ITEM_FLAGS expected, desired;
1471
1489 - expected = __atomic_load_n(&item->flags, __ATOMIC_SEQ_CST);
1472 + expected = __atomic_load_n(&item->flags, __ATOMIC_RELAXED);
1473
1474 do {
1475
@@ -1495,7 +1478,7 @@ static bool dict_item_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) {
1478
1479 desired = expected | ITEM_FLAG_DELETED;
1480
1498 - } while(!__atomic_compare_exchange_n(&item->flags, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
1481 + } while(!__atomic_compare_exchange_n(&item->flags, &expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
1482
1483 DICTIONARY_ENTRIES_MINUS1(dict);
1484 return true;
@@ -2063,11 +2046,11 @@ DICTIONARY *dictionary_create_view(DICTIONARY *master) {
2046
2047 dictionary_hooks_allocate(master);
2048
2066 - if(unlikely(__atomic_load_n(&master->hooks->links, __ATOMIC_SEQ_CST)) < 1)
2049 + if(unlikely(__atomic_load_n(&master->hooks->links, __ATOMIC_RELAXED)) < 1)
2050 fatal("DICTIONARY: attempted to create a view that has %d links", master->hooks->links);
2051
2052 dict->hooks = master->hooks;
2070 - __atomic_add_fetch(&master->hooks->links, 1, __ATOMIC_SEQ_CST);
2053 + __atomic_add_fetch(&master->hooks->links, 1, __ATOMIC_ACQUIRE);
2054
2055 #ifdef NETDATA_INTERNAL_CHECKS
2056 dict->creation_function = function;
@@ -2472,7 +2455,7 @@ int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(
2455 DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict);
2456
2457 ll_recursive_lock(dict, rw);
2475 - size_t entries = __atomic_load_n(&dict->entries, __ATOMIC_SEQ_CST);
2458 + size_t entries = __atomic_load_n(&dict->entries, __ATOMIC_RELAXED);
2459 DICTIONARY_ITEM **array = mallocz(sizeof(DICTIONARY_ITEM *) * entries);
2460
2461 size_t i;
@@ -3293,12 +3276,12 @@ static void *unittest_dict_master_thread(void *arg) {
3276
3277 DICTIONARY_ITEM *item = NULL;
3278 int loops = 0;
3296 - while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST)) {
3279 + while(!__atomic_load_n(&tv->join, __ATOMIC_RELAXED)) {
3280
3281 if(!item)
3282 item = dictionary_set_and_acquire_item(tv->master, "ITEM1", "123", strlen("123") + 1);
3283
3301 - if(__atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST) != NULL) {
3284 + if(__atomic_load_n(&tv->item_master, __ATOMIC_RELAXED) != NULL) {
3285 dictionary_acquired_item_release(tv->master, item);
3286 dictionary_del(tv->master, "ITEM1");
3287 item = NULL;
@@ -3307,7 +3290,7 @@ static void *unittest_dict_master_thread(void *arg) {
3290 }
3291
3292 dictionary_acquired_item_dup(tv->master, item); // for the view thread
3310 - __atomic_store_n(&tv->item_master, item, __ATOMIC_SEQ_CST);
3293 + __atomic_store_n(&tv->item_master, item, __ATOMIC_RELAXED);
3294 dictionary_del(tv->master, "ITEM1");
3295
3296
@@ -3333,13 +3316,13 @@ static void *unittest_dict_view_thread(void *arg) {
3316
3317 DICTIONARY_ITEM *m_item = NULL;
3318
3336 - while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST)) {
3337 - if(!(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST)))
3319 + while(!__atomic_load_n(&tv->join, __ATOMIC_RELAXED)) {
3320 + if(!(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_RELAXED)))
3321 continue;
3322
3323 DICTIONARY_ITEM *v_item = dictionary_view_set_and_acquire_item(tv->view, "ITEM2", m_item);
3324 dictionary_acquired_item_release(tv->master, m_item);
3342 - __atomic_store_n(&tv->item_master, NULL, __ATOMIC_SEQ_CST);
3325 + __atomic_store_n(&tv->item_master, NULL, __ATOMIC_RELAXED);
3326
3327 for(int i = 0; i < tv->dups ; i++) {
3328 dictionary_acquired_item_dup(tv->view, v_item);
@@ -3351,7 +3334,7 @@ static void *unittest_dict_view_thread(void *arg) {
3334
3335 dictionary_del(tv->view, "ITEM2");
3336
3354 - while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST) && !(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST))) {
3337 + while(!__atomic_load_n(&tv->join, __ATOMIC_RELAXED) && !(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_RELAXED))) {
3338 dictionary_acquired_item_dup(tv->view, v_item);
3339 dictionary_acquired_item_release(tv->view, v_item);
3340 }
libnetdata/inlined.h
+31
@@ -122,6 +122,37 @@ static inline unsigned long long str2ull(const char *s) {
122 return n;
123 }
124
125 +static inline unsigned long long str2ull_hex_or_dec(const char *s) {
126 + unsigned long long n = 0;
127 + char c;
128 +
129 + if(likely(s[0] == '0' && s[1] == 'x')) {
130 + const char *e = &s[sizeof(unsigned long long) * 2 + 2 + 1]; // max number of character to iterate: 8 bytes * 2 + '0x' + '\0'
131 +
132 + // skip 0x
133 + s += 2;
134 +
135 + for (c = *s; ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) && s < e; c = *(++s)) {
136 + n = n << 4;
137 +
138 + if (c <= '9')
139 + n += c - '0';
140 + else
141 + n += c - 'A' + 10;
142 + }
143 + return n;
144 + }
145 + else
146 + return str2ull(s);
147 +}
148 +
149 +static inline long long str2ll_hex_or_dec(const char *s) {
150 + if(*s == '-')
151 + return -(long long)str2ull_hex_or_dec(&s[1]);
152 + else
153 + return (long long)str2ull_hex_or_dec(s);
154 +}
155 +
156 static inline long long str2ll(const char *s, char **endptr) {
157 int negative = 0;
158
libnetdata/libnetdata.c
+113
@@ -2069,3 +2069,116 @@ void for_each_open_fd(OPEN_FD_ACTION action, OPEN_FD_EXCLUDE excluded_fds){
2069 closedir(dir);
2070 }
2071 }
2072 +
2073 +struct timing_steps {
2074 + const char *name;
2075 + usec_t time;
2076 + size_t count;
2077 +} timing_steps[TIMING_STEP_MAX + 1] = {
2078 + [TIMING_STEP_INTERNAL] = { .name = "internal", .time = 0, },
2079 +
2080 + [TIMING_STEP_BEGIN2_PREPARE] = { .name = "BEGIN2 prepare", .time = 0, },
2081 + [TIMING_STEP_BEGIN2_FIND_CHART] = { .name = "BEGIN2 find chart", .time = 0, },
2082 + [TIMING_STEP_BEGIN2_PARSE] = { .name = "BEGIN2 parse", .time = 0, },
2083 + [TIMING_STEP_BEGIN2_ML] = { .name = "BEGIN2 ml", .time = 0, },
2084 + [TIMING_STEP_BEGIN2_PROPAGATE] = { .name = "BEGIN2 propagate", .time = 0, },
2085 + [TIMING_STEP_BEGIN2_STORE] = { .name = "BEGIN2 store", .time = 0, },
2086 +
2087 + [TIMING_STEP_SET2_PREPARE] = { .name = "SET2 prepare", .time = 0, },
2088 + [TIMING_STEP_SET2_LOOKUP_DIMENSION] = { .name = "SET2 find dimension", .time = 0, },
2089 + [TIMING_STEP_SET2_PARSE] = { .name = "SET2 parse", .time = 0, },
2090 + [TIMING_STEP_SET2_ML] = { .name = "SET2 ml", .time = 0, },
2091 + [TIMING_STEP_SET2_PROPAGATE] = { .name = "SET2 propagate", .time = 0, },
2092 + [TIMING_STEP_RRDSET_STORE_METRIC] = { .name = "SET2 rrdset store", .time = 0, },
2093 + [TIMING_STEP_DBENGINE_FIRST_CHECK] = { .name = "db 1st check", .time = 0, },
2094 + [TIMING_STEP_DBENGINE_CHECK_DATA] = { .name = "db check data", .time = 0, },
2095 + [TIMING_STEP_DBENGINE_PACK] = { .name = "db pack", .time = 0, },
2096 + [TIMING_STEP_DBENGINE_PAGE_FIN] = { .name = "db page fin", .time = 0, },
2097 + [TIMING_STEP_DBENGINE_MRG_UPDATE] = { .name = "db mrg update", .time = 0, },
2098 + [TIMING_STEP_DBENGINE_PAGE_ALLOC] = { .name = "db page alloc", .time = 0, },
2099 + [TIMING_STEP_DBENGINE_CREATE_NEW_PAGE] = { .name = "db new page", .time = 0, },
2100 + [TIMING_STEP_DBENGINE_FLUSH_PAGE] = { .name = "db page flush", .time = 0, },
2101 + [TIMING_STEP_SET2_STORE] = { .name = "SET2 store", .time = 0, },
2102 +
2103 + [TIMING_STEP_END2_PREPARE] = { .name = "END2 prepare", .time = 0, },
2104 + [TIMING_STEP_END2_PUSH_V1] = { .name = "END2 push v1", .time = 0, },
2105 + [TIMING_STEP_END2_ML] = { .name = "END2 ml", .time = 0, },
2106 + [TIMING_STEP_END2_RRDSET] = { .name = "END2 rrdset", .time = 0, },
2107 + [TIMING_STEP_END2_PROPAGATE] = { .name = "END2 propagate", .time = 0, },
2108 + [TIMING_STEP_END2_STORE] = { .name = "END2 store", .time = 0, },
2109 +
2110 + // terminator
2111 + [TIMING_STEP_MAX] = { .name = NULL, .time = 0, },
2112 +};
2113 +
2114 +void timing_action(TIMING_ACTION action, TIMING_STEP step) {
2115 + static __thread usec_t last_action_time = 0;
2116 + static struct timing_steps timings2[TIMING_STEP_MAX + 1] = {};
2117 +
2118 + switch(action) {
2119 + case TIMING_ACTION_INIT:
2120 + last_action_time = now_monotonic_usec();
2121 + break;
2122 +
2123 + case TIMING_ACTION_STEP: {
2124 + if(!last_action_time)
2125 + return;
2126 +
2127 + usec_t now = now_monotonic_usec();
2128 + __atomic_add_fetch(&timing_steps[step].time, now - last_action_time, __ATOMIC_RELAXED);
2129 + __atomic_add_fetch(&timing_steps[step].count, 1, __ATOMIC_RELAXED);
2130 + last_action_time = now;
2131 + break;
2132 + }
2133 +
2134 + case TIMING_ACTION_FINISH: {
2135 + if(!last_action_time)
2136 + return;
2137 +
2138 + usec_t expected = __atomic_load_n(&timing_steps[TIMING_STEP_INTERNAL].time, __ATOMIC_RELAXED);
2139 + if(last_action_time - expected < 10 * USEC_PER_SEC) {
2140 + last_action_time = 0;
2141 + return;
2142 + }
2143 +
2144 + if(!__atomic_compare_exchange_n(&timing_steps[TIMING_STEP_INTERNAL].time, &expected, last_action_time, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
2145 + last_action_time = 0;
2146 + return;
2147 + }
2148 +
2149 + struct timing_steps timings3[TIMING_STEP_MAX + 1];
2150 + memcpy(timings3, timing_steps, sizeof(timings3));
2151 +
2152 + size_t total_reqs = 0;
2153 + usec_t total_usec = 0;
2154 + for(size_t t = 1; t < TIMING_STEP_MAX ; t++) {
2155 + total_usec += timings3[t].time - timings2[t].time;
2156 + total_reqs += timings3[t].count - timings2[t].count;
2157 + }
2158 +
2159 + BUFFER *wb = buffer_create(1024, NULL);
2160 +
2161 + for(size_t t = 1; t < TIMING_STEP_MAX ; t++) {
2162 + size_t requests = timings3[t].count - timings2[t].count;
2163 + if(!requests) continue;
2164 +
2165 + buffer_sprintf(wb, "TIMINGS REPORT: [%3zu. %-20s]: # %10zu, t %11.2f ms (%6.2f %%), avg %6.2f usec/run\n",
2166 + t,
2167 + timing_steps[t].name ? timing_steps[t].name : "x",
2168 + requests,
2169 + (double) (timings3[t].time - timings2[t].time) / (double)USEC_PER_MS,
2170 + (double) (timings3[t].time - timings2[t].time) * 100.0 / (double) total_usec,
2171 + (double) (timings3[t].time - timings2[t].time) / (double)requests
2172 + );
2173 + }
2174 +
2175 + info("TIMINGS REPORT:\n%sTIMINGS REPORT: total # %10zu, t %11.2f ms",
2176 + buffer_tostring(wb), total_reqs, (double)total_usec / USEC_PER_MS);
2177 +
2178 + memcpy(timings2, timings3, sizeof(timings2));
2179 +
2180 + last_action_time = 0;
2181 + buffer_free(wb);
2182 + }
2183 + }
2184 +}
libnetdata/libnetdata.h
+54
@@ -661,6 +661,60 @@ static inline size_t indexing_partition(Word_t ptr, Word_t modulo) {
661 }
662 }
663
664 +typedef enum {
665 + TIMING_STEP_INTERNAL = 0,
666 +
667 + TIMING_STEP_BEGIN2_PREPARE,
668 + TIMING_STEP_BEGIN2_FIND_CHART,
669 + TIMING_STEP_BEGIN2_PARSE,
670 + TIMING_STEP_BEGIN2_ML,
671 + TIMING_STEP_BEGIN2_PROPAGATE,
672 + TIMING_STEP_BEGIN2_STORE,
673 +
674 + TIMING_STEP_SET2_PREPARE,
675 + TIMING_STEP_SET2_LOOKUP_DIMENSION,
676 + TIMING_STEP_SET2_PARSE,
677 + TIMING_STEP_SET2_ML,
678 + TIMING_STEP_SET2_PROPAGATE,
679 + TIMING_STEP_RRDSET_STORE_METRIC,
680 + TIMING_STEP_DBENGINE_FIRST_CHECK,
681 + TIMING_STEP_DBENGINE_CHECK_DATA,
682 + TIMING_STEP_DBENGINE_PACK,
683 + TIMING_STEP_DBENGINE_PAGE_FIN,
684 + TIMING_STEP_DBENGINE_MRG_UPDATE,
685 + TIMING_STEP_DBENGINE_PAGE_ALLOC,
686 + TIMING_STEP_DBENGINE_CREATE_NEW_PAGE,
687 + TIMING_STEP_DBENGINE_FLUSH_PAGE,
688 + TIMING_STEP_SET2_STORE,
689 +
690 + TIMING_STEP_END2_PREPARE,
691 + TIMING_STEP_END2_PUSH_V1,
692 + TIMING_STEP_END2_ML,
693 + TIMING_STEP_END2_RRDSET,
694 + TIMING_STEP_END2_PROPAGATE,
695 + TIMING_STEP_END2_STORE,
696 +
697 + // terminator
698 + TIMING_STEP_MAX,
699 +} TIMING_STEP;
700 +
701 +typedef enum {
702 + TIMING_ACTION_INIT,
703 + TIMING_ACTION_STEP,
704 + TIMING_ACTION_FINISH,
705 +} TIMING_ACTION;
706 +
707 +#ifdef NETDATA_TIMING_REPORT
708 +#define timing_init() timing_action(TIMING_ACTION_INIT, TIMING_STEP_INTERNAL)
709 +#define timing_step(step) timing_action(TIMING_ACTION_STEP, step)
710 +#define timing_report() timing_action(TIMING_ACTION_FINISH, TIMING_STEP_INTERNAL)
711 +#else
712 +#define timing_init() debug_dummy()
713 +#define timing_step(step) debug_dummy()
714 +#define timing_report() debug_dummy()
715 +#endif
716 +void timing_action(TIMING_ACTION action, TIMING_STEP step);
717 +
718 # ifdef __cplusplus
719 }
720 # endif
libnetdata/storage_number/storage_number.h
+4
@@ -180,6 +180,10 @@ static inline NETDATA_DOUBLE str2ndd(const char *s, char **endptr) {
180 if(endptr) *endptr = (char *)&s[3];
181 return NAN;
182 }
183 + if(s[1] == 'u' && s[2] == 'l' && s[3] == 'l') {
184 + if(endptr) *endptr = (char *)&s[3];
185 + return NAN;
186 + }
187 break;
188
189 case 'i':
libnetdata/worker_utilization/worker_utilization.c
+12 -4
@@ -61,6 +61,14 @@ static struct workers_globals {
61
62 static __thread struct worker *worker = NULL; // the current thread worker
63
64 +static inline usec_t worker_now_monotonic_usec(void) {
65 +#ifdef NETDATA_WITHOUT_WORKERS_LATENCY
66 + return 0;
67 +#else
68 + return now_monotonic_usec();
69 +#endif
70 +}
71 +
72 size_t workers_allocated_memory(void) {
73 netdata_spinlock_lock(&workers_globals.spinlock);
74 size_t memory = workers_globals.memory;
@@ -77,7 +85,7 @@ void worker_register(const char *name) {
85 worker->tag = strdupz(netdata_thread_tag());
86 worker->workname = strdupz(name);
87
80 - usec_t now = now_monotonic_usec();
88 + usec_t now = worker_now_monotonic_usec();
89 worker->statistics_last_checkpoint = now;
90 worker->last_action_timestamp = now;
91 worker->last_action = WORKER_IDLE;
@@ -181,14 +189,14 @@ static inline void worker_is_idle_with_time(usec_t now) {
189 void worker_is_idle(void) {
190 if(unlikely(!worker || worker->last_action != WORKER_BUSY)) return;
191
184 - worker_is_idle_with_time(now_monotonic_usec());
192 + worker_is_idle_with_time(worker_now_monotonic_usec());
193 }
194
195 void worker_is_busy(size_t job_id) {
196 if(unlikely(!worker || job_id >= WORKER_UTILIZATION_MAX_JOB_TYPES))
197 return;
198
191 - usec_t now = now_monotonic_usec();
199 + usec_t now = worker_now_monotonic_usec();
200
201 if(worker->last_action == WORKER_BUSY)
202 worker_is_idle_with_time(now);
@@ -260,7 +268,7 @@ void workers_foreach(const char *name, void (*callback)(
268
269 struct worker *p;
270 DOUBLE_LINKED_LIST_FOREACH_FORWARD(workname->base, p, prev, next) {
263 - usec_t now = now_monotonic_usec();
271 + usec_t now = worker_now_monotonic_usec();
272
273 // find per job type statistics
274 STRING *per_job_type_name[WORKER_UTILIZATION_MAX_JOB_TYPES];
ml/ml-dummy.c
+2 -1
@@ -57,8 +57,9 @@ char *ml_get_host_runtime_info(RRDHOST *RH) {
57 return NULL;
58 }
59
60 -void ml_chart_update_begin(RRDSET *RS) {
60 +bool ml_chart_update_begin(RRDSET *RS) {
61 (void) RS;
62 + return false;
63 }
64
65 void ml_chart_update_end(RRDSET *RS) {
ml/ml.cc
+4 -2
@@ -167,12 +167,14 @@ void ml_cancel_anomaly_detection_threads(RRDHOST *RH) {
167 }
168 }
169
170 -void ml_chart_update_begin(RRDSET *RS) {
170 +bool ml_chart_update_begin(RRDSET *RS) {
171 Chart *C = reinterpret_cast<Chart *>(RS->ml_chart);
172 if (!C)
173 - return;
173 + return false;
174
175 C->updateBegin();
176 +
177 + return true;
178 }
179
180 void ml_chart_update_end(RRDSET *RS) {
ml/ml.h
+1 -1
@@ -37,7 +37,7 @@ char *ml_get_host_info(RRDHOST *RH);
37 char *ml_get_host_runtime_info(RRDHOST *RH);
38 char *ml_get_host_models(RRDHOST *RH);
39
40 -void ml_chart_update_begin(RRDSET *RS);
40 +bool ml_chart_update_begin(RRDSET *RS);
41 void ml_chart_update_end(RRDSET *RS);
42
43 bool ml_is_anomalous(RRDDIM *RD, time_t curr_t, double value, bool exists);
parser/parser.c
+7 -2
@@ -29,12 +29,13 @@ inline int find_first_keyword(const char *str, char *keyword, int max_size, int
29 *
30 */
31
32 -PARSER *parser_init(RRDHOST *host, void *user, FILE *fp_input, FILE *fp_output, int fd, PARSER_INPUT_TYPE flags, void *ssl __maybe_unused)
32 +PARSER *parser_init(RRDHOST *host, void *user, parser_cleanup_t cleanup_cb, FILE *fp_input, FILE *fp_output, int fd, PARSER_INPUT_TYPE flags, void *ssl __maybe_unused)
33 {
34 PARSER *parser;
35
36 parser = callocz(1, sizeof(*parser));
37 parser->user = user;
38 + parser->user_cleanup_cb = cleanup_cb;
39 parser->fd = fd;
40 parser->fp_input = fp_input;
41 parser->fp_output = fp_output;
@@ -69,11 +70,15 @@ PARSER *parser_init(RRDHOST *host, void *user, FILE *fp_input, FILE *fp_output,
70 parser_add_keyword(parser, PLUGINSD_KEYWORD_FUNCTION, pluginsd_function);
71 parser_add_keyword(parser, PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN, pluginsd_function_result_begin);
72
72 - parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_BEGIN, pluginsd_replay_rrdset_begin);
73 + parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_BEGIN, pluginsd_replay_begin);
74 parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_SET, pluginsd_replay_set);
75 parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE, pluginsd_replay_rrddim_collection_state);
76 parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE, pluginsd_replay_rrdset_collection_state);
77 parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_END, pluginsd_replay_end);
78 +
79 + parser_add_keyword(parser, PLUGINSD_KEYWORD_BEGIN_V2, pluginsd_begin_v2);
80 + parser_add_keyword(parser, PLUGINSD_KEYWORD_SET_V2, pluginsd_set_v2);
81 + parser_add_keyword(parser, PLUGINSD_KEYWORD_END_V2, pluginsd_end_v2);
82 }
83
84 return parser;
parser/parser.h
+10 -2
@@ -46,6 +46,8 @@ typedef struct parser_data {
46 struct parser_data *next;
47 } PARSER_DATA;
48
49 +typedef void (*parser_cleanup_t)(void *user);
50 +
51 typedef struct parser {
52 size_t worker_job_next_id;
53 uint8_t version; // Parser version
@@ -59,6 +61,7 @@ typedef struct parser {
61 PARSER_DATA *data; // extra input
62 PARSER_KEYWORD *keyword; // List of parse keywords and functions
63 void *user; // User defined structure to hold extra state between calls
64 + parser_cleanup_t user_cleanup_cb;
65 uint32_t flags;
66 size_t line;
67
@@ -90,7 +93,7 @@ typedef struct parser {
93
94 int find_first_keyword(const char *str, char *keyword, int max_size, int (*custom_isspace)(char));
95
93 -PARSER *parser_init(RRDHOST *host, void *user, FILE *fp_input, FILE *fp_output, int fd, PARSER_INPUT_TYPE flags, void *ssl);
96 +PARSER *parser_init(RRDHOST *host, void *user, parser_cleanup_t cleanup_cb, FILE *fp_input, FILE *fp_output, int fd, PARSER_INPUT_TYPE flags, void *ssl);
97 int parser_add_keyword(PARSER *working_parser, char *keyword, keyword_function func);
98 int parser_next(PARSER *working_parser);
99 int parser_action(PARSER *working_parser, char *input);
@@ -114,10 +117,15 @@ PARSER_RC pluginsd_overwrite(char **words, size_t num_words, void *user);
117 PARSER_RC pluginsd_clabel_commit(char **words, size_t num_words, void *user);
118 PARSER_RC pluginsd_clabel(char **words, size_t num_words, void *user);
119
117 -PARSER_RC pluginsd_replay_rrdset_begin(char **words, size_t num_words, void *user);
120 +PARSER_RC pluginsd_replay_begin(char **words, size_t num_words, void *user);
121 PARSER_RC pluginsd_replay_rrddim_collection_state(char **words, size_t num_words, void *user);
122 PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words, void *user);
123 PARSER_RC pluginsd_replay_set(char **words, size_t num_words, void *user);
124 PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user);
125
126 +PARSER_RC pluginsd_begin_v2(char **words, size_t num_words, void *user);
127 +PARSER_RC pluginsd_set_v2(char **words, size_t num_words, void *user);
128 +PARSER_RC pluginsd_end_v2(char **words, size_t num_words, void *user);
129 +void pluginsd_cleanup_v2(void *user);
130 +
131 #endif
streaming/receiver.c
+6 -2
@@ -332,6 +332,10 @@ static void streaming_parser_thread_cleanup(void *ptr) {
332
333 bool plugin_is_enabled(struct plugind *cd);
334
335 +void streaming_parser_cleanup(void *user) {
336 + pluginsd_cleanup_v2(user);
337 +}
338 +
339 static size_t streaming_parser(struct receiver_state *rpt, struct plugind *cd, int fd, void *ssl) {
340 size_t result;
341
@@ -343,7 +347,7 @@ static size_t streaming_parser(struct receiver_state *rpt, struct plugind *cd, i
347 .trust_durations = 1
348 };
349
346 - PARSER *parser = parser_init(rpt->host, &user, NULL, NULL, fd, PARSER_INPUT_SPLIT, ssl);
350 + PARSER *parser = parser_init(rpt->host, &user, streaming_parser_cleanup, NULL, NULL, fd, PARSER_INPUT_SPLIT, ssl);
351
352 rrd_collector_started();
353
@@ -416,7 +420,7 @@ static size_t streaming_parser(struct receiver_state *rpt, struct plugind *cd, i
420 }
421
422 done:
419 - result = user.count;
423 + result = user.data_collections_count;
424
425 // free parser with the pop function
426 netdata_thread_cleanup_pop(1);
streaming/replication.c
+106 -51
@@ -88,6 +88,7 @@ struct replication_query {
88 bool locked_data_collection;
89 bool execute;
90 bool interrupted;
91 + bool send_anomaly_bit;
92 } query;
93
94 time_t wall_clock_time;
@@ -112,7 +113,8 @@ static struct replication_query *replication_query_prepare(
113 time_t query_after,
114 time_t query_before,
115 bool query_enable_streaming,
115 - time_t wall_clock_time
116 + time_t wall_clock_time,
117 + bool send_anomaly_bit
118 ) {
119 size_t dimensions = rrdset_number_of_dimensions(st);
120 struct replication_query *q = callocz(1, sizeof(struct replication_query) + dimensions * sizeof(struct replication_dimension));
@@ -131,6 +133,7 @@ static struct replication_query *replication_query_prepare(
133 q->query.after = query_after;
134 q->query.before = query_before;
135 q->query.enable_streaming = query_enable_streaming;
136 + q->query.send_anomaly_bit = send_anomaly_bit;
137
138 q->wall_clock_time = wall_clock_time;
139
@@ -209,25 +212,30 @@ static struct replication_query *replication_query_prepare(
212 return q;
213 }
214
212 -static void replication_send_chart_collection_state(BUFFER *wb, RRDSET *st) {
215 +void replication_send_chart_collection_state(BUFFER *wb, RRDSET *st) {
216 RRDDIM *rd;
217 rrddim_foreach_read(rd, st) {
218 if(!rd->exposed) continue;
219
217 - buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE " \"%s\" %llu %lld " NETDATA_DOUBLE_FORMAT " " NETDATA_DOUBLE_FORMAT "\n",
218 - rrddim_id(rd),
219 - (usec_t)rd->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)rd->last_collected_time.tv_usec,
220 - rd->last_collected_value,
221 - rd->last_calculated_value,
222 - rd->last_stored_value
223 - );
220 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE " '", sizeof(PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE) - 1 + 2);
221 + buffer_fast_strcat(wb, rrddim_id(rd), string_strlen(rd->id));
222 + buffer_fast_strcat(wb, "' ", 2);
223 + buffer_print_llu(wb, (usec_t)rd->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)rd->last_collected_time.tv_usec);
224 + buffer_fast_strcat(wb, " ", 1);
225 + buffer_print_ll(wb, rd->last_collected_value);
226 + buffer_fast_strcat(wb, " ", 1);
227 + buffer_rrd_value(wb, rd->last_calculated_value);
228 + buffer_fast_strcat(wb, " ", 1);
229 + buffer_rrd_value(wb, rd->last_stored_value);
230 + buffer_fast_strcat(wb, "\n", 1);
231 }
232 rrddim_foreach_done(rd);
233
227 - buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE " %llu %llu\n",
228 - (usec_t)st->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)st->last_collected_time.tv_usec,
229 - (usec_t)st->last_updated.tv_sec * USEC_PER_SEC + (usec_t)st->last_updated.tv_usec
230 - );
234 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE " ", sizeof(PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE) - 1 + 1);
235 + buffer_print_llu(wb, (usec_t)st->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)st->last_collected_time.tv_usec);
236 + buffer_fast_strcat(wb, " ", 1);
237 + buffer_print_llu(wb, (usec_t)st->last_updated.tv_sec * USEC_PER_SEC + (usec_t)st->last_updated.tv_usec);
238 + buffer_fast_strcat(wb, "\n", 1);
239 }
240
241 static void replication_query_finalize(BUFFER *wb, struct replication_query *q, bool executed) {
@@ -328,9 +336,10 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
336
337 error_limit_static_global_var(erl, 1, 0);
338 error_limit(&erl,
331 - "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s/dim:%s': db does not advance the query beyond time %llu (tried 1000 times to get the next point and always got back a point in the past)",
332 - rrdhost_hostname(q->st->rrdhost), rrdset_id(q->st), rrddim_id(d->rd),
333 - (unsigned long long) now);
339 + "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s/dim:%s': db does not advance the query "
340 + "beyond time %llu (tried 1000 times to get the next point and always got back a point in the past)",
341 + rrdhost_hostname(q->st->rrdhost), rrdset_id(q->st), rrddim_id(d->rd),
342 + (unsigned long long) now);
343
344 continue;
345 }
@@ -374,9 +383,10 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
383 else
384 fix_min_start_time = min_end_time - min_update_every;
385
386 +#ifdef NETDATA_INTERNAL_CHECKS
387 error_limit_static_global_var(erl, 1, 0);
388 error_limit(&erl, "REPLAY WARNING: 'host:%s/chart:%s' "
379 - "misaligned dimensions "
389 + "misaligned dimensions, "
390 "update every (min: %ld, max: %ld), "
391 "start time (min: %ld, max: %ld), "
392 "end time (min %ld, max %ld), "
@@ -389,6 +399,7 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
399 now, last_end_time_in_buffer,
400 fix_min_start_time
401 );
402 +#endif
403
404 min_start_time = fix_min_start_time;
405 }
@@ -410,7 +421,8 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
421 q->query.before = last_end_time_in_buffer;
422 q->query.enable_streaming = false;
423
413 - internal_error(true, "REPLICATION: buffer size %zu is more than the max message size %zu for chart '%s' of host '%s'. "
424 + internal_error(true, "REPLICATION: current buffer size %zu is more than the "
425 + "max message size %zu for chart '%s' of host '%s'. "
426 "Interrupting replication request (%ld to %ld, %s) at %ld to %ld, %s.",
427 buffer_strlen(wb), max_msg_size, rrdset_id(q->st), rrdhost_hostname(q->st->rrdhost),
428 q->request.after, q->request.before, q->request.enable_streaming?"true":"false",
@@ -422,11 +434,13 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
434 }
435 last_end_time_in_buffer = min_end_time;
436
425 - buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_BEGIN " '' %llu %llu %llu\n",
426 - (unsigned long long) min_start_time,
427 - (unsigned long long) min_end_time,
428 - (unsigned long long) wall_clock_time
429 - );
437 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_REPLAY_BEGIN " '' ", sizeof(PLUGINSD_KEYWORD_REPLAY_BEGIN) - 1 + 4);
438 + buffer_print_llu(wb, min_start_time);
439 + buffer_fast_strcat(wb, " ", 1);
440 + buffer_print_llu(wb, min_end_time);
441 + buffer_fast_strcat(wb, " ", 1);
442 + buffer_print_llu(wb, wall_clock_time);
443 + buffer_fast_strcat(wb, "\n", 1);
444
445 // output the replay values for this time
446 for (size_t i = 0; i < dimensions; i++) {
@@ -438,8 +452,13 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
452 !storage_point_is_unset(d->sp) &&
453 !storage_point_is_gap(d->sp))) {
454
441 - buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_SET " \"%s\" " NETDATA_DOUBLE_FORMAT " \"%s\"\n",
442 - rrddim_id(d->rd), d->sp.sum, d->sp.flags & SN_FLAG_RESET ? "R" : "");
455 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_REPLAY_SET " \"", sizeof(PLUGINSD_KEYWORD_REPLAY_SET) - 1 + 2);
456 + buffer_fast_strcat(wb, rrddim_id(d->rd), string_strlen(d->rd->id));
457 + buffer_fast_strcat(wb, "\" ", 2);
458 + buffer_rrd_value(wb, d->sp.sum);
459 + buffer_fast_strcat(wb, " ", 1);
460 + buffer_print_sn_flags(wb, d->sp.flags, q->query.send_anomaly_bit);
461 + buffer_fast_strcat(wb, "\n", 1);
462
463 points_generated++;
464 }
@@ -462,14 +481,14 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
481 log_date(actual_before_buf, LOG_DATE_LENGTH, actual_before);
482 internal_error(true,
483 "STREAM_SENDER REPLAY: 'host:%s/chart:%s': sending data %llu [%s] to %llu [%s] (requested %llu [delta %lld] to %llu [delta %lld])",
465 - rrdhost_hostname(st->rrdhost), rrdset_id(st),
484 + rrdhost_hostname(q->st->rrdhost), rrdset_id(q->st),
485 (unsigned long long)actual_after, actual_after_buf, (unsigned long long)actual_before, actual_before_buf,
486 (unsigned long long)after, (long long)(actual_after - after), (unsigned long long)before, (long long)(actual_before - before));
487 }
488 else
489 internal_error(true,
490 "STREAM_SENDER REPLAY: 'host:%s/chart:%s': nothing to send (requested %llu to %llu)",
472 - rrdhost_hostname(st->rrdhost), rrdset_id(st),
491 + rrdhost_hostname(q->st->rrdhost), rrdset_id(q->st),
492 (unsigned long long)after, (unsigned long long)before);
493 #endif // NETDATA_LOG_REPLICATION_REQUESTS
494
@@ -483,7 +502,13 @@ static bool replication_query_execute(BUFFER *wb, struct replication_query *q, s
502 return finished_with_gap;
503 }
504
486 -static struct replication_query *replication_response_prepare(RRDSET *st, bool requested_enable_streaming, time_t requested_after, time_t requested_before) {
505 +static struct replication_query *replication_response_prepare(
506 + RRDSET *st,
507 + bool requested_enable_streaming,
508 + time_t requested_after,
509 + time_t requested_before,
510 + bool send_anomaly_bit
511 + ) {
512 time_t wall_clock_time = now_realtime_sec();
513
514 if(requested_after > requested_before) {
@@ -509,7 +534,8 @@ static struct replication_query *replication_response_prepare(RRDSET *st, bool r
534 bool query_enable_streaming = requested_enable_streaming;
535
536 time_t db_first_entry = 0, db_last_entry = 0;
512 - rrdset_get_retention_of_tier_for_collected_chart(st, &db_first_entry, &db_last_entry, wall_clock_time, 0);
537 + rrdset_get_retention_of_tier_for_collected_chart(
538 + st, &db_first_entry, &db_last_entry, wall_clock_time, 0);
539
540 if(requested_after == 0 && requested_before == 0 && requested_enable_streaming == true) {
541 // no data requested - just enable streaming
@@ -543,7 +569,7 @@ static struct replication_query *replication_response_prepare(RRDSET *st, bool r
569 db_first_entry, db_last_entry,
570 requested_after, requested_before, requested_enable_streaming,
571 query_after, query_before, query_enable_streaming,
546 - wall_clock_time);
572 + wall_clock_time, send_anomaly_bit);
573 }
574
575 void replication_response_cancel_and_finalize(struct replication_query *q) {
@@ -562,7 +588,11 @@ bool replication_response_execute_and_finalize(struct replication_query *q, size
588 // holding the host's buffer lock for too long
589 BUFFER *wb = sender_start(host->sender);
590
565 - buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_BEGIN " \"%s\"\n", rrdset_id(st));
591 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_REPLAY_BEGIN " '", sizeof(PLUGINSD_KEYWORD_REPLAY_BEGIN) - 1 + 2);
592 + buffer_fast_strcat(wb, rrdset_id(st), string_strlen(st->id));
593 + buffer_fast_strcat(wb, "'\n", 2);
594 +
595 +// buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_BEGIN " \"%s\"\n", rrdset_id(st));
596
597 bool locked_data_collection = q->query.locked_data_collection;
598 q->query.locked_data_collection = false;
@@ -585,23 +615,38 @@ bool replication_response_execute_and_finalize(struct replication_query *q, size
615
616 // end with first/last entries we have, and the first start time and
617 // last end time of the data we sent
588 - buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_END " %d %llu %llu %s %llu %llu %llu\n",
589 -
590 - // current chart update every
591 - (int)st->update_every
618
593 - // child first db time, child end db time
594 - , (unsigned long long)db_first_entry, (unsigned long long)db_last_entry
595 -
596 - // start streaming boolean
597 - , enable_streaming ? "true" : "false"
598 -
599 - // after requested, before requested ('before' can be altered by the child when the request had enable_streaming true)
600 - , (unsigned long long)after, (unsigned long long)before
601 -
602 - // child world clock time
603 - , (unsigned long long)wall_clock_time
604 - );
619 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_REPLAY_END " ", sizeof(PLUGINSD_KEYWORD_REPLAY_END) - 1 + 1);
620 + buffer_print_ll(wb, st->update_every);
621 + buffer_fast_strcat(wb, " ", 1);
622 + buffer_print_llu(wb, db_first_entry);
623 + buffer_fast_strcat(wb, " ", 1);
624 + buffer_print_llu(wb, db_last_entry);
625 + buffer_fast_strcat(wb, enable_streaming ? " true " : " false ", 7);
626 + buffer_print_llu(wb, after);
627 + buffer_fast_strcat(wb, " ", 1);
628 + buffer_print_llu(wb, before);
629 + buffer_fast_strcat(wb, " ", 1);
630 + buffer_print_llu(wb, wall_clock_time);
631 + buffer_fast_strcat(wb, "\n", 1);
632 +
633 +// buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_END " %d %llu %llu %s %llu %llu %llu\n",
634 +//
635 +// // current chart update every
636 +// (int)st->update_every
637 +//
638 +// // child first db time, child end db time
639 +// , (unsigned long long)db_first_entry, (unsigned long long)db_last_entry
640 +//
641 +// // start streaming boolean
642 +// , enable_streaming ? "true" : "false"
643 +//
644 +// // after requested, before requested ('before' can be altered by the child when the request had enable_streaming true)
645 +// , (unsigned long long)after, (unsigned long long)before
646 +//
647 +// // child world clock time
648 +// , (unsigned long long)wall_clock_time
649 +// );
650
651 worker_is_busy(WORKER_JOB_BUFFER_COMMIT);
652 sender_commit(host->sender, wb);
@@ -733,9 +778,9 @@ static bool send_replay_chart_cmd(struct replication_request_details *r, const c
778 , msg
779 , r->last_request.after, r->last_request.before
780 , r->child_db.first_entry_t, r->child_db.last_entry_t
736 - , r->child_db.world_time_t, (r->child_db.world_time_t == r->local_db.now) ? "SAME" : (r->child_db.world_time_t < r->local_db.now) ? "BEHIND" : "AHEAD"
781 + , r->child_db.wall_clock_time, (r->child_db.wall_clock_time == r->local_db.wall_clock_time) ? "SAME" : (r->child_db.wall_clock_time < r->local_db.wall_clock_time) ? "BEHIND" : "AHEAD"
782 , r->local_db.first_entry_t, r->local_db.last_entry_t
738 - , r->local_db.now
783 + , r->local_db.wall_clock_time
784 , r->gap.from, r->gap.to
785 , (r->gap.from == r->wanted.after) ? "FULL" : "PARTIAL"
786 , (st->replay.after != 0 || st->replay.before != 0) ? "OVERLAPPING" : ""
@@ -1371,7 +1416,12 @@ static bool replication_execute_request(struct replication_request *rq, bool wor
1416 if(likely(workers))
1417 worker_is_busy(WORKER_JOB_PREPARE_QUERY);
1418
1374 - rq->q = replication_response_prepare(rq->st, rq->start_streaming, rq->after, rq->before);
1419 + rq->q = replication_response_prepare(
1420 + rq->st,
1421 + rq->start_streaming,
1422 + rq->after,
1423 + rq->before,
1424 + stream_has_capability(rq->sender, STREAM_CAP_INTERPOLATED));
1425 }
1426
1427 if(likely(workers))
@@ -1650,7 +1700,12 @@ static int replication_execute_next_pending_request(bool cancel) {
1700
1701 if (rq->st && !rq->q) {
1702 worker_is_busy(WORKER_JOB_PREPARE_QUERY);
1653 - rq->q = replication_response_prepare(rq->st, rq->start_streaming, rq->after, rq->before);
1703 + rq->q = replication_response_prepare(
1704 + rq->st,
1705 + rq->start_streaming,
1706 + rq->after,
1707 + rq->before,
1708 + stream_has_capability(rq->sender, STREAM_CAP_INTERPOLATED));
1709 }
1710
1711 rq->executed = false;
streaming/rrdpush.c
+81 -9
@@ -378,7 +378,72 @@ bool rrdset_push_chart_definition_now(RRDSET *st) {
378 return true;
379 }
380
381 -void rrdset_done_push(RRDSET *st) {
381 +void rrdset_push_metrics_v1(RRDSET_STREAM_BUFFER *rsb, RRDSET *st) {
382 + RRDHOST *host = st->rrdhost;
383 + rrdpush_send_chart_metrics(rsb->wb, st, host->sender, rsb->rrdset_flags);
384 +}
385 +
386 +void rrddim_push_metrics_v2(RRDSET_STREAM_BUFFER *rsb, RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n, SN_FLAGS flags) {
387 + if(!rsb->wb || !rsb->v2 || !netdata_double_isnumber(n) || !does_storage_number_exist(flags))
388 + return;
389 +
390 + BUFFER *wb = rsb->wb;
391 + time_t point_end_time_s = (time_t)(point_end_time_ut / USEC_PER_SEC);
392 + if(unlikely(rsb->last_point_end_time_s != point_end_time_s)) {
393 +
394 + if(unlikely(rsb->begin_v2_added))
395 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_END_V2 "\n", sizeof(PLUGINSD_KEYWORD_END_V2) - 1 + 1);
396 +
397 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_BEGIN_V2 " '", sizeof(PLUGINSD_KEYWORD_BEGIN_V2) - 1 + 2);
398 + buffer_fast_strcat(wb, rrdset_id(rd->rrdset), string_strlen(rd->rrdset->id));
399 + buffer_fast_strcat(wb, "' ", 2);
400 + buffer_print_llu_hex(wb, rd->rrdset->update_every);
401 + buffer_fast_strcat(wb, " ", 1);
402 + buffer_print_llu_hex(wb, point_end_time_s);
403 + buffer_fast_strcat(wb, " ", 1);
404 + if(point_end_time_s == rsb->wall_clock_time)
405 + buffer_fast_strcat(wb, "#", 1);
406 + else
407 + buffer_print_llu_hex(wb, rsb->wall_clock_time);
408 + buffer_fast_strcat(wb, "\n", 1);
409 +
410 + rsb->last_point_end_time_s = point_end_time_s;
411 + rsb->begin_v2_added = true;
412 + }
413 +
414 + buffer_fast_strcat(wb, PLUGINSD_KEYWORD_SET_V2 " '", sizeof(PLUGINSD_KEYWORD_SET_V2) - 1 + 2);
415 + buffer_fast_strcat(wb, rrddim_id(rd), string_strlen(rd->id));
416 + buffer_fast_strcat(wb, "' ", 2);
417 + buffer_print_ll_hex(wb, rd->last_collected_value);
418 + buffer_fast_strcat(wb, " ", 1);
419 +
420 + if((NETDATA_DOUBLE)rd->last_collected_value == n)
421 + buffer_fast_strcat(wb, "#", 1);
422 + else
423 + buffer_rrd_value(wb, n);
424 +
425 + buffer_fast_strcat(wb, " ", 1);
426 + buffer_print_sn_flags(wb, flags, true);
427 + buffer_fast_strcat(wb, "\n", 1);
428 +}
429 +
430 +void rrdset_push_metrics_finished(RRDSET_STREAM_BUFFER *rsb, RRDSET *st) {
431 + if(!rsb->wb)
432 + return;
433 +
434 + if(rsb->v2 && rsb->begin_v2_added) {
435 + if(unlikely(rsb->rrdset_flags & RRDSET_FLAG_UPSTREAM_SEND_VARIABLES))
436 + rrdsetvar_print_to_streaming_custom_chart_variables(st, rsb->wb);
437 +
438 + buffer_fast_strcat(rsb->wb, PLUGINSD_KEYWORD_END_V2 "\n", sizeof(PLUGINSD_KEYWORD_END_V2) - 1 + 1);
439 + }
440 +
441 + sender_commit(st->rrdhost->sender, rsb->wb);
442 +
443 + *rsb = (RRDSET_STREAM_BUFFER){ .wb = NULL, };
444 +}
445 +
446 +RRDSET_STREAM_BUFFER rrdset_push_metric_initialize(RRDSET *st, time_t wall_clock_time) {
447 RRDHOST *host = st->rrdhost;
448
449 // fetch the flags we need to check with one atomic operation
@@ -395,7 +460,7 @@ void rrdset_done_push(RRDSET *st) {
460 error("STREAM %s [send]: not ready - collected metrics are not sent to parent.", rrdhost_hostname(host));
461 }
462
398 - return;
463 + return (RRDSET_STREAM_BUFFER) { .wb = NULL, };
464 }
465 else if(unlikely(host_flags & RRDHOST_FLAG_RRDPUSH_SENDER_LOGGED_STATUS)) {
466 info("STREAM %s [send]: sending metrics to parent...", rrdhost_hostname(host));
@@ -408,17 +473,23 @@ void rrdset_done_push(RRDSET *st) {
473
474 if(unlikely((exposed_upstream && replication_in_progress) ||
475 !should_send_chart_matching(st, rrdset_flags)))
411 - return;
412 -
413 - BUFFER *wb = sender_start(host->sender);
476 + return (RRDSET_STREAM_BUFFER) { .wb = NULL, };
477
415 - if(unlikely(!exposed_upstream))
478 + if(unlikely(!exposed_upstream)) {
479 + BUFFER *wb = sender_start(host->sender);
480 replication_in_progress = rrdpush_send_chart_definition(wb, st);
481 + sender_commit(host->sender, wb);
482 + }
483
418 - if (likely(!replication_in_progress))
419 - rrdpush_send_chart_metrics(wb, st, host->sender, rrdset_flags);
484 + if(replication_in_progress)
485 + return (RRDSET_STREAM_BUFFER) { .wb = NULL, };
486
421 - sender_commit(host->sender, wb);
487 + return (RRDSET_STREAM_BUFFER) {
488 + .v2 = stream_has_capability(host->sender, STREAM_CAP_INTERPOLATED),
489 + .rrdset_flags = rrdset_flags,
490 + .wb = sender_start(host->sender),
491 + .wall_clock_time = wall_clock_time,
492 + };
493 }
494
495 // labels
@@ -1077,6 +1148,7 @@ static void stream_capabilities_to_string(BUFFER *wb, STREAM_CAPABILITIES caps)
1148 if(caps & STREAM_CAP_FUNCTIONS) buffer_strcat(wb, "FUNCTIONS ");
1149 if(caps & STREAM_CAP_REPLICATION) buffer_strcat(wb, "REPLICATION ");
1150 if(caps & STREAM_CAP_BINARY) buffer_strcat(wb, "BINARY ");
1151 + if(caps & STREAM_CAP_INTERPOLATED) buffer_strcat(wb, "INTERPOLATED ");
1152 }
1153
1154 void log_receiver_capabilities(struct receiver_state *rpt) {
streaming/rrdpush.h
+18 -2
@@ -41,6 +41,7 @@ typedef enum {
41 STREAM_CAP_FUNCTIONS = (1 << 11), // plugin functions supported
42 STREAM_CAP_REPLICATION = (1 << 12), // replication supported
43 STREAM_CAP_BINARY = (1 << 13), // streaming supports binary data
44 + STREAM_CAP_INTERPOLATED = (1 << 14), // streaming supports interpolated streaming of values
45
46 STREAM_CAP_INVALID = (1 << 30), // used as an invalid value for capabilities when this is set
47 // this must be signed int, so don't use the last bit
@@ -56,7 +57,8 @@ typedef enum {
57 #define STREAM_OUR_CAPABILITIES ( \
58 STREAM_CAP_V1 | STREAM_CAP_V2 | STREAM_CAP_VN | STREAM_CAP_VCAPS | \
59 STREAM_CAP_HLABELS | STREAM_CAP_CLAIM | STREAM_CAP_CLABELS | \
59 - STREAM_HAS_COMPRESSION | STREAM_CAP_FUNCTIONS | STREAM_CAP_REPLICATION | STREAM_CAP_BINARY )
60 + STREAM_HAS_COMPRESSION | STREAM_CAP_FUNCTIONS | STREAM_CAP_REPLICATION | STREAM_CAP_BINARY | \
61 + STREAM_CAP_INTERPOLATED)
62
63 #define stream_has_capability(rpt, capability) ((rpt) && ((rpt)->capabilities & (capability)))
64
@@ -303,7 +305,21 @@ void sender_commit(struct sender_state *s, BUFFER *wb);
305 int rrdpush_init();
306 bool rrdpush_receiver_needs_dbengine();
307 int configured_as_parent();
306 -void rrdset_done_push(RRDSET *st);
308 +
309 +typedef struct rrdset_stream_buffer {
310 + bool v2;
311 + bool begin_v2_added;
312 + time_t wall_clock_time;
313 + uint64_t rrdset_flags; // RRDSET_FLAGS
314 + time_t last_point_end_time_s;
315 + BUFFER *wb;
316 +} RRDSET_STREAM_BUFFER;
317 +
318 +RRDSET_STREAM_BUFFER rrdset_push_metric_initialize(RRDSET *st, time_t wall_clock_time);
319 +void rrdset_push_metrics_v1(RRDSET_STREAM_BUFFER *rsb, RRDSET *st);
320 +void rrdset_push_metrics_finished(RRDSET_STREAM_BUFFER *rsb, RRDSET *st);
321 +void rrddim_push_metrics_v2(RRDSET_STREAM_BUFFER *rsb, RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n, SN_FLAGS flags);
322 +
323 bool rrdset_push_chart_definition_now(RRDSET *st);
324 void *rrdpush_sender_thread(void *ptr);
325 void rrdpush_send_host_labels(RRDHOST *host);
streaming/sender.c
+8
@@ -104,6 +104,14 @@ void sender_commit(struct sender_state *s, BUFFER *wb) {
104
105 netdata_mutex_lock(&s->mutex);
106
107 +// FILE *fp = fopen("/tmp/stream.txt", "a");
108 +// fprintf(fp,
109 +// "\n--- SEND BEGIN: %s ----\n"
110 +// "%s"
111 +// "--- SEND END ----------------------------------------\n"
112 +// , rrdhost_hostname(s->host), src);
113 +// fclose(fp);
114 +
115 if(unlikely(s->buffer->max_size < (src_len + 1) * SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE)) {
116 info("STREAM %s [send to %s]: max buffer size of %zu is too small for a data message of size %zu. Increasing the max buffer size to %d times the max data message size.",
117 rrdhost_hostname(s->host), s->connected_to, s->buffer->max_size, buffer_strlen(wb) + 1, SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE);