@cryptotaxi247 / netdata-1 / commits / cd39ffd37

added RRDCALC management; preparation for expression evaluation

Costa Tsaousis committed Aug 10, 2016 at 02:33 UTC cd39ffd3753f705d3edc290df0f2856df9f581f7
9 files changed +586 -36
CMakeLists.txt
+1 -1
@@ -82,7 +82,7 @@ set(NETDATA_SOURCE_FILES
82 src/web_client.h
83 src/web_server.c
84 src/web_server.h
85 - config.h src/health.h src/health.c)
85 + config.h src/health.h src/health.c src/eval.h src/eval.c)
86
87 set(APPS_PLUGIN_SOURCE_FILES
88 src/appconfig.c
src/Makefile.am
+1
@@ -32,6 +32,7 @@ netdata_SOURCES = \
32 common.c common.h \
33 daemon.c daemon.h \
34 dictionary.c dictionary.h \
35 + eval.c eval.h \
36 global_statistics.c global_statistics.h \
37 health.c health.h \
38 log.c log.h \
src/common.h
+1
@@ -80,6 +80,7 @@
80 #include "plugin_tc.h"
81 #include "plugins_d.h"
82
83 +#include "eval.h"
84 #include "health.h"
85
86 #include "rrd.h"
src/eval.c new
+289
@@ -0,0 +1,289 @@
1 +#include "common.h"
2 +
3 +// ----------------------------------------------------------------------------
4 +// operators that work on 2 operands
5 +
6 +static inline int isoperatorterm_word(const char s) {
7 + if(isspace(s) || s == '(' || s == '$' || s == '!' || s == '-' || s == '+' || isdigit(s)) return 1;
8 + return 0;
9 +}
10 +
11 +static inline int isoperatorterm_symbol(const char s) {
12 + if(isoperatorterm_word(s) || isalpha(s)) return 1;
13 + return 0;
14 +}
15 +
16 +static inline int parse_and(const char **string) {
17 + const char *s = *string;
18 +
19 + // AND
20 + if((s[0] == 'A' || s[0] == 'a') && (s[1] == 'N' || s[1] == 'n') && (s[2] == 'D' || s[2] == 'd') && isoperatorterm_word(s[3])) {
21 + *string = &s[4];
22 + return 1;
23 + }
24 +
25 + // &&
26 + if(s[0] == '&' && s[1] == '&' && isoperatorterm_symbol(s[2])) {
27 + *string = &s[2];
28 + return 1;
29 + }
30 +
31 + return 0;
32 +}
33 +
34 +static inline int parse_or(const char **string) {
35 + const char *s = *string;
36 +
37 + // OR
38 + if((s[0] == 'O' || s[0] == '0') && (s[1] == 'R' || s[1] == 'r') && isoperatorterm_word(s[2])) {
39 + *string = &s[3];
40 + return 1;
41 + }
42 +
43 + // ||
44 + if(s[0] == '|' && s[1] == '|' && isoperatorterm_symbol(s[2])) {
45 + *string = &s[2];
46 + return 1;
47 + }
48 +
49 + return 0;
50 +}
51 +
52 +static inline int parse_greater_than_or_equal(const char **string) {
53 + const char *s = *string;
54 +
55 + // >=
56 + if(s[0] == '>' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
57 + *string = &s[2];
58 + return 1;
59 + }
60 +
61 + return 0;
62 +}
63 +
64 +static inline int parse_less_than_or_equal(const char **string) {
65 + const char *s = *string;
66 +
67 + // <=
68 + if (s[0] == '<' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
69 + *string = &s[2];
70 + return 1;
71 + }
72 +
73 + return 0;
74 +}
75 +
76 +static inline int parse_greater(const char **string) {
77 + const char *s = *string;
78 +
79 + // >
80 + if(s[0] == '>' && isoperatorterm_symbol(s[1])) {
81 + *string = &s[1];
82 + return 1;
83 + }
84 +
85 + return 0;
86 +}
87 +
88 +static inline int parse_less(const char **string) {
89 + const char *s = *string;
90 +
91 + // <
92 + if(s[0] == '<' && isoperatorterm_symbol(s[1])) {
93 + *string = &s[1];
94 + return 1;
95 + }
96 +
97 + return 0;
98 +}
99 +
100 +static inline int parse_equal(const char **string) {
101 + const char *s = *string;
102 +
103 + // ==
104 + if(s[0] == '=' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
105 + *string = &s[2];
106 + return 1;
107 + }
108 +
109 + // =
110 + if(s[0] == '=' && isoperatorterm_symbol(s[1])) {
111 + *string = &s[1];
112 + return 1;
113 + }
114 +
115 + return 0;
116 +}
117 +
118 +static inline int parse_not_equal(const char **string) {
119 + const char *s = *string;
120 +
121 + // !=
122 + if(s[0] == '!' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
123 + *string = &s[2];
124 + return 1;
125 + }
126 +
127 + // <>
128 + if(s[0] == '<' && s[1] == '>' && isoperatorterm_symbol(s[2])) {
129 + *string = &s[2];
130 + }
131 +
132 + return 0;
133 +}
134 +
135 +static inline int parse_multiply(const char **string) {
136 + const char *s = *string;
137 +
138 + // *
139 + if(s[0] == '*' && isoperatorterm_symbol(s[1])) {
140 + *string = &s[1];
141 + return 1;
142 + }
143 +
144 + return 0;
145 +}
146 +
147 +static inline int parse_divide(const char **string) {
148 + const char *s = *string;
149 +
150 + // /
151 + if(s[0] == '/' && isoperatorterm_symbol(s[1])) {
152 + *string = &s[1];
153 + return 1;
154 + }
155 +
156 + return 0;
157 +}
158 +
159 +static inline int parse_minus(const char **string) {
160 + const char *s = *string;
161 +
162 + // -
163 + if(s[0] == '-' && isoperatorterm_symbol(s[1])) {
164 + *string = &s[1];
165 + return 1;
166 + }
167 +
168 + return 0;
169 +}
170 +
171 +static inline int parse_plus(const char **string) {
172 + const char *s = *string;
173 +
174 + // +
175 + if(s[0] == '+' && isoperatorterm_symbol(s[1])) {
176 + *string = &s[1];
177 + return 1;
178 + }
179 +
180 + return 0;
181 +}
182 +
183 +// ----------------------------------------------------------------------------
184 +// operators that affect a single operand
185 +
186 +static inline int parse_not(const char **string) {
187 + const char *s = *string;
188 +
189 + // NOT
190 + if((s[0] == 'N' || s[0] == 'n') && (s[1] == 'O' || s[1] == 'o') && (s[2] == 'T' || s[2] == 't') && isoperatorterm_word(s[3])) {
191 + *string = &s[4];
192 + return 1;
193 + }
194 +
195 + if(s[0] == EVAL_OPERATOR_NOT) {
196 + *string = &s[1];
197 + return 1;
198 + }
199 +
200 + return 0;
201 +}
202 +
203 +static struct operator {
204 + const char *printas;
205 + int precedence;
206 + char id;
207 + int (*parse)(const char **);
208 +} operators[] = {
209 + { "&&", 2, '&', parse_and },
210 + { "||", 2, '|', parse_or },
211 + { ">=", 3, '}', parse_greater_than_or_equal },
212 + { "<=", 3, '{', parse_less_than_or_equal },
213 + { "<>", 3, '~', parse_not_equal },
214 + { "==", 3, '=', parse_equal },
215 + { "<", 3, '<', parse_less },
216 + { ">", 3, '>', parse_greater },
217 + { "+", 4, EVAL_OPERATOR_PLUS, parse_plus },
218 + { "-", 4, EVAL_OPERATOR_MINUS, parse_minus },
219 + { "*", 5, '*', parse_multiply },
220 + { "/", 5, '/', parse_divide },
221 +
222 + // we should not put NOT in this list
223 +
224 + { NULL, 0, EVAL_OPERATOR_NOP, NULL }
225 +};
226 +
227 +static inline char parse_operator(const char **s, int *precedence) {
228 + int i;
229 +
230 + for(i = 0 ; operators[i].parse != NULL ; i++)
231 + if(operators[i].parse(s)) {
232 + if(precedence) *precedence = operators[i].precedence;
233 + return operators[i].id;
234 + }
235 +
236 + return EVAL_OPERATOR_NOP;
237 +}
238 +
239 +static inline EVAL_OPERAND *operand_alloc(int count) {
240 + EVAL_OPERAND *op = calloc(1, sizeof(EVAL_OPERAND) + (sizeof(EVAL_VALUE) * count));
241 + if(!op) fatal("Cannot allocate memory for OPERAND");
242 +
243 + op->count = count;
244 + return op;
245 +}
246 +
247 +static inline void operand_set_value_operand(EVAL_OPERAND *op, int pos, EVAL_OPERAND *value) {
248 + if(pos >= op->count)
249 + fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
250 +
251 + op->ops[pos].type = EVAL_OPERAND_EXPRESSION;
252 + op->ops[pos].expression = value;
253 +}
254 +
255 +// forward definitions
256 +static inline EVAL_OPERAND *parse_operand(const char **string);
257 +
258 +static inline EVAL_OPERAND *operand_alloc_single(const char **string, char type) {
259 + EVAL_OPERAND *sub = parse_operand(string);
260 + if(!sub) return NULL;
261 +
262 + EVAL_OPERAND *op = operand_alloc(1);
263 + if(!op) fatal("Cannot allocate memory for OPERAND");
264 +
265 + op->operator = type;
266 + operand_set_value_operand(op, 0, sub);
267 + return op;
268 +}
269 +
270 +static inline EVAL_OPERAND *parse_operand(const char **string) {
271 + const char *s = *string;
272 + while(isspace(*s)) s++;
273 +
274 + if(!*s) return NULL;
275 + *string = s;
276 +
277 + if(parse_not(string))
278 + return operand_alloc_single(string, EVAL_OPERATOR_NOT);
279 +
280 + if(parse_plus(string))
281 + return operand_alloc_single(string, EVAL_OPERATOR_PLUS);
282 +
283 + if(parse_minus(string))
284 + return operand_alloc_single(string, EVAL_OPERATOR_MINUS);
285 +
286 +
287 +
288 + return NULL;
289 +}
src/eval.h new
+38
@@ -0,0 +1,38 @@
1 +#ifndef NETDATA_EVAL_H
2 +#define NETDATA_EVAL_H
3 +
4 +typedef struct variable {
5 + char *name;
6 + struct rrdvar *rrdvar;
7 + struct variable *next;
8 +} VARIABLE;
9 +
10 +#define EVAL_OPERAND_INVALID 0
11 +#define EVAL_OPERAND_NUMBER 1
12 +#define EVAL_OPERAND_VARIABLE 2
13 +#define EVAL_OPERAND_EXPRESSION 3
14 +
15 +// these are used for EVAL_OPERAND.operator
16 +#define EVAL_OPERATOR_NOP '\0'
17 +#define EVAL_OPERATOR_NOT '!'
18 +#define EVAL_OPERATOR_PLUS '+'
19 +#define EVAL_OPERATOR_MINUS '-'
20 +
21 +typedef struct eval_value {
22 + int type;
23 +
24 + union {
25 + calculated_number number;
26 + VARIABLE *variable;
27 + struct eval_operand *expression;
28 + };
29 +} EVAL_VALUE;
30 +
31 +typedef struct eval_operand {
32 + char operator;
33 +
34 + int count;
35 + EVAL_VALUE ops[];
36 +} EVAL_OPERAND;
37 +
38 +#endif //NETDATA_EVAL_H
src/health.c
+174 -27
@@ -45,8 +45,15 @@ static inline RRDVAR *rrdvar_create(const char *name, uint32_t hash, int type, c
45 return rv;
46 }
47
48 -static inline void rrdvar_free(RRDVAR *rv) {
49 - // FIXME: find all references of this and NULL them.
48 +static inline void rrdvar_free(RRDHOST *host, RRDVAR *rv) {
49 + if(host) {
50 + // FIXME: we may need some kind of locking here
51 + // to have mutually exclusive access with eval()
52 + VARIABLE *rf;
53 + for (rf = host->references; rf; rf = rf->next)
54 + if (rf->rrdvar == rv) rf->rrdvar = NULL;
55 + }
56 +
57 free(rv);
58 }
59
@@ -59,7 +66,7 @@ static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *
66 RRDVAR *ret = rrdvar_index_add(tree, rv);
67 if(unlikely(ret != rv)) {
68 debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", name, scope);
62 - rrdvar_free(rv);
69 + rrdvar_free(NULL, rv);
70 rv = NULL;
71 }
72 else
@@ -147,12 +154,12 @@ void rrdsetvar_rename_all(RRDSET *st) {
154 // name changed
155 if (rs->context_name) {
156 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
150 - rrdvar_free(rs->context_name);
157 + rrdvar_free(st->rrdhost, rs->context_name);
158 }
159
160 if (rs->host_name) {
161 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
155 - rrdvar_free(rs->host_name);
162 + rrdvar_free(st->rrdhost, rs->host_name);
163 }
164
165 free(rs->fullname);
@@ -163,6 +170,8 @@ void rrdsetvar_rename_all(RRDSET *st) {
170 rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
171 }
172 }
173 +
174 + rrdsetcalc_link_matching(st);
175 }
176
177 void rrdsetvar_free(RRDSETVAR *rs) {
@@ -171,27 +180,27 @@ void rrdsetvar_free(RRDSETVAR *rs) {
180
181 if(rs->local) {
182 rrdvar_index_del(&st->variables_root_index, rs->local);
174 - rrdvar_free(rs->local);
183 + rrdvar_free(st->rrdhost, rs->local);
184 }
185
186 if(rs->context) {
187 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context);
179 - rrdvar_free(rs->context);
188 + rrdvar_free(st->rrdhost, rs->context);
189 }
190
191 if(rs->host) {
192 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host);
184 - rrdvar_free(rs->host);
193 + rrdvar_free(st->rrdhost, rs->host);
194 }
195
196 if(rs->context_name) {
197 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
189 - rrdvar_free(rs->context_name);
198 + rrdvar_free(st->rrdhost, rs->context_name);
199 }
200
201 if(rs->host_name) {
202 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
194 - rrdvar_free(rs->host_name);
203 + rrdvar_free(st->rrdhost, rs->host_name);
204 }
205
206 if(st->variables == rs) {
@@ -302,7 +311,7 @@ void rrddimvar_rename_all(RRDDIM *rd) {
311 // name
312 if (rs->local_name) {
313 rrdvar_index_del(&st->variables_root_index, rs->local_name);
305 - rrdvar_free(rs->local_name);
314 + rrdvar_free(st->rrdhost, rs->local_name);
315 }
316 free(rs->name);
317 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
@@ -314,11 +323,11 @@ void rrddimvar_rename_all(RRDDIM *rd) {
323 // fullidname
324 if (rs->context_fullidname) {
325 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
317 - rrdvar_free(rs->context_fullidname);
326 + rrdvar_free(st->rrdhost, rs->context_fullidname);
327 }
328 if (rs->host_fullidname) {
329 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullidname);
321 - rrdvar_free(rs->host_fullidname);
330 + rrdvar_free(st->rrdhost, rs->host_fullidname);
331 }
332 free(rs->fullidname);
333 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->name);
@@ -333,11 +342,11 @@ void rrddimvar_rename_all(RRDDIM *rd) {
342 // fullnameid
343 if (rs->context_fullnameid) {
344 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
336 - rrdvar_free(rs->context_fullnameid);
345 + rrdvar_free(st->rrdhost, rs->context_fullnameid);
346 }
347 if (rs->host_fullnameid) {
348 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnameid);
340 - rrdvar_free(rs->host_fullnameid);
349 + rrdvar_free(st->rrdhost, rs->host_fullnameid);
350 }
351 free(rs->fullnameid);
352 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->id);
@@ -352,11 +361,11 @@ void rrddimvar_rename_all(RRDDIM *rd) {
361 // fullnamename
362 if (rs->context_fullnamename) {
363 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
355 - rrdvar_free(rs->context_fullnamename);
364 + rrdvar_free(st->rrdhost, rs->context_fullnamename);
365 }
366 if (rs->host_fullnamename) {
367 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnamename);
359 - rrdvar_free(rs->host_fullnamename);
368 + rrdvar_free(st->rrdhost, rs->host_fullnamename);
369 }
370 free(rs->fullnamename);
371 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->name);
@@ -378,45 +387,45 @@ void rrddimvar_free(RRDDIMVAR *rs) {
387
388 if(rs->local_id) {
389 rrdvar_index_del(&st->variables_root_index, rs->local_id);
381 - rrdvar_free(rs->local_id);
390 + rrdvar_free(st->rrdhost, rs->local_id);
391 }
392 if(rs->local_name) {
393 rrdvar_index_del(&st->variables_root_index, rs->local_name);
385 - rrdvar_free(rs->local_name);
394 + rrdvar_free(st->rrdhost, rs->local_name);
395 }
396
397 if(rs->context_fullidid) {
398 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidid);
390 - rrdvar_free(rs->context_fullidid);
399 + rrdvar_free(st->rrdhost, rs->context_fullidid);
400 }
401 if(rs->context_fullidname) {
402 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
394 - rrdvar_free(rs->context_fullidname);
403 + rrdvar_free(st->rrdhost, rs->context_fullidname);
404 }
405 if(rs->context_fullnameid) {
406 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
398 - rrdvar_free(rs->context_fullnameid);
407 + rrdvar_free(st->rrdhost, rs->context_fullnameid);
408 }
409 if(rs->context_fullnamename) {
410 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
402 - rrdvar_free(rs->context_fullnamename);
411 + rrdvar_free(st->rrdhost, rs->context_fullnamename);
412 }
413
414 if(rs->host_fullidid) {
415 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidid);
407 - rrdvar_free(rs->host_fullidid);
416 + rrdvar_free(st->rrdhost, rs->host_fullidid);
417 }
418 if(rs->host_fullidname) {
419 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidname);
411 - rrdvar_free(rs->host_fullidname);
420 + rrdvar_free(st->rrdhost, rs->host_fullidname);
421 }
422 if(rs->host_fullnameid) {
423 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnameid);
415 - rrdvar_free(rs->host_fullnameid);
424 + rrdvar_free(st->rrdhost, rs->host_fullnameid);
425 }
426 if(rs->host_fullnamename) {
427 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnamename);
419 - rrdvar_free(rs->host_fullnamename);
428 + rrdvar_free(st->rrdhost, rs->host_fullnamename);
429 }
430
431 if(rd->variables == rs) {
@@ -441,3 +450,141 @@ void rrddimvar_free(RRDDIMVAR *rs) {
450 free(rs->fullnamename);
451 free(rs);
452 }
453 +
454 +// ----------------------------------------------------------------------------
455 +// RRDCALC management
456 +
457 +// this has to be called while the caller has locked
458 +// the RRDHOST
459 +static inline void rrdhostcalc_linked(RRDHOST *host, RRDCALC *rc) {
460 + // move it to be last
461 +
462 + if(!rc->next)
463 + // we are last already
464 + return;
465 +
466 + RRDCALC *t, *last = NULL, *prev = NULL;
467 + for (t = host->calculations; t ; t = t->next) {
468 + if(t->next == rc)
469 + prev = t;
470 +
471 + if(!t->next)
472 + last = t;
473 + }
474 +
475 + if(!last) {
476 + error("RRDCALC '%s' cannot be linked to the end of host '%s' list", rc->name, host->hostname);
477 + return;
478 + }
479 +
480 + if(prev)
481 + prev->next = rc->next;
482 + else {
483 + if(host->calculations == rc)
484 + host->calculations = rc->next;
485 + else {
486 + error("RRDCALC '%s' is not found in host '%s' list", rc->name, host->hostname);
487 + return;
488 + }
489 + }
490 +
491 + last->next = rc;
492 + rc->next = NULL;
493 +}
494 +
495 +// this has to be called while the caller has locked
496 +// the RRDHOST
497 +static inline void rrdhostcalc_unlinked(RRDHOST *host, RRDCALC *rc) {
498 + // move it to be first
499 +
500 + if(host->calculations == rc) {
501 + // ok, we are the first
502 + return;
503 + }
504 + else {
505 + // find the previous one
506 + RRDCALC *t;
507 + for (t = host->calculations; t && t->next != rc; rc = rc->next) ;
508 + if(unlikely(!t)) {
509 + error("RRDCALC '%s' is not linked to host '%s'.", rc->name, host->hostname);
510 + return;
511 + }
512 + t->next = rc->next;
513 + rc->next = host->calculations;
514 + host->calculations = rc;
515 + }
516 +}
517 +
518 +static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
519 + rc->rrdset = st;
520 +
521 + rc->local = rrdvar_create_and_index("local", &st->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
522 + rc->context = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
523 + rc->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
524 +
525 + rrdhostcalc_linked(st->rrdhost, rc);
526 +}
527 +
528 +// this has to be called while the RRDHOST is locked
529 +void rrdsetcalc_link_matching(RRDSET *st) {
530 + RRDCALC *rc;
531 +
532 + for(rc = st->rrdhost->calculations; rc ; rc = rc->next) {
533 + // since unlinked ones are in front and linked at the end
534 + // we stop on the first linked RRDCALC
535 + if(rc->rrdset != NULL) break;
536 +
537 + if((rc->hash_chart == st->hash && !strcmp(rc->name, st->id)) ||
538 + (rc->hash_chart == st->hash_name && !strcmp(rc->name, st->name))) {
539 + rrdsetcalc_link(st, rc);
540 + }
541 + }
542 +}
543 +
544 +// this has to be called while the RRDHOST is locked
545 +void rrdsetcalc_unlink(RRDCALC *rc) {
546 + RRDSET *st = rc->rrdset;
547 +
548 + if(!st) {
549 + error("Requested to unlink RRDCALC '%s' which is not linked to any RRDSET", rc->name);
550 + return;
551 + }
552 +
553 + RRDHOST *host = st->rrdhost;
554 +
555 + // unlink it
556 + if(rc->rrdset_prev)
557 + rc->rrdset_prev->rrdset_next = rc->rrdset_next;
558 +
559 + if(rc->rrdset_next)
560 + rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
561 +
562 + if(st->calculations == rc)
563 + st->calculations = rc->rrdset_next;
564 +
565 + rc->rrdset_prev = rc->rrdset_next = NULL;
566 +
567 + if(rc->local) {
568 + rrdvar_index_del(&st->variables_root_index, rc->local);
569 + rrdvar_free(st->rrdhost, rc->local);
570 + rc->local = NULL;
571 + }
572 +
573 + if(rc->context) {
574 + rrdvar_index_del(&st->rrdcontext->variables_root_index, rc->context);
575 + rc->context = NULL;
576 + }
577 +
578 + if(rc->host) {
579 + rrdvar_index_del(&st->rrdhost->variables_root_index, rc->host);
580 + rc->host = NULL;
581 + }
582 +
583 + rc->rrdset = NULL;
584 +
585 + // RRDCALC will remain in RRDHOST
586 + // so that if the matching chart is found in the future
587 + // it will be applied automatically
588 +
589 + rrdhostcalc_unlinked(host, rc);
590 +}
src/health.h
+60 -3
@@ -44,6 +44,10 @@ typedef struct rrdvar {
44 } RRDVAR;
45
46 // variables linked to charts
47 +// We link variables to point the values that are already
48 +// calculated / processed by the normal data collection process
49 +// This means, there will be no speed penalty for using
50 +// these variables
51 typedef struct rrdsetvar {
52 char *fullid; // chart type.chart id.variable
53 uint32_t hash_fullid;
@@ -72,6 +76,10 @@ typedef struct rrdsetvar {
76
77
78 // variables linked to dimensions
79 +// We link variables to point the values that are already
80 +// calculated / processed by the normal data collection process
81 +// This means, there will be no speed penalty for using
82 +// these variables
83 typedef struct rrddimvar {
84 char *prefix;
85 char *suffix;
@@ -117,25 +125,71 @@ typedef struct rrddimvar {
125 struct rrddimvar *next;
126 } RRDDIMVAR;
127
128 +// additional calculated variables
129 +// These aggregate time-series data at fixed intervals
130 +// (defined in their update_every member below)
131 +// These increase the overhead of netdata.
132 +//
133 +// These calculations are allocated and linked (->next)
134 +// to RRDHOST.
135 +// Then are also linked to RRDSET (of course only when the
136 +// chart is found, via ->rrdset_next and ->rrdset_prev).
137 +// This double-linked list is maintained sorted at all times
138 +// having as RRDSET->calculations the RRDCALC to be processed
139 +// next.
140 typedef struct rrdcalc {
121 - avl avl;
141 + char *name;
142 + uint32_t hash;
143 +
144 + char *chart; // the chart name
145 + uint32_t hash_chart;
146 +
147 + char *dimensions; // the chart dimensions
148
149 int group; // grouping method: average, max, etc.
150 int before; // ending point in time-series
151 int after; // starting point in time-series
152 + uint32_t options; // calculation options
153 int update_every; // update frequency for the calculation
154
128 - const char *name;
155 + time_t last_updated;
156 + time_t next_update;
157 +
158 calculated_number value;
159
160 RRDVAR *local;
161 RRDVAR *context;
162 RRDVAR *host;
163
164 + struct rrdset *rrdset;
165 + struct rrdcalc *rrdset_next;
166 + struct rrdcalc *rrdset_prev;
167 +
168 struct rrdcalc *next;
136 - struct rrdcalc *prev;
169 } RRDCALC;
170
171 +
172 +// RRDCALCTEMPLATE
173 +// these are to be applied to charts found dynamically
174 +// based on their context.
175 +typedef struct rrdcalctemplate {
176 + char *name;
177 + uint32_t hash_name;
178 +
179 + char *context;
180 + uint32_t hash_context;
181 +
182 + char *dimensions;
183 +
184 + int group; // grouping method: average, max, etc.
185 + int before; // ending point in time-series
186 + int after; // starting point in time-series
187 + uint32_t options; // calculation options
188 + int update_every; // update frequency for the calculation
189 +
190 + struct rrdcalctemplate *next;
191 +} RRDCALCTEMPLATE;
192 +
193 #include "rrd.h"
194
195 extern void rrdsetvar_rename_all(RRDSET *st);
@@ -146,4 +200,7 @@ extern void rrddimvar_rename_all(RRDDIM *rd);
200 extern RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options);
201 extern void rrddimvar_free(RRDDIMVAR *rs);
202
203 +extern void rrdsetcalc_link_matching(RRDSET *st);
204 +extern void rrdsetcalc_unlink(RRDCALC *rc);
205 +
206 #endif //NETDATA_HEALTH_H
src/rrd.c
+5
@@ -531,6 +531,8 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const
531
532 rrdset_index_add(&localhost, st);
533
534 + rrdsetcalc_link_matching(st);
535 +
536 pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
537
538 return(st);
@@ -740,6 +742,9 @@ void rrdset_free_all(void)
742 while(st->variables)
743 rrdsetvar_free(st->variables);
744
745 + while(st->calculations)
746 + rrdsetcalc_unlink(st->calculations);
747 +
748 while(st->dimensions)
749 rrddim_free(st, st->dimensions);
750
src/rrd.h
+17 -5
@@ -253,6 +253,13 @@ struct rrdset {
253
254 struct rrdset *next; // linking of rrdsets
255
256 + // ------------------------------------------------------------------------
257 + // local variables
258 +
259 + avl_tree_lock variables_root_index;
260 + RRDSETVAR *variables;
261 + RRDCALC *calculations;
262 +
263 // ------------------------------------------------------------------------
264 // members for checking the data when loading from disk
265
@@ -266,11 +273,6 @@ struct rrdset {
273 avl_tree_lock dimensions_index; // the root of the dimensions index
274 RRDDIM *dimensions; // the actual data for every dimension
275
269 - // ------------------------------------------------------------------------
270 - // local variables
271 -
272 - avl_tree_lock variables_root_index;
273 - RRDSETVAR *variables;
276 };
277 typedef struct rrdset RRDSET;
278
@@ -290,6 +292,16 @@ struct rrdhost {
292
293 avl_tree_lock rrdcontext_root_index;
294 avl_tree_lock variables_root_index;
295 +
296 + // all RRDCALCs are primarily allocated and linked here
297 + // RRDCALCs may be linked to charts at any point
298 + // (charts may or may not exist when these are loaded)
299 + RRDCALC *calculations;
300 +
301 + // all variable references are linked here
302 + // RRDVARs may be free'd, so every time this happens
303 + // we need to find all their references and invalidate them
304 + VARIABLE *references;
305 };
306 typedef struct rrdhost RRDHOST;
307