]> arthur.barton.de Git - netdata.git/blob - src/rrd.c
Approved changes from Costa Tsaousis (#323)
[netdata.git] / src / rrd.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4 #include <stddef.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <time.h>
8 #include <sys/time.h>
9 #include <sys/mman.h>
10 #include <pthread.h>
11 #include <errno.h>
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <stdlib.h>
16
17 #include "common.h"
18 #include "log.h"
19 #include "appconfig.h"
20
21 #include "rrd.h"
22
23 #define RRD_DEFAULT_GAP_INTERPOLATIONS 1
24
25 // ----------------------------------------------------------------------------
26 // globals
27
28 // if not zero it gives the time (in seconds) to remove un-updated dimensions
29 // DO NOT ENABLE
30 // if dimensions are removed, the chart generation will have to run again
31 int rrd_delete_unupdated_dimensions = 0;
32
33 int rrd_update_every = UPDATE_EVERY;
34 int rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
35
36 RRDSET *rrdset_root = NULL;
37 pthread_rwlock_t rrdset_root_rwlock = PTHREAD_RWLOCK_INITIALIZER;
38
39 int rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
40
41
42 // ----------------------------------------------------------------------------
43 // RRDSET index
44
45 static int rrdset_iterator(avl *a) { if(a) {}; return 0; }
46
47 static int rrdset_compare(void* a, void* b) {
48         if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1;
49         else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1;
50         else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id);
51 }
52
53 avl_tree rrdset_root_index = {
54                 NULL,
55                 rrdset_compare,
56 #ifndef AVL_WITHOUT_PTHREADS
57 #ifdef AVL_LOCK_WITH_MUTEX
58                 PTHREAD_MUTEX_INITIALIZER
59 #else
60                 PTHREAD_RWLOCK_INITIALIZER
61 #endif
62 #endif
63 };
64
65 #define rrdset_index_add(st) avl_insert(&rrdset_root_index, (avl *)(st))
66 #define rrdset_index_del(st) avl_remove(&rrdset_root_index, (avl *)(st))
67
68 static RRDSET *rrdset_index_find(const char *id, uint32_t hash) {
69         RRDSET *result = NULL, tmp;
70         strncpy(tmp.id, id, RRD_ID_LENGTH_MAX);
71         tmp.id[RRD_ID_LENGTH_MAX] = '\0';
72         tmp.hash = (hash)?hash:simple_hash(tmp.id);
73
74         avl_search(&(rrdset_root_index), (avl *)&tmp, rrdset_iterator, (avl **)&result);
75         return result;
76 }
77
78 // ----------------------------------------------------------------------------
79 // RRDSET name index
80
81 #define rrdset_from_avlname(avlname_ptr) ((RRDSET *)((avlname_ptr) - offsetof(RRDSET, avlname)))
82
83 static int rrdset_iterator_name(avl *a) { if(a) {}; return 0; }
84
85 static int rrdset_compare_name(void* a, void* b) {
86         RRDSET *A = rrdset_from_avlname(a);
87         RRDSET *B = rrdset_from_avlname(b);
88
89         // fprintf(stderr, "COMPARING: %s with %s\n", A->name, B->name);
90
91         if(A->hash_name < B->hash_name) return -1;
92         else if(A->hash_name > B->hash_name) return 1;
93         else return strcmp(A->name, B->name);
94 }
95
96 avl_tree rrdset_root_index_name = {
97                 NULL,
98                 rrdset_compare_name,
99 #ifndef AVL_WITHOUT_PTHREADS
100 #ifdef AVL_LOCK_WITH_MUTEX
101                 PTHREAD_MUTEX_INITIALIZER
102 #else
103                 PTHREAD_RWLOCK_INITIALIZER
104 #endif
105 #endif
106 };
107
108 int rrdset_index_add_name(RRDSET *st) {
109         // fprintf(stderr, "ADDING: %s (name: %s)\n", st->id, st->name);
110         return avl_insert(&rrdset_root_index_name, (avl *)(&st->avlname));
111 }
112
113 #define rrdset_index_del_name(st) avl_remove(&rrdset_root_index_name, (avl *)(&st->avlname))
114
115 static RRDSET *rrdset_index_find_name(const char *name, uint32_t hash) {
116         void *result = NULL;
117         RRDSET tmp;
118         tmp.name = name;
119         tmp.hash_name = (hash)?hash:simple_hash(tmp.name);
120
121         // fprintf(stderr, "SEARCHING: %s\n", name);
122         avl_search(&(rrdset_root_index_name), (avl *)(&(tmp.avlname)), rrdset_iterator_name, (avl **)&result);
123         if(result) {
124                 RRDSET *st = rrdset_from_avlname(result);
125                 if(strcmp(st->magic, RRDSET_MAGIC))
126                         error("Search for RRDSET %s returned an invalid RRDSET %s (name %s)", name, st->id, st->name);
127
128                 // fprintf(stderr, "FOUND: %s\n", name);
129                 return rrdset_from_avlname(result);
130         }
131         // fprintf(stderr, "NOT FOUND: %s\n", name);
132         return NULL;
133 }
134
135
136 // ----------------------------------------------------------------------------
137 // RRDDIM index
138
139 static int rrddim_iterator(avl *a) { if(a) {}; return 0; }
140
141 static int rrddim_compare(void* a, void* b) {
142         if(((RRDDIM *)a)->hash < ((RRDDIM *)b)->hash) return -1;
143         else if(((RRDDIM *)a)->hash > ((RRDDIM *)b)->hash) return 1;
144         else return strcmp(((RRDDIM *)a)->id, ((RRDDIM *)b)->id);
145 }
146
147 #define rrddim_index_add(st, rd) avl_insert(&((st)->dimensions_index), (avl *)(rd))
148 #define rrddim_index_del(st,rd ) avl_remove(&((st)->dimensions_index), (avl *)(rd))
149
150 static RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) {
151         RRDDIM *result = NULL, tmp;
152         strncpy(tmp.id, id, RRD_ID_LENGTH_MAX);
153         tmp.id[RRD_ID_LENGTH_MAX] = '\0';
154         tmp.hash = (hash)?hash:simple_hash(tmp.id);
155
156         avl_search(&(st->dimensions_index), (avl *)&tmp, rrddim_iterator, (avl **)&result);
157         return result;
158 }
159
160 // ----------------------------------------------------------------------------
161 // chart types
162
163 int rrdset_type_id(const char *name)
164 {
165         if(unlikely(strcmp(name, RRDSET_TYPE_AREA_NAME) == 0)) return RRDSET_TYPE_AREA;
166         else if(unlikely(strcmp(name, RRDSET_TYPE_STACKED_NAME) == 0)) return RRDSET_TYPE_STACKED;
167         else if(unlikely(strcmp(name, RRDSET_TYPE_LINE_NAME) == 0)) return RRDSET_TYPE_LINE;
168         return RRDSET_TYPE_LINE;
169 }
170
171 const char *rrdset_type_name(int chart_type)
172 {
173         static char line[] = RRDSET_TYPE_LINE_NAME;
174         static char area[] = RRDSET_TYPE_AREA_NAME;
175         static char stacked[] = RRDSET_TYPE_STACKED_NAME;
176
177         switch(chart_type) {
178                 case RRDSET_TYPE_LINE:
179                         return line;
180
181                 case RRDSET_TYPE_AREA:
182                         return area;
183
184                 case RRDSET_TYPE_STACKED:
185                         return stacked;
186         }
187         return line;
188 }
189
190 // ----------------------------------------------------------------------------
191 // load / save
192
193 const char *rrd_memory_mode_name(int id)
194 {
195         static const char ram[] = RRD_MEMORY_MODE_RAM_NAME;
196         static const char map[] = RRD_MEMORY_MODE_MAP_NAME;
197         static const char save[] = RRD_MEMORY_MODE_SAVE_NAME;
198
199         switch(id) {
200                 case RRD_MEMORY_MODE_RAM:
201                         return ram;
202
203                 case RRD_MEMORY_MODE_MAP:
204                         return map;
205
206                 case RRD_MEMORY_MODE_SAVE:
207                 default:
208                         return save;
209         }
210
211         return save;
212 }
213
214 int rrd_memory_mode_id(const char *name)
215 {
216         if(unlikely(!strcmp(name, RRD_MEMORY_MODE_RAM_NAME)))
217                 return RRD_MEMORY_MODE_RAM;
218         else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_MAP_NAME)))
219                 return RRD_MEMORY_MODE_MAP;
220
221         return RRD_MEMORY_MODE_SAVE;
222 }
223
224 // ----------------------------------------------------------------------------
225 // algorithms types
226
227 int rrddim_algorithm_id(const char *name)
228 {
229         if(strcmp(name, RRDDIM_INCREMENTAL_NAME) == 0)                  return RRDDIM_INCREMENTAL;
230         if(strcmp(name, RRDDIM_ABSOLUTE_NAME) == 0)                     return RRDDIM_ABSOLUTE;
231         if(strcmp(name, RRDDIM_PCENT_OVER_ROW_TOTAL_NAME) == 0)                 return RRDDIM_PCENT_OVER_ROW_TOTAL;
232         if(strcmp(name, RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME) == 0)        return RRDDIM_PCENT_OVER_DIFF_TOTAL;
233         return RRDDIM_ABSOLUTE;
234 }
235
236 const char *rrddim_algorithm_name(int chart_type)
237 {
238         static char absolute[] = RRDDIM_ABSOLUTE_NAME;
239         static char incremental[] = RRDDIM_INCREMENTAL_NAME;
240         static char percentage_of_absolute_row[] = RRDDIM_PCENT_OVER_ROW_TOTAL_NAME;
241         static char percentage_of_incremental_row[] = RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME;
242
243         switch(chart_type) {
244                 case RRDDIM_ABSOLUTE:
245                         return absolute;
246
247                 case RRDDIM_INCREMENTAL:
248                         return incremental;
249
250                 case RRDDIM_PCENT_OVER_ROW_TOTAL:
251                         return percentage_of_absolute_row;
252
253                 case RRDDIM_PCENT_OVER_DIFF_TOTAL:
254                         return percentage_of_incremental_row;
255         }
256         return absolute;
257 }
258
259 // ----------------------------------------------------------------------------
260 // chart names
261
262 char *rrdset_strncpy_name(char *to, const char *from, int length)
263 {
264         int i;
265         for(i = 0; i < length && from[i] ;i++) {
266                 if(from[i] == '.' || isalpha(from[i]) || isdigit(from[i])) to[i] = from[i];
267                 else to[i] = '_';
268         }
269         if(i < length) to[i] = '\0';
270         to[length - 1] = '\0';
271
272         return to;
273 }
274
275 void rrdset_set_name(RRDSET *st, const char *name)
276 {
277         debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name);
278
279         if(st->name) rrdset_index_del_name(st);
280
281         char b[CONFIG_MAX_VALUE + 1];
282         char n[RRD_ID_LENGTH_MAX + 1];
283
284         snprintf(n, RRD_ID_LENGTH_MAX, "%s.%s", st->type, name);
285         rrdset_strncpy_name(b, n, CONFIG_MAX_VALUE);
286         st->name = config_get(st->id, "name", b);
287         st->hash_name = simple_hash(st->name);
288
289         rrdset_index_add_name(st);
290 }
291
292 // ----------------------------------------------------------------------------
293 // cache directory
294
295 char *rrdset_cache_dir(const char *id)
296 {
297         char *ret = NULL;
298
299         static char *cache_dir = NULL;
300         if(!cache_dir) cache_dir = config_get("global", "cache directory", CACHE_DIR);
301
302         char b[FILENAME_MAX + 1];
303         char n[FILENAME_MAX + 1];
304         rrdset_strncpy_name(b, id, FILENAME_MAX);
305
306         snprintf(n, FILENAME_MAX, "%s/%s", cache_dir, b);
307         ret = config_get(id, "cache directory", n);
308
309         if(rrd_memory_mode == RRD_MEMORY_MODE_MAP || rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
310                 int r = mkdir(ret, 0775);
311                 if(r != 0 && errno != EEXIST)
312                         error("Cannot create directory '%s'", ret);
313         }
314
315         return ret;
316 }
317
318 // ----------------------------------------------------------------------------
319 // core functions
320
321 void rrdset_reset(RRDSET *st)
322 {
323         debug(D_RRD_CALLS, "rrdset_reset() %s", st->name);
324
325         st->last_collected_time.tv_sec = 0;
326         st->last_collected_time.tv_usec = 0;
327         st->last_updated.tv_sec = 0;
328         st->last_updated.tv_usec = 0;
329         st->current_entry = 0;
330         st->counter = 0;
331         st->counter_done = 0;
332
333         RRDDIM *rd;
334         for(rd = st->dimensions; rd ; rd = rd->next) {
335                 rd->last_collected_time.tv_sec = 0;
336                 rd->last_collected_time.tv_usec = 0;
337                 rd->counter = 0;
338                 bzero(rd->values, rd->entries * sizeof(storage_number));
339         }
340 }
341
342 RRDSET *rrdset_create(const char *type, const char *id, const char *name, const char *family, const char *context, const char *title, const char *units, long priority, int update_every, int chart_type)
343 {
344         if(!type || !type[0]) {
345                 fatal("Cannot create rrd stats without a type.");
346                 return NULL;
347         }
348
349         if(!id || !id[0]) {
350                 fatal("Cannot create rrd stats without an id.");
351                 return NULL;
352         }
353
354         char fullid[RRD_ID_LENGTH_MAX + 1];
355         char fullfilename[FILENAME_MAX + 1];
356         RRDSET *st = NULL;
357
358         snprintf(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id);
359
360         st = rrdset_find(fullid);
361         if(st) {
362                 error("Cannot create rrd stats for '%s', it already exists.", fullid);
363                 return st;
364         }
365
366         long entries = config_get_number(fullid, "history", rrd_default_history_entries);
367         if(entries < 5) entries = config_set_number(fullid, "history", 5);
368         if(entries > RRD_HISTORY_ENTRIES_MAX) entries = config_set_number(fullid, "history", RRD_HISTORY_ENTRIES_MAX);
369
370         int enabled = config_get_boolean(fullid, "enabled", 1);
371         if(!enabled) entries = 5;
372
373         unsigned long size = sizeof(RRDSET);
374         char *cache_dir = rrdset_cache_dir(fullid);
375
376         debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
377
378         snprintf(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
379         if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) st = (RRDSET *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 0);
380         if(st) {
381                 if(strcmp(st->magic, RRDSET_MAGIC) != 0) {
382                         errno = 0;
383                         info("Initializing file %s.", fullfilename);
384                         bzero(st, size);
385                 }
386                 else if(strcmp(st->id, fullid) != 0) {
387                         errno = 0;
388                         error("File %s contents are not for chart %s. Clearing it.", fullfilename, fullid);
389                         // munmap(st, size);
390                         // st = NULL;
391                         bzero(st, size);
392                 }
393                 else if(st->memsize != size || st->entries != entries) {
394                         errno = 0;
395                         error("File %s does not have the desired size. Clearing it.", fullfilename);
396                         bzero(st, size);
397                 }
398                 else if(st->update_every != update_every) {
399                         errno = 0;
400                         error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
401                         bzero(st, size);
402                 }
403                 else if((time(NULL) - st->last_updated.tv_sec) > update_every * entries) {
404                         errno = 0;
405                         error("File %s is too old. Clearing it.", fullfilename);
406                         bzero(st, size);
407                 }
408         }
409
410         if(st) {
411                 st->name = NULL;
412                 st->type = NULL;
413                 st->family = NULL;
414                 st->context = NULL;
415                 st->title = NULL;
416                 st->units = NULL;
417                 st->dimensions = NULL;
418                 st->next = NULL;
419                 st->mapped = rrd_memory_mode;
420         }
421         else {
422                 st = calloc(1, size);
423                 if(!st) {
424                         fatal("Cannot allocate memory for RRD_STATS %s.%s", type, id);
425                         return NULL;
426                 }
427                 st->mapped = RRD_MEMORY_MODE_RAM;
428         }
429         st->memsize = size;
430         st->entries = entries;
431         st->update_every = update_every;
432
433         strcpy(st->cache_filename, fullfilename);
434         strcpy(st->magic, RRDSET_MAGIC);
435
436         strcpy(st->id, fullid);
437         st->hash = simple_hash(st->id);
438
439         st->cache_dir = cache_dir;
440
441         st->chart_type = rrdset_type_id(config_get(st->id, "chart type", rrdset_type_name(chart_type)));
442         st->type       = config_get(st->id, "type", type);
443         st->family     = config_get(st->id, "family", family?family:st->type);
444         st->context    = config_get(st->id, "context", context?context:st->id);
445         st->units      = config_get(st->id, "units", units?units:"");
446
447         st->priority = config_get_number(st->id, "priority", priority);
448         st->enabled = enabled;
449
450         st->isdetail = 0;
451         st->debug = 0;
452
453         st->last_collected_time.tv_sec = 0;
454         st->last_collected_time.tv_usec = 0;
455         st->counter_done = 0;
456
457         st->gap_when_lost_iterations_above = (int) (
458                         config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);
459
460         avl_init(&st->dimensions_index, rrddim_compare);
461
462         pthread_rwlock_init(&st->rwlock, NULL);
463         pthread_rwlock_wrlock(&rrdset_root_rwlock);
464
465         if(name && *name) rrdset_set_name(st, name);
466         else rrdset_set_name(st, id);
467
468         {
469                 char varvalue[CONFIG_MAX_VALUE + 1];
470                 snprintf(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name);
471                 st->title = config_get(st->id, "title", varvalue);
472         }
473
474         st->next = rrdset_root;
475         rrdset_root = st;
476
477         rrdset_index_add(st);
478
479         pthread_rwlock_unlock(&rrdset_root_rwlock);
480
481         return(st);
482 }
483
484 RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier, long divisor, int algorithm)
485 {
486         char filename[FILENAME_MAX + 1];
487         char fullfilename[FILENAME_MAX + 1];
488
489         char varname[CONFIG_MAX_NAME + 1];
490         RRDDIM *rd = NULL;
491         unsigned long size = sizeof(RRDDIM) + (st->entries * sizeof(storage_number));
492
493         debug(D_RRD_CALLS, "Adding dimension '%s/%s'.", st->id, id);
494
495         rrdset_strncpy_name(filename, id, FILENAME_MAX);
496         snprintf(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
497         if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) rd = (RRDDIM *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 1);
498         if(rd) {
499                 struct timeval now;
500                 gettimeofday(&now, NULL);
501
502                 if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) {
503                         errno = 0;
504                         info("Initializing file %s.", fullfilename);
505                         bzero(rd, size);
506                 }
507                 else if(rd->memsize != size) {
508                         errno = 0;
509                         error("File %s does not have the desired size. Clearing it.", fullfilename);
510                         bzero(rd, size);
511                 }
512                 else if(rd->multiplier != multiplier) {
513                         errno = 0;
514                         error("File %s does not have the same multiplier. Clearing it.", fullfilename);
515                         bzero(rd, size);
516                 }
517                 else if(rd->divisor != divisor) {
518                         errno = 0;
519                         error("File %s does not have the same divisor. Clearing it.", fullfilename);
520                         bzero(rd, size);
521                 }
522                 else if(rd->algorithm != algorithm) {
523                         errno = 0;
524                         error("File %s does not have the same algorithm. Clearing it.", fullfilename);
525                         bzero(rd, size);
526                 }
527                 else if(rd->update_every != st->update_every) {
528                         errno = 0;
529                         error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
530                         bzero(rd, size);
531                 }
532                 else if(usecdiff(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * 1000000ULL)) {
533                         errno = 0;
534                         error("File %s is too old. Clearing it.", fullfilename);
535                         bzero(rd, size);
536                 }
537                 else if(strcmp(rd->id, id) != 0) {
538                         errno = 0;
539                         error("File %s contents are not for dimension %s. Clearing it.", fullfilename, id);
540                         // munmap(rd, size);
541                         // rd = NULL;
542                         bzero(rd, size);
543                 }
544         }
545
546         if(rd) {
547                 // we have a file mapped for rd
548                 rd->mapped = rrd_memory_mode;
549                 rd->flags = 0x00000000;
550                 rd->next = NULL;
551                 rd->name = NULL;
552         }
553         else {
554                 // if we didn't manage to get a mmap'd dimension, just create one
555
556                 rd = calloc(1, size);
557                 if(!rd) {
558                         fatal("Cannot allocate RRD_DIMENSION %s/%s.", st->id, id);
559                         return NULL;
560                 }
561
562                 rd->mapped = RRD_MEMORY_MODE_RAM;
563         }
564         rd->memsize = size;
565
566         strcpy(rd->magic, RRDDIMENSION_MAGIC);
567         strcpy(rd->cache_filename, fullfilename);
568         strncpy(rd->id, id, RRD_ID_LENGTH_MAX);
569         rd->hash = simple_hash(rd->id);
570
571         snprintf(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
572         rd->name = config_get(st->id, varname, (name && *name)?name:rd->id);
573
574         snprintf(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id);
575         rd->algorithm = rrddim_algorithm_id(config_get(st->id, varname, rrddim_algorithm_name(algorithm)));
576
577         snprintf(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id);
578         rd->multiplier = config_get_number(st->id, varname, multiplier);
579
580         snprintf(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id);
581         rd->divisor = config_get_number(st->id, varname, divisor);
582         if(!rd->divisor) rd->divisor = 1;
583
584         rd->entries = st->entries;
585         rd->update_every = st->update_every;
586
587         // prevent incremental calculation spikes
588         rd->counter = 0;
589
590         // append this dimension
591         pthread_rwlock_wrlock(&st->rwlock);
592         if(!st->dimensions)
593                 st->dimensions = rd;
594         else {
595                 RRDDIM *td = st->dimensions;
596                 for(; td->next; td = td->next) ;
597                 td->next = rd;
598         }
599         pthread_rwlock_unlock(&st->rwlock);
600
601         rrddim_index_add(st, rd);
602
603         return(rd);
604 }
605
606 void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name)
607 {
608         debug(D_RRD_CALLS, "rrddim_set_name() %s.%s", st->name, rd->name);
609
610         char varname[CONFIG_MAX_NAME + 1];
611         snprintf(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
612         config_set_default(st->id, varname, name);
613 }
614
615 void rrddim_free(RRDSET *st, RRDDIM *rd)
616 {
617         debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name);
618
619         RRDDIM *i, *last = NULL;
620         for(i = st->dimensions; i && i != rd ; i = i->next) last = i;
621
622         if(!i) {
623                 error("Request to free dimension %s.%s but it is not linked.", st->id, rd->name);
624                 return;
625         }
626
627         if(last) last->next = rd->next;
628         else st->dimensions = rd->next;
629         rd->next = NULL;
630
631         rrddim_index_del(st, rd);
632
633         // free(rd->annotations);
634         if(rd->mapped == RRD_MEMORY_MODE_SAVE) {
635                 debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
636                 savememory(rd->cache_filename, rd, rd->memsize);
637
638                 debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
639                 munmap(rd, rd->memsize);
640         }
641         else if(rd->mapped == RRD_MEMORY_MODE_MAP) {
642                 debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
643                 munmap(rd, rd->memsize);
644         }
645         else {
646                 debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
647                 free(rd);
648         }
649 }
650
651 void rrdset_free_all(void)
652 {
653         info("Freeing all memory...");
654
655         RRDSET *st;
656         for(st = rrdset_root; st ;) {
657                 RRDSET *next = st->next;
658
659                 while(st->dimensions)
660                         rrddim_free(st, st->dimensions);
661
662                 rrdset_index_del(st);
663
664                 if(st->mapped == RRD_MEMORY_MODE_SAVE) {
665                         debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
666                         savememory(st->cache_filename, st, st->memsize);
667
668                         debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
669                         munmap(st, st->memsize);
670                 }
671                 else if(st->mapped == RRD_MEMORY_MODE_MAP) {
672                         debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
673                         munmap(st, st->memsize);
674                 }
675                 else
676                         free(st);
677
678                 st = next;
679         }
680         rrdset_root = NULL;
681
682         info("Memory cleanup completed...");
683 }
684
685 void rrdset_save_all(void)
686 {
687         debug(D_RRD_CALLS, "rrdset_save_all()");
688
689         // let it log a few error messages
690         error_log_limit_reset();
691
692         RRDSET *st;
693         RRDDIM *rd;
694
695         pthread_rwlock_wrlock(&rrdset_root_rwlock);
696         for(st = rrdset_root; st ; st = st->next) {
697                 pthread_rwlock_wrlock(&st->rwlock);
698
699                 if(st->mapped == RRD_MEMORY_MODE_SAVE) {
700                         debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
701                         savememory(st->cache_filename, st, st->memsize);
702                 }
703
704                 for(rd = st->dimensions; rd ; rd = rd->next) {
705                         if(likely(rd->mapped == RRD_MEMORY_MODE_SAVE)) {
706                                 debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
707                                 savememory(rd->cache_filename, rd, rd->memsize);
708                         }
709                 }
710
711                 pthread_rwlock_unlock(&st->rwlock);
712         }
713         pthread_rwlock_unlock(&rrdset_root_rwlock);
714 }
715
716
717 RRDSET *rrdset_find(const char *id)
718 {
719         debug(D_RRD_CALLS, "rrdset_find() for chart %s", id);
720
721         RRDSET *st = rrdset_index_find(id, 0);
722         return(st);
723 }
724
725 RRDSET *rrdset_find_bytype(const char *type, const char *id)
726 {
727         debug(D_RRD_CALLS, "rrdset_find_bytype() for chart %s.%s", type, id);
728
729         char buf[RRD_ID_LENGTH_MAX + 1];
730
731         strncpy(buf, type, RRD_ID_LENGTH_MAX - 1);
732         buf[RRD_ID_LENGTH_MAX - 1] = '\0';
733         strcat(buf, ".");
734         int len = (int) strlen(buf);
735         strncpy(&buf[len], id, (size_t) (RRD_ID_LENGTH_MAX - len));
736         buf[RRD_ID_LENGTH_MAX] = '\0';
737
738         return(rrdset_find(buf));
739 }
740
741 RRDSET *rrdset_find_byname(const char *name)
742 {
743         debug(D_RRD_CALLS, "rrdset_find_byname() for chart %s", name);
744
745         RRDSET *st = rrdset_index_find_name(name, 0);
746         return(st);
747 }
748
749 RRDDIM *rrddim_find(RRDSET *st, const char *id)
750 {
751         debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", st->name, id);
752
753         return rrddim_index_find(st, id, 0);
754 }
755
756 int rrddim_hide(RRDSET *st, const char *id)
757 {
758         debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", st->name, id);
759
760         RRDDIM *rd = rrddim_find(st, id);
761         if(unlikely(!rd)) {
762                 error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
763                 return 1;
764         }
765
766         rd->flags |= RRDDIM_FLAG_HIDDEN;
767         return 0;
768 }
769
770 int rrddim_unhide(RRDSET *st, const char *id)
771 {
772         debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", st->name, id);
773
774         RRDDIM *rd = rrddim_find(st, id);
775         if(unlikely(!rd)) {
776                 error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
777                 return 1;
778         }
779
780         if(rd->flags & RRDDIM_FLAG_HIDDEN) rd->flags ^= RRDDIM_FLAG_HIDDEN;
781         return 0;
782 }
783
784 collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
785 {
786         debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value);
787
788         gettimeofday(&rd->last_collected_time, NULL);
789         rd->collected_value = value;
790         rd->updated = 1;
791         rd->counter++;
792
793         return rd->last_collected_value;
794 }
795
796 collected_number rrddim_set(RRDSET *st, const char *id, collected_number value)
797 {
798         RRDDIM *rd = rrddim_find(st, id);
799         if(unlikely(!rd)) {
800                 error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
801                 return 0;
802         }
803
804         return rrddim_set_by_pointer(st, rd, value);
805 }
806
807 void rrdset_next_usec(RRDSET *st, unsigned long long microseconds)
808 {
809         if(!microseconds) rrdset_next(st);
810         else {
811                 debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
812
813                 if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
814                 st->usec_since_last_update = microseconds;
815         }
816 }
817
818 void rrdset_next(RRDSET *st)
819 {
820         unsigned long long microseconds = 0;
821
822         if(likely(st->last_collected_time.tv_sec)) {
823                 struct timeval now;
824                 gettimeofday(&now, NULL);
825                 microseconds = usecdiff(&now, &st->last_collected_time);
826         }
827         // prevent infinite loop
828         else microseconds = st->update_every * 1000000ULL;
829
830         rrdset_next_usec(st, microseconds);
831 }
832
833 void rrdset_next_plugins(RRDSET *st)
834 {
835         rrdset_next(st);
836 }
837
838 unsigned long long rrdset_done(RRDSET *st)
839 {
840         debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name);
841
842         RRDDIM *rd, *last;
843         int oldstate, store_this_entry = 1, first_entry = 0;
844         unsigned long long last_ut, now_ut, next_ut, stored_entries = 0;
845
846         if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
847                 error("Cannot set pthread cancel state to DISABLE.");
848
849         // a read lock is OK here
850         pthread_rwlock_rdlock(&st->rwlock);
851
852         // enable the chart, if it was disabled
853         if(unlikely(rrd_delete_unupdated_dimensions) && !st->enabled)
854                 st->enabled = 1;
855
856         // check if the chart has a long time to be updated
857         if(unlikely(st->usec_since_last_update > st->entries * st->update_every * 1000000ULL)) {
858                 info("%s: took too long to be updated (%0.3Lf secs). Reseting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0));
859                 rrdset_reset(st);
860                 st->usec_since_last_update = st->update_every * 1000000ULL;
861                 first_entry = 1;
862         }
863         if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: microseconds since last update: %llu", st->name, st->usec_since_last_update);
864
865         // set last_collected_time
866         if(unlikely(!st->last_collected_time.tv_sec)) {
867                 // it is the first entry
868                 // set the last_collected_time to now
869                 gettimeofday(&st->last_collected_time, NULL);
870
871                 // the first entry should not be stored
872                 store_this_entry = 0;
873                 first_entry = 1;
874
875                 if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: has not set last_collected_time. Setting it now. Will not store the next entry.", st->name);
876         }
877         else {
878                 // it is not the first entry
879                 // calculate the proper last_collected_time, using usec_since_last_update
880                 unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec + st->usec_since_last_update;
881                 st->last_collected_time.tv_sec = (time_t) (ut / 1000000ULL);
882                 st->last_collected_time.tv_usec = (useconds_t) (ut % 1000000ULL);
883         }
884
885         // if this set has not been updated in the past
886         // we fake the last_update time to be = now - usec_since_last_update
887         if(unlikely(!st->last_updated.tv_sec)) {
888                 // it has never been updated before
889                 // set a fake last_updated, in the past using usec_since_last_update
890                 unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
891                 st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
892                 st->last_updated.tv_usec = (useconds_t) (ut % 1000000ULL);
893
894                 // the first entry should not be stored
895                 store_this_entry = 0;
896                 first_entry = 1;
897
898                 if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: initializing last_updated to now - %llu microseconds (%0.3Lf). Will not store the next entry.", st->name, st->usec_since_last_update, (long double)ut/1000000.0);
899         }
900
901         // check if we will re-write the entire data set
902         if(unlikely(usecdiff(&st->last_collected_time, &st->last_updated) > st->update_every * st->entries * 1000000ULL)) {
903                 info("%s: too old data (last updated at %u.%u, last collected at %u.%u). Reseting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec);
904                 rrdset_reset(st);
905
906                 st->usec_since_last_update = st->update_every * 1000000ULL;
907
908                 gettimeofday(&st->last_collected_time, NULL);
909
910                 unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
911                 st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
912                 st->last_updated.tv_usec = (useconds_t) (ut % 1000000ULL);
913
914                 // the first entry should not be stored
915                 store_this_entry = 0;
916                 first_entry = 1;
917         }
918
919         // these are the 3 variables that will help us in interpolation
920         // last_ut = the last time we added a value to the storage
921         //  now_ut = the time the current value is taken at
922         // next_ut = the time of the next interpolation point
923         last_ut = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
924         now_ut  = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec;
925         next_ut = (st->last_updated.tv_sec + st->update_every) * 1000000ULL;
926
927         if(unlikely(!first_entry && now_ut < next_ut)) {
928                 if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name);
929         }
930
931         if(unlikely(st->debug)) {
932                 debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
933                 debug(D_RRD_STATS, "%s: now  ut = %0.3Lf (current update time)", st->name, (long double)now_ut/1000000.0);
934                 debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
935         }
936
937         if(unlikely(!st->counter_done)) {
938                 store_this_entry = 0;
939                 if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: Will not store the next entry.", st->name);
940         }
941         st->counter_done++;
942
943         // calculate totals and count the dimensions
944         int dimensions;
945         st->collected_total = 0;
946         for( rd = st->dimensions, dimensions = 0 ; likely(rd) ; rd = rd->next, dimensions++ )
947                 st->collected_total += rd->collected_value;
948
949         uint32_t storage_flags = SN_EXISTS;
950
951         // process all dimensions to calculate their values
952         // based on the collected figures only
953         // at this stage we do not interpolate anything
954         for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
955
956                 if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: START "
957                         " last_collected_value = " COLLECTED_NUMBER_FORMAT
958                         " collected_value = " COLLECTED_NUMBER_FORMAT
959                         " last_calculated_value = " CALCULATED_NUMBER_FORMAT
960                         " calculated_value = " CALCULATED_NUMBER_FORMAT
961                         , st->id, rd->name
962                         , rd->last_collected_value
963                         , rd->collected_value
964                         , rd->last_calculated_value
965                         , rd->calculated_value
966                         );
967
968                 switch(rd->algorithm) {
969                 case RRDDIM_ABSOLUTE:
970                         rd->calculated_value = (calculated_number)rd->collected_value
971                                 * (calculated_number)rd->multiplier
972                                 / (calculated_number)rd->divisor;
973
974                         if(unlikely(st->debug))
975                                 debug(D_RRD_STATS, "%s/%s: CALC ABS/ABS-NO-IN "
976                                         CALCULATED_NUMBER_FORMAT " = "
977                                         COLLECTED_NUMBER_FORMAT
978                                         " * " CALCULATED_NUMBER_FORMAT
979                                         " / " CALCULATED_NUMBER_FORMAT
980                                         , st->id, rd->name
981                                         , rd->calculated_value
982                                         , rd->collected_value
983                                         , (calculated_number)rd->multiplier
984                                         , (calculated_number)rd->divisor
985                                         );
986                         break;
987
988                         case RRDDIM_PCENT_OVER_ROW_TOTAL:
989                                 if(unlikely(!st->collected_total)) rd->calculated_value = 0;
990                                 else
991                                 // the percentage of the current value
992                                 // over the total of all dimensions
993                                 rd->calculated_value =
994                                           (calculated_number)100
995                                         * (calculated_number)rd->collected_value
996                                         / (calculated_number)st->collected_total;
997
998                                 if(unlikely(st->debug))
999                                         debug(D_RRD_STATS, "%s/%s: CALC PCENT-ROW "
1000                                                 CALCULATED_NUMBER_FORMAT " = 100"
1001                                                 " * " COLLECTED_NUMBER_FORMAT
1002                                                 " / " COLLECTED_NUMBER_FORMAT
1003                                                 , st->id, rd->name
1004                                                 , rd->calculated_value
1005                                                 , rd->collected_value
1006                                                 , st->collected_total
1007                                                 );
1008                                 break;
1009
1010                         case RRDDIM_INCREMENTAL:
1011                                 if(unlikely(!rd->updated || rd->counter <= 1)) {
1012                                         rd->calculated_value = 0;
1013                                         continue;
1014                                 }
1015
1016                                 // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
1017                                 // to reset the calculation (it will give zero as the calculation for this second)
1018                                 if(unlikely(rd->last_collected_value > rd->collected_value)) {
1019                                         debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
1020                                                         , st->name, rd->name
1021                                                         , rd->last_collected_value
1022                                                         , rd->collected_value);
1023                                         if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
1024                                         rd->last_collected_value = rd->collected_value;
1025                                 }
1026
1027                                 rd->calculated_value = (calculated_number)(rd->collected_value - rd->last_collected_value)
1028                                         * (calculated_number)rd->multiplier
1029                                         / (calculated_number)rd->divisor;
1030
1031                                 if(unlikely(st->debug))
1032                                         debug(D_RRD_STATS, "%s/%s: CALC INC PRE "
1033                                                 CALCULATED_NUMBER_FORMAT " = ("
1034                                                 COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT
1035                                                 ")"
1036                                                 " * " CALCULATED_NUMBER_FORMAT
1037                                                 " / " CALCULATED_NUMBER_FORMAT
1038                                                 , st->id, rd->name
1039                                                 , rd->calculated_value
1040                                                 , rd->collected_value, rd->last_collected_value
1041                                                 , (calculated_number)rd->multiplier
1042                                                 , (calculated_number)rd->divisor
1043                                                 );
1044                                 break;
1045
1046                         case RRDDIM_PCENT_OVER_DIFF_TOTAL:
1047                                 if(unlikely(!rd->updated || rd->counter <= 1)) {
1048                                         rd->calculated_value = 0;
1049                                         continue;
1050                                 }
1051
1052                                 // the percentage of the current increment
1053                                 // over the increment of all dimensions together
1054                                 if(unlikely(st->collected_total == st->last_collected_total)) rd->calculated_value = rd->last_calculated_value;
1055                                 else rd->calculated_value =
1056                                           (calculated_number)100
1057                                         * (calculated_number)(rd->collected_value - rd->last_collected_value)
1058                                         / (calculated_number)(st->collected_total  - st->last_collected_total);
1059
1060                                 if(unlikely(st->debug))
1061                                         debug(D_RRD_STATS, "%s/%s: CALC PCENT-DIFF "
1062                                                 CALCULATED_NUMBER_FORMAT " = 100"
1063                                                 " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
1064                                                 " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
1065                                                 , st->id, rd->name
1066                                                 , rd->calculated_value
1067                                                 , rd->collected_value, rd->last_collected_value
1068                                                 , st->collected_total, st->last_collected_total
1069                                                 );
1070                                 break;
1071
1072                         default:
1073                                 // make the default zero, to make sure
1074                                 // it gets noticed when we add new types
1075                                 rd->calculated_value = 0;
1076
1077                                 if(unlikely(st->debug))
1078                                         debug(D_RRD_STATS, "%s/%s: CALC "
1079                                                 CALCULATED_NUMBER_FORMAT " = 0"
1080                                                 , st->id, rd->name
1081                                                 , rd->calculated_value
1082                                                 );
1083                                 break;
1084                 }
1085
1086                 if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: PHASE2 "
1087                         " last_collected_value = " COLLECTED_NUMBER_FORMAT
1088                         " collected_value = " COLLECTED_NUMBER_FORMAT
1089                         " last_calculated_value = " CALCULATED_NUMBER_FORMAT
1090                         " calculated_value = " CALCULATED_NUMBER_FORMAT
1091                         , st->id, rd->name
1092                         , rd->last_collected_value
1093                         , rd->collected_value
1094                         , rd->last_calculated_value
1095                         , rd->calculated_value
1096                         );
1097
1098         }
1099
1100         // at this point we have all the calculated values ready
1101         // it is now time to interpolate values on a second boundary
1102
1103         unsigned long long first_ut = last_ut;
1104         long long iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL);
1105         if((now_ut % (st->update_every * 1000000ULL)) == 0) iterations++;
1106
1107         for( ; likely(next_ut <= now_ut) ; next_ut += st->update_every * 1000000ULL, iterations-- ) {
1108 #ifdef NETDATA_INTERNAL_CHECKS
1109                 if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_ut = %llu, next_ut = %llu, now_ut = %llu", st->name, first_ut, last_ut, next_ut, now_ut); }
1110 #endif
1111
1112                 if(unlikely(st->debug)) {
1113                         debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
1114                         debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
1115                 }
1116
1117                 st->last_updated.tv_sec = (time_t) (next_ut / 1000000ULL);
1118                 st->last_updated.tv_usec = 0;
1119
1120                 for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
1121                         calculated_number new_value;
1122
1123                         switch(rd->algorithm) {
1124                                 case RRDDIM_INCREMENTAL:
1125                                         new_value = (calculated_number)
1126                                                 (          rd->calculated_value
1127                                                         * (calculated_number)(next_ut - last_ut)
1128                                                         / (calculated_number)(now_ut - last_ut)
1129                                                 );
1130
1131                                         if(unlikely(st->debug))
1132                                                 debug(D_RRD_STATS, "%s/%s: CALC2 INC "
1133                                                         CALCULATED_NUMBER_FORMAT " = "
1134                                                         CALCULATED_NUMBER_FORMAT
1135                                                         " * %llu"
1136                                                         " / %llu"
1137                                                         , st->id, rd->name
1138                                                         , new_value
1139                                                         , rd->calculated_value
1140                                                         , (next_ut - last_ut)
1141                                                         , (now_ut - last_ut)
1142                                                         );
1143
1144                                         rd->calculated_value -= new_value;
1145                                         new_value += rd->last_calculated_value;
1146                                         rd->last_calculated_value = 0;
1147                                         new_value /= (calculated_number)st->update_every;
1148                                         break;
1149
1150                                 case RRDDIM_ABSOLUTE:
1151                                 case RRDDIM_PCENT_OVER_ROW_TOTAL:
1152                                 case RRDDIM_PCENT_OVER_DIFF_TOTAL:
1153                                 default:
1154                                         if(iterations == 1) {
1155                                                 // this is the last iteration
1156                                                 // do not interpolate
1157                                                 // just show the calculated value
1158
1159                                                 new_value = rd->calculated_value;
1160                                         }
1161                                         else {
1162                                                 // we have missed an update
1163                                                 // interpolate in the middle values
1164
1165                                                 new_value = (calculated_number)
1166                                                         (       (         (rd->calculated_value - rd->last_calculated_value)
1167                                                                         * (calculated_number)(next_ut - first_ut)
1168                                                                         / (calculated_number)(now_ut - first_ut)
1169                                                                 )
1170                                                                 +  rd->last_calculated_value
1171                                                         );
1172
1173                                                 if(unlikely(st->debug))
1174                                                         debug(D_RRD_STATS, "%s/%s: CALC2 DEF "
1175                                                                 CALCULATED_NUMBER_FORMAT " = ((("
1176                                                                 "(" CALCULATED_NUMBER_FORMAT " - " CALCULATED_NUMBER_FORMAT ")"
1177                                                                 " * %llu"
1178                                                                 " / %llu) + " CALCULATED_NUMBER_FORMAT
1179                                                                 , st->id, rd->name
1180                                                                 , new_value
1181                                                                 , rd->calculated_value, rd->last_calculated_value
1182                                                                 , (next_ut - first_ut)
1183                                                                 , (now_ut - first_ut), rd->last_calculated_value
1184                                                                 );
1185
1186                                                 // this is wrong
1187                                                 // it fades the value towards the target
1188                                                 // while we know the calculated value is different
1189                                                 // if(likely(next_ut + st->update_every * 1000000ULL > now_ut)) rd->calculated_value = new_value;
1190                                         }
1191                                         break;
1192                         }
1193
1194                         if(unlikely(!store_this_entry)) {
1195                                 store_this_entry = 1;
1196                                 continue;
1197                         }
1198
1199                         if(likely(rd->updated && rd->counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
1200                                 rd->values[st->current_entry] = pack_storage_number(new_value, storage_flags );
1201
1202                                 if(unlikely(st->debug))
1203                                         debug(D_RRD_STATS, "%s/%s: STORE[%ld] "
1204                                                 CALCULATED_NUMBER_FORMAT " = " CALCULATED_NUMBER_FORMAT
1205                                                 , st->id, rd->name
1206                                                 , st->current_entry
1207                                                 , unpack_storage_number(rd->values[st->current_entry]), new_value
1208                                                 );
1209                         }
1210                         else {
1211                                 if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: STORE[%ld] = NON EXISTING "
1212                                                 , st->id, rd->name
1213                                                 , st->current_entry
1214                                                 );
1215                                 rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
1216                         }
1217
1218                         stored_entries++;
1219
1220                         if(unlikely(st->debug)) {
1221                                 calculated_number t1 = new_value * (calculated_number)rd->multiplier / (calculated_number)rd->divisor;
1222                                 calculated_number t2 = unpack_storage_number(rd->values[st->current_entry]);
1223                                 calculated_number accuracy = accuracy_loss(t1, t2);
1224                                 debug(D_RRD_STATS, "%s/%s: UNPACK[%ld] = " CALCULATED_NUMBER_FORMAT " FLAGS=0x%08x (original = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s)"
1225                                                 , st->id, rd->name
1226                                                 , st->current_entry
1227                                                 , t2
1228                                                 , get_storage_number_flags(rd->values[st->current_entry])
1229                                                 , t1
1230                                                 , accuracy
1231                                                 , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
1232                                                 );
1233
1234                                 rd->collected_volume += t1;
1235                                 rd->stored_volume += t2;
1236                                 accuracy = accuracy_loss(rd->collected_volume, rd->stored_volume);
1237                                 debug(D_RRD_STATS, "%s/%s: VOLUME[%ld] = " CALCULATED_NUMBER_FORMAT ", calculated  = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s"
1238                                                 , st->id, rd->name
1239                                                 , st->current_entry
1240                                                 , rd->stored_volume
1241                                                 , rd->collected_volume
1242                                                 , accuracy
1243                                                 , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
1244                                                 );
1245
1246                         }
1247                 }
1248                 // reset the storage flags for the next point, if any;
1249                 storage_flags = SN_EXISTS;
1250
1251                 st->counter++;
1252                 st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
1253                 last_ut = next_ut;
1254         }
1255
1256         // align next interpolation to last collection point
1257         if(likely(stored_entries || !store_this_entry)) {
1258                 st->last_updated.tv_sec = st->last_collected_time.tv_sec;
1259                 st->last_updated.tv_usec = st->last_collected_time.tv_usec;
1260         }
1261
1262         for( rd = st->dimensions; likely(rd) ; rd = rd->next ) {
1263                 if(unlikely(!rd->updated)) continue;
1264
1265                 if(likely(stored_entries || !store_this_entry)) {
1266                         if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_collected_value, rd->collected_value);
1267                         rd->last_collected_value = rd->collected_value;
1268
1269                         if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value, rd->calculated_value);
1270                         rd->last_calculated_value = rd->calculated_value;
1271                 }
1272
1273                 rd->calculated_value = 0;
1274                 rd->collected_value = 0;
1275                 rd->updated = 0;
1276
1277                 if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: END "
1278                         " last_collected_value = " COLLECTED_NUMBER_FORMAT
1279                         " collected_value = " COLLECTED_NUMBER_FORMAT
1280                         " last_calculated_value = " CALCULATED_NUMBER_FORMAT
1281                         " calculated_value = " CALCULATED_NUMBER_FORMAT
1282                         , st->id, rd->name
1283                         , rd->last_collected_value
1284                         , rd->collected_value
1285                         , rd->last_calculated_value
1286                         , rd->calculated_value
1287                         );
1288         }
1289         st->last_collected_total  = st->collected_total;
1290
1291         // ALL DONE ABOUT THE DATA UPDATE
1292         // --------------------------------------------------------------------
1293
1294         // find if there are any obsolete dimensions (not updated recently)
1295         if(unlikely(rrd_delete_unupdated_dimensions)) {
1296
1297                 for( rd = st->dimensions; likely(rd) ; rd = rd->next )
1298                         if((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)
1299                                 break;
1300
1301                 if(unlikely(rd)) {
1302                         // there is dimension to free
1303                         // upgrade our read lock to a write lock
1304                         pthread_rwlock_unlock(&st->rwlock);
1305                         pthread_rwlock_wrlock(&st->rwlock);
1306
1307                         for( rd = st->dimensions, last = NULL ; likely(rd) ; ) {
1308                                 // remove it only it is not updated in rrd_delete_unupdated_dimensions seconds
1309
1310                                 if(unlikely((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)) {
1311                                         info("Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
1312
1313                                         if(unlikely(!last)) {
1314                                                 st->dimensions = rd->next;
1315                                                 rd->next = NULL;
1316                                                 rrddim_free(st, rd);
1317                                                 rd = st->dimensions;
1318                                                 continue;
1319                                         }
1320                                         else {
1321                                                 last->next = rd->next;
1322                                                 rd->next = NULL;
1323                                                 rrddim_free(st, rd);
1324                                                 rd = last->next;
1325                                                 continue;
1326                                         }
1327                                 }
1328
1329                                 last = rd;
1330                                 rd = rd->next;
1331                         }
1332
1333                         if(unlikely(!st->dimensions)) {
1334                                 info("Disabling chart %s (%s) since it does not have any dimensions", st->name, st->id);
1335                                 st->enabled = 0;
1336                         }
1337                 }
1338         }
1339
1340         pthread_rwlock_unlock(&st->rwlock);
1341
1342         if(unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
1343                 error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
1344
1345         return(st->usec_since_last_update);
1346 }