6
#include "database/ram/rrddim_mem.h"
7
8
#include "average/average.h"
9
+#include "countif/countif.h"
10
#include "incremental_sum/incremental_sum.h"
11
#include "max/max.h"
12
#include "median/median.h"
29
30
// Allocate all required structures for a query.
31
// This is called once for each netdata query.
31
- void (*create)(struct rrdresult *r);
32
+ void (*create)(struct rrdresult *r, const char *options);
33
34
// Cleanup collected values, but don't destroy the structures.
35
// This is called when the query engine switches dimensions,
234
.flush = grouping_flush_des
235
},
236
237
+ {.name = "countif",
238
+ .hash = 0,
239
+ .value = RRDR_GROUPING_COUNTIF,
240
+ .init = NULL,
241
+ .create= grouping_create_countif,
242
+ .reset = grouping_reset_countif,
243
+ .free = grouping_free_countif,
244
+ .add = grouping_add_countif,
245
+ .flush = grouping_flush_countif
246
+ },
247
+
248
// terminator
249
{.name = NULL,
250
.hash = 0,
302
return "unknown";
303
}
304
305
+static void rrdr_set_grouping_function(RRDR *r, RRDR_GROUPING group_method) {
306
+ int i, found = 0;
307
+ for(i = 0; !found && api_v1_data_groups[i].name ;i++) {
308
+ if(api_v1_data_groups[i].value == group_method) {
309
+ r->internal.grouping_create= api_v1_data_groups[i].create;
310
+ r->internal.grouping_reset = api_v1_data_groups[i].reset;
311
+ r->internal.grouping_free = api_v1_data_groups[i].free;
312
+ r->internal.grouping_add = api_v1_data_groups[i].add;
313
+ r->internal.grouping_flush = api_v1_data_groups[i].flush;
314
+ found = 1;
315
+ }
316
+ }
317
+ if(!found) {
318
+ errno = 0;
319
+#ifdef NETDATA_INTERNAL_CHECKS
320
+ error("INTERNAL ERROR: grouping method %u not found. Using 'average'", (unsigned int)group_method);
321
+#endif
322
+ r->internal.grouping_create= grouping_create_average;
323
+ r->internal.grouping_reset = grouping_reset_average;
324
+ r->internal.grouping_free = grouping_free_average;
325
+ r->internal.grouping_add = grouping_add_average;
326
+ r->internal.grouping_flush = grouping_flush_average;
327
+ }
328
+}
329
+
330
// ----------------------------------------------------------------------------
331
332
static void rrdr_disable_not_selected_dimensions(RRDR *r, RRDR_OPTIONS options, const char *dims,
430
// ----------------------------------------------------------------------------
431
// fill RRDR for a single dimension
432
396
-static inline void do_dimension_variablestep(
397
- RRDR *r
398
- , long points_wanted
399
- , RRDDIM *rd
400
- , long dim_id_in_rrdr
401
- , time_t after_wanted
402
- , time_t before_wanted
403
- , uint32_t options
404
-){
405
-// RRDSET *st = r->st;
406
-
407
- time_t
408
- now = after_wanted,
409
- dt = r->update_every,
410
- max_date = 0,
411
- min_date = 0;
412
-
413
- long
414
-// group_size = r->group,
415
- points_added = 0,
416
- values_in_group = 0,
417
- values_in_group_non_zero = 0,
418
- rrdr_line = -1;
419
-
420
- RRDR_VALUE_FLAGS
421
- group_value_flags = RRDR_VALUE_NOTHING;
422
-
423
- struct rrddim_query_handle handle;
424
-
425
- calculated_number min = r->min, max = r->max;
426
- size_t db_points_read = 0;
427
- time_t db_now = now;
428
- storage_number n_curr, n_prev = SN_EMPTY_SLOT;
429
- calculated_number value;
430
-
431
- for(rd->state->query_ops.init(rd, &handle, now, before_wanted) ; points_added < points_wanted ; now += dt) {
432
- // make sure we return data in the proper time range
433
- if (unlikely(now > before_wanted)) {
434
-#ifdef NETDATA_INTERNAL_CHECKS
435
- r->internal.log = "stopped, because attempted to access the db after 'wanted before'";
436
-#endif
437
- break;
438
- }
439
- if (unlikely(now < after_wanted)) {
440
-#ifdef NETDATA_INTERNAL_CHECKS
441
- r->internal.log = "skipped, because attempted to access the db before 'wanted after'";
442
-#endif
443
- continue;
444
- }
445
-
446
- while (now >= db_now && (!rd->state->query_ops.is_finished(&handle) ||
447
- does_storage_number_exist(n_prev))) {
448
- value = NAN;
449
- if (does_storage_number_exist(n_prev)) {
450
- // use the previously read database value
451
- n_curr = n_prev;
452
- } else {
453
- // read the value from the database
454
- n_curr = rd->state->query_ops.next_metric(&handle, &db_now);
455
- }
456
- n_prev = SN_EMPTY_SLOT;
457
- // db_now has a different value than above
458
- if (likely(now >= db_now)) {
459
- if (likely(does_storage_number_exist(n_curr))) {
460
- if (options & RRDR_OPTION_ANOMALY_BIT)
461
- value = (n_curr & SN_ANOMALY_BIT) ? 0.0 : 100.0;
462
- else
463
- value = unpack_storage_number(n_curr);
464
-
465
- if (likely(value != 0.0))
466
- values_in_group_non_zero++;
467
-
468
- if (unlikely(did_storage_number_reset(n_curr)))
469
- group_value_flags |= RRDR_VALUE_RESET;
470
- }
471
- } else {
472
- // We must postpone processing the value and fill the result with gaps instead
473
- if (likely(does_storage_number_exist(n_curr))) {
474
- n_prev = n_curr;
475
- }
476
- }
477
- // add this value to grouping
478
- if(likely(!isnan(value)))
479
- r->internal.grouping_add(r, value);
480
-
481
- values_in_group++;
482
- db_points_read++;
483
- }
484
-
485
- if (0 == values_in_group) {
486
- // add NAN to grouping
487
- r->internal.grouping_add(r, NAN);
488
- }
489
-
490
- rrdr_line = rrdr_line_init(r, now, rrdr_line);
491
-
492
- if(unlikely(!min_date)) min_date = now;
493
- max_date = now;
494
-
495
- // find the place to store our values
496
- RRDR_VALUE_FLAGS *rrdr_value_options_ptr = &r->o[rrdr_line * r->d + dim_id_in_rrdr];
497
-
498
- // update the dimension options
499
- if(likely(values_in_group_non_zero))
500
- r->od[dim_id_in_rrdr] |= RRDR_DIMENSION_NONZERO;
501
-
502
- // store the specific point options
503
- *rrdr_value_options_ptr = group_value_flags;
504
-
505
- // store the value
506
- value = r->internal.grouping_flush(r, rrdr_value_options_ptr);
507
- r->v[rrdr_line * r->d + dim_id_in_rrdr] = value;
508
-
509
- if(likely(points_added || dim_id_in_rrdr)) {
510
- // find the min/max across all dimensions
511
-
512
- if(unlikely(value < min)) min = value;
513
- if(unlikely(value > max)) max = value;
514
-
515
- }
516
- else {
517
- // runs only when dim_id_in_rrdr == 0 && points_added == 0
518
- // so, on the first point added for the query.
519
- min = max = value;
520
- }
521
-
522
- points_added++;
523
- values_in_group = 0;
524
- group_value_flags = RRDR_VALUE_NOTHING;
525
- values_in_group_non_zero = 0;
526
- }
527
- rd->state->query_ops.finalize(&handle);
528
-
529
- r->internal.db_points_read += db_points_read;
530
- r->internal.result_points_generated += points_added;
531
-
532
- r->min = min;
533
- r->max = max;
534
- r->before = max_date;
535
- r->after = min_date - (r->group - 1) * dt;
536
- rrdr_done(r, rrdr_line);
537
-
538
- #ifdef NETDATA_INTERNAL_CHECKS
539
- if(unlikely(r->rows != points_added))
540
- error("INTERNAL ERROR: %s.%s added %zu rows, but RRDR says I added %zu.", r->st->name, rd->name, (size_t)points_added, (size_t)r->rows);
541
- #endif
542
-}
543
-
544
-static inline void do_dimension_fixedstep(
433
+static inline void rrd2rrdr_do_dimension(
434
RRDR *r
435
, long points_wanted
436
, RRDDIM *rd
444
max_date = 0,
445
min_date = 0;
446
558
- long group_size = r->group,
559
- points_added = 0,
560
- values_in_group = 0,
561
- values_in_group_non_zero = 0,
447
+ long group_points_wanted = r->group,
448
+ points_added = 0, group_points_added = 0, group_points_non_zero = 0,
449
rrdr_line = -1;
450
451
+ size_t group_anomaly_rate = 0;
452
+
453
RRDR_VALUE_FLAGS group_value_flags = RRDR_VALUE_NOTHING;
454
455
struct rrddim_query_handle handle;
456
457
calculated_number min = r->min, max = r->max;
458
size_t db_points_read = 0;
570
- time_t db_now = now;
571
- time_t first_time_t = rrddim_first_entry_t(rd);
459
460
// cache the function pointers we need in the loop
574
- storage_number (*next_metric)(struct rrddim_query_handle *handle, time_t *current_time) = rd->state->query_ops.next_metric;
461
+ calculated_number (*next_metric)(struct rrddim_query_handle *handle, time_t *current_time, time_t *end_time, SN_FLAGS *flags) = rd->state->query_ops.next_metric;
462
void (*grouping_add)(struct rrdresult *r, calculated_number value) = r->internal.grouping_add;
463
calculated_number (*grouping_flush)(struct rrdresult *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) = r->internal.grouping_flush;
577
- RRD_MEMORY_MODE rrd_memory_mode = rd->rrd_memory_mode;
464
+
465
+ calculated_number last_point_value;
466
+ SN_FLAGS last_point_flags;
467
+ time_t last_point_start_time;
468
+ time_t last_point_end_time;
469
+ size_t last_point_anomaly;
470
+
471
+ calculated_number new_point_value = NAN;
472
+ SN_FLAGS new_point_flags = SN_EMPTY_SLOT;
473
+ time_t new_point_start_time = 0;
474
+ time_t new_point_end_time = 0;
475
+ size_t new_point_anomaly = 0;
476
477
for(rd->state->query_ops.init(rd, &handle, now, before_wanted) ; points_added < points_wanted ; now += dt) {
580
- // make sure we return data in the proper time range
581
- if(unlikely(now > before_wanted)) {
582
-#ifdef NETDATA_INTERNAL_CHECKS
583
- r->internal.log = "stopped, because attempted to access the db after 'wanted before'";
584
-#endif
478
+
479
+ // TODO - should be removed when before and after are always respected
480
+ // independently of the databaase first and last time and points_wanted
481
+ // is set to a sane number for the user to get the timeframe wanted.
482
+ // Without the above, this check is needed to stop the loop when the
483
+ // points_wanted is set to an unreasonably high number for the duration
484
+ // of the query.
485
+ if(unlikely(now > before_wanted))
486
break;
586
- }
487
588
- if(unlikely(now < after_wanted)) {
589
-#ifdef NETDATA_INTERNAL_CHECKS
590
- r->internal.log = "skipped, because attempted to access the db before 'wanted after'";
591
-#endif
592
- continue;
593
- }
488
+ // save the old point, in case we need it
489
+ last_point_value = new_point_value;
490
+ last_point_flags = new_point_flags;
491
+ last_point_anomaly = new_point_anomaly;
492
+ last_point_start_time = new_point_start_time;
493
+ last_point_end_time = new_point_end_time;
494
595
- // read the value from the database
596
- //storage_number n = rd->values[slot];
495
+ if(likely(!rd->state->query_ops.is_finished(&handle))) {
496
+ // fetch the new point
497
+ new_point_value = next_metric(&handle, &new_point_start_time, &new_point_end_time, &new_point_flags);
498
598
-#ifdef NETDATA_INTERNAL_CHECKS
599
- struct mem_query_handle* mem_handle = (struct mem_query_handle*)handle.handle;
600
- if ((rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) &&
601
- (rrdset_time2slot(r->st, now) != (long unsigned)(mem_handle->slot))) {
602
- error("INTERNAL CHECK: Unaligned query for %s, database slot: %lu, expected slot: %lu", rd->id, (long unsigned)mem_handle->slot, rrdset_time2slot(r->st, now));
603
- }
604
-#endif
499
+ if(likely(calculated_number_isnumber(new_point_value))) {
500
+ new_point_anomaly = (new_point_flags & SN_ANOMALY_BIT) ? 0 : 100;
501
606
- db_now = now; // this is needed to set db_now in case the next_metric implementation does not set it
502
+ if(unlikely(options & RRDR_OPTION_ANOMALY_BIT))
503
+ new_point_value = (calculated_number)new_point_anomaly;
504
+ }
505
+ else {
506
+ new_point_flags = SN_EMPTY_SLOT;
507
+ new_point_value = NAN;
508
+ new_point_anomaly = 0;
509
+ }
510
608
- storage_number n;
609
- calculated_number value;
511
+ if(unlikely(new_point_start_time == new_point_end_time)) {
512
+ error("QUERY: INTERNAL BUG: next_metric(%s, %s) returned point start time %ld, end time %ld, that are both equal", rd->rrdset->name, rd->name, new_point_start_time, new_point_end_time);
513
+ new_point_start_time = new_point_end_time - rd->update_every;
514
+ }
515
611
- if (unlikely(rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE && now <= first_time_t)) {
612
- n = SN_EMPTY_SLOT;
613
- value = NAN;
614
- }
615
- else {
616
- // load the metric value
617
- n = next_metric(&handle, &db_now);
618
- db_points_read++;
619
-
620
- // and unpack it
621
- if(likely(does_storage_number_exist(n))) {
622
- if (options & RRDR_OPTION_ANOMALY_BIT)
623
- value = (n & SN_ANOMALY_BIT) ? 0.0 : 100.0;
624
- else
625
- value = unpack_storage_number(n);
516
+ if(unlikely(new_point_start_time < last_point_start_time && new_point_end_time < last_point_end_time)) {
517
+ error("QUERY: INTERNAL BUG: next_metric(%s, %s) returned point start time %ld, end time %ld, before the last point start time %ld, end time %ld", rd->rrdset->name, rd->name, new_point_start_time, new_point_end_time, last_point_start_time, last_point_end_time);
518
+ new_point_value = last_point_value;
519
+ new_point_flags = last_point_flags;
520
+ new_point_start_time = last_point_start_time;
521
+ new_point_end_time = last_point_end_time;
522
}
627
- else
628
- value = NAN;
629
- }
523
631
- if(unlikely(db_now > before_wanted)) {
632
-#ifdef NETDATA_INTERNAL_CHECKS
633
- r->internal.log = "stopped, because attempted to access the db after 'wanted before'";
634
-#endif
635
- break;
524
+ if(unlikely(new_point_end_time < last_point_end_time)) {
525
+ error("QUERY: INTERNAL BUG: next_metric(%s, %s) returned point end time %ld, before the last point end time %ld", rd->rrdset->name, rd->name, new_point_end_time, last_point_end_time);
526
+ new_point_value = last_point_value;
527
+ new_point_flags = last_point_flags;
528
+ new_point_start_time = last_point_start_time;
529
+ new_point_end_time = last_point_end_time;
530
+ }
531
+
532
+ if(unlikely(new_point_end_time < now)) {
533
+ error("QUERY: INTERNAL BUG: next_metric(%s, %s) returned point %ld to %ld, before now (now = %ld, after_wanted = %ld, before_wanted = %ld, dt = %ld)", rd->rrdset->name, rd->name, new_point_start_time, new_point_end_time, now, after_wanted, before_wanted, dt);
534
+ new_point_end_time = now;
535
+ }
536
+ }
537
+ else {
538
+ new_point_value = NAN;
539
+ new_point_flags = SN_EMPTY_SLOT;
540
+ new_point_start_time = last_point_end_time;
541
+ new_point_end_time = now;
542
}
543
638
- // this loop exists only to fill nulls
639
- // so, if there is a value already, we use it for the first iteration
640
- // but the following iterations will just fill nulls to the destination
641
- for ( ; now <= db_now ; now += dt, value = NAN, n = SN_EMPTY_SLOT) {
642
- if(likely(does_storage_number_exist(n))) {
643
-
644
-#if defined(NETDATA_INTERNAL_CHECKS) && defined(ENABLE_DBENGINE)
645
- if(now >= db_now) {
646
- struct rrdeng_query_handle *rrd_handle = (struct rrdeng_query_handle *)handle.handle;
647
- if ((rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) && (now != rrd_handle->now))
648
- error(
649
- "INTERNAL CHECK: Unaligned query for %s, database time: %ld, expected time: %ld",
650
- rd->id,
651
- (long)rrd_handle->now,
652
- (long)now);
653
- }
654
-#endif
544
+ size_t iterations = 0;
545
+ for ( ; now <= new_point_end_time && points_added < points_wanted; now += dt, iterations++) {
546
+
547
+ calculated_number current_point_value;
548
+ SN_FLAGS current_point_flags;
549
+ size_t current_point_anomaly;
550
+ //time_t current_point_start_time;
551
+ //time_t current_point_end_time;
552
+
553
+ if(likely(now > new_point_start_time)) {
554
+ // it is time for our NEW point to be used
555
+ current_point_value = new_point_value;
556
+ current_point_flags = new_point_flags;
557
+ current_point_anomaly = new_point_anomaly;
558
+ //current_point_start_time = new_point_start_time;
559
+ //current_point_end_time = new_point_end_time;
560
+ }
561
+ else if(likely(now <= last_point_end_time)) {
562
+ // our LAST point is still valid
563
+ current_point_value = last_point_value;
564
+ current_point_flags = last_point_flags;
565
+ current_point_anomaly = last_point_anomaly;
566
+ //current_point_start_time = last_point_start_time;
567
+ //current_point_end_time = last_point_end_time;
568
+ }
569
+ else {
570
+ // a GAP, we don't have a value this time
571
+ current_point_value = NAN;
572
+ current_point_flags = SN_EMPTY_SLOT;
573
+ current_point_anomaly = 0;
574
+ //current_point_start_time = now - dt;
575
+ //current_point_end_time = now;
576
+ }
577
656
- if(likely(value != 0.0))
657
- values_in_group_non_zero++;
578
+ if(likely(calculated_number_isnumber(current_point_value))) {
579
+ if(likely(current_point_value != 0.0))
580
+ group_points_non_zero++;
581
659
- if(unlikely(did_storage_number_reset(n)))
582
+ if(unlikely(current_point_flags & SN_EXISTS_RESET))
583
group_value_flags |= RRDR_VALUE_RESET;
584
662
- grouping_add(r, value);
585
+ grouping_add(r, current_point_value);
586
}
587
588
// add this value for grouping
666
- values_in_group++;
589
+ group_points_added++;
590
+ group_anomaly_rate += current_point_anomaly;
591
668
- if(unlikely(values_in_group == group_size)) {
592
+ if(unlikely(group_points_added == group_points_wanted)) {
593
rrdr_line = rrdr_line_init(r, now, rrdr_line);
594
size_t rrdr_o_v_index = rrdr_line * r->d + dim_id_in_rrdr;
595
600
RRDR_VALUE_FLAGS *rrdr_value_options_ptr = &r->o[rrdr_o_v_index];
601
602
// update the dimension options
679
- if(likely(values_in_group_non_zero))
603
+ if(likely(group_points_non_zero))
604
r->od[dim_id_in_rrdr] |= RRDR_DIMENSION_NONZERO;
605
606
// store the specific point options
610
calculated_number group_value = grouping_flush(r, rrdr_value_options_ptr);
611
r->v[rrdr_o_v_index] = group_value;
612
613
+ // we only store uint8_t anomaly rates,
614
+ // so let's get double precision by storing
615
+ // anomaly rates in the range 0 - 200
616
+ group_anomaly_rate = (group_anomaly_rate << 1) / group_points_added;
617
+ r->ar[rrdr_o_v_index] = (uint8_t)group_anomaly_rate;
618
+
619
if(likely(points_added || dim_id_in_rrdr)) {
620
// find the min/max across all dimensions
621
630
}
631
632
points_added++;
703
- values_in_group = 0;
633
+ group_points_added = 0;
634
group_value_flags = RRDR_VALUE_NOTHING;
705
- values_in_group_non_zero = 0;
635
+ group_points_non_zero = 0;
636
+ group_anomaly_rate = 0;
637
}
638
}
708
- now = db_now;
639
+ // the loop above increased "now" by dt,
640
+ // but the main loop will increase it,
641
+ // so, let's undo the last iteration of this loop
642
+ if(iterations)
643
+ now -= dt;
644
}
645
rd->state->query_ops.finalize(&handle);
646
653
r->after = min_date - (r->group - 1) * dt;
654
rrdr_done(r, rrdr_line);
655
721
-#ifdef NETDATA_INTERNAL_CHECKS
722
- if(unlikely(r->rows != points_added))
723
- error("INTERNAL ERROR: %s.%s added %zu rows, but RRDR says I added %zu.", r->st->name, rd->name, (size_t)points_added, (size_t)r->rows);
724
-#endif
656
+ if(unlikely(points_wanted != points_added))
657
+ error("QUERY: INTERNAL ERROR: query on %s/%s requested %zu points, but RRDR added %zu.", r->st->name, rd->name, (size_t)points_wanted, (size_t)points_added);
658
}
659
660
// ----------------------------------------------------------------------------
806
return absolute_period_requested;
807
}
808
876
-static RRDR *rrd2rrdr_fixedstep(
809
+static RRDR *rrd2rrdr_do_chart(
810
ONEWAYALLOC *owa
811
, RRDSET *st
812
, long points_requested
821
, time_t last_entry_t
822
, int absolute_period_requested
823
, struct context_param *context_param_list
824
+ , const char *group_options
825
, int timeout
826
) {
827
UNUSED(last_entry_t);
1007
1008
// -------------------------------------------------------------------------
1009
// assign the processor functions
1076
-
1077
- {
1078
- int i, found = 0;
1079
- for(i = 0; !found && api_v1_data_groups[i].name ;i++) {
1080
- if(api_v1_data_groups[i].value == group_method) {
1081
- r->internal.grouping_create= api_v1_data_groups[i].create;
1082
- r->internal.grouping_reset = api_v1_data_groups[i].reset;
1083
- r->internal.grouping_free = api_v1_data_groups[i].free;
1084
- r->internal.grouping_add = api_v1_data_groups[i].add;
1085
- r->internal.grouping_flush = api_v1_data_groups[i].flush;
1086
- found = 1;
1087
- }
1088
- }
1089
- if(!found) {
1090
- errno = 0;
1091
- #ifdef NETDATA_INTERNAL_CHECKS
1092
- error("INTERNAL ERROR: grouping method %u not found for chart '%s'. Using 'average'", (unsigned int)group_method, r->st->name);
1093
- #endif
1094
- r->internal.grouping_create= grouping_create_average;
1095
- r->internal.grouping_reset = grouping_reset_average;
1096
- r->internal.grouping_free = grouping_free_average;
1097
- r->internal.grouping_add = grouping_add_average;
1098
- r->internal.grouping_flush = grouping_flush_average;
1099
- }
1100
- }
1010
+ rrdr_set_grouping_function(r, group_method);
1011
1012
// allocate any memory required by the grouping method
1103
- r->internal.grouping_create(r);
1013
+ r->internal.grouping_create(r, group_options);
1014
1015
1016
// -------------------------------------------------------------------------
1047
// reset the grouping for the new dimension
1048
r->internal.grouping_reset(r);
1049
1140
- do_dimension_fixedstep(
1141
- r
1142
- , points_wanted
1143
- , rd
1144
- , c
1145
- , after_wanted
1146
- , before_wanted
1147
- , options
1148
- );
1050
+ rrd2rrdr_do_dimension(r, points_wanted, rd, c, after_wanted, before_wanted, options);
1051
if (timeout)
1052
now_realtime_timeval(&query_current_time);
1053
1139
return r;
1140
}
1141
1240
-#ifdef ENABLE_DBENGINE
1241
-static RRDR *rrd2rrdr_variablestep(
1242
- ONEWAYALLOC *owa
1243
- , RRDSET *st
1244
- , long points_requested
1245
- , long long after_requested
1246
- , long long before_requested
1247
- , RRDR_GROUPING group_method
1248
- , long resampling_time_requested
1249
- , RRDR_OPTIONS options
1250
- , const char *dimensions
1251
- , int update_every
1252
- , time_t first_entry_t
1253
- , time_t last_entry_t
1254
- , int absolute_period_requested
1255
- , struct rrdeng_region_info *region_info_array
1256
- , struct context_param *context_param_list
1257
- , int timeout
1258
-) {
1259
- UNUSED(last_entry_t);
1260
- int aligned = !(options & RRDR_OPTION_NOT_ALIGNED);
1261
-
1262
- // the duration of the chart
1263
- time_t duration = before_requested - after_requested;
1264
- long available_points = duration / update_every;
1265
-
1266
- RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL;
1267
-
1268
- if(duration <= 0 || available_points <= 0) {
1269
- freez(region_info_array);
1270
- return rrdr_create(owa, st, 1, context_param_list);
1271
- }
1272
-
1273
- // check the number of wanted points in the result
1274
- if(unlikely(points_requested < 0)) points_requested = -points_requested;
1275
- if(unlikely(points_requested > available_points)) points_requested = available_points;
1276
- if(unlikely(points_requested == 0)) points_requested = available_points;
1277
-
1278
- // calculate the desired grouping of source data points
1279
- long group = available_points / points_requested;
1280
- if(unlikely(group <= 0)) group = 1;
1281
- if(unlikely(available_points % points_requested > points_requested / 2)) group++; // rounding to the closest integer
1282
-
1283
- // resampling_time_requested enforces a certain grouping multiple
1284
- calculated_number resampling_divisor = 1.0;
1285
- long resampling_group = 1;
1286
- if(unlikely(resampling_time_requested > update_every)) {
1287
- if (unlikely(resampling_time_requested > duration)) {
1288
- // group_time is above the available duration
1289
-
1290
- #ifdef NETDATA_INTERNAL_CHECKS
1291
- info("INTERNAL CHECK: %s: requested gtime %ld secs, is greater than the desired duration %ld secs", st->id, resampling_time_requested, duration);
1292
- #endif
1293
-
1294
- after_requested = before_requested - resampling_time_requested;
1295
- duration = before_requested - after_requested;
1296
- available_points = duration / update_every;
1297
- group = available_points / points_requested;
1298
- }
1299
-
1300
- // if the duration is not aligned to resampling time
1301
- // extend the duration to the past, to avoid a gap at the chart
1302
- // only when the missing duration is above 1/10th of a point
1303
- if(duration % resampling_time_requested) {
1304
- time_t delta = duration % resampling_time_requested;
1305
- if(delta > resampling_time_requested / 10) {
1306
- after_requested -= resampling_time_requested - delta;
1307
- duration = before_requested - after_requested;
1308
- available_points = duration / update_every;
1309
- group = available_points / points_requested;
1310
- }
1311
- }
1312
-
1313
- // the points we should group to satisfy gtime
1314
- resampling_group = resampling_time_requested / update_every;
1315
- if(unlikely(resampling_time_requested % update_every)) {
1316
- #ifdef NETDATA_INTERNAL_CHECKS
1317
- info("INTERNAL CHECK: %s: requested gtime %ld secs, is not a multiple of the chart's data collection frequency %d secs", st->id, resampling_time_requested, update_every);
1318
- #endif
1319
-
1320
- resampling_group++;
1321
- }
1322
-
1323
- // adapt group according to resampling_group
1324
- if(unlikely(group < resampling_group)) group = resampling_group; // do not allow grouping below the desired one
1325
- if(unlikely(group % resampling_group)) group += resampling_group - (group % resampling_group); // make sure group is multiple of resampling_group
1326
-
1327
- //resampling_divisor = group / resampling_group;
1328
- resampling_divisor = (calculated_number)(group * update_every) / (calculated_number)resampling_time_requested;
1329
- }
1330
-
1331
- // now that we have group,
1332
- // align the requested timeframe to fit it.
1333
-
1334
- if(aligned) {
1335
- // alignment has been requested, so align the values
1336
- before_requested -= before_requested % (group * update_every);
1337
- after_requested -= after_requested % (group * update_every);
1338
- }
1339
-
1340
- // we align the request on requested_before
1341
- time_t before_wanted = before_requested;
1342
-
1343
- //size_t before_slot = rrdset_time2slot(st, before_wanted);
1344
-
1345
- // we need to estimate the number of points, for having
1346
- // an integer number of values per point
1347
- long points_wanted = (before_wanted - after_requested) / (update_every * group);
1348
-
1349
- time_t after_wanted = before_wanted - (points_wanted * group * update_every) + update_every;
1350
- if(unlikely(after_wanted < first_entry_t)) {
1351
- // hm... we go to the past, calculate again points_wanted using all the db from before_wanted to the beginning
1352
- points_wanted = (before_wanted - first_entry_t) / group;
1353
-
1354
- // recalculate after wanted with the new number of points
1355
- after_wanted = before_wanted - (points_wanted * group * update_every) + update_every;
1356
-
1357
- if(unlikely(after_wanted < first_entry_t)) {
1358
- #ifdef NETDATA_INTERNAL_CHECKS
1359
- error("INTERNAL ERROR: rrd2rrdr() on %s, after_wanted is before db min", st->name);
1360
- #endif
1361
-
1362
- after_wanted = first_entry_t - (first_entry_t % ( ((aligned)?group:1) * update_every )) + ( ((aligned)?group:1) * update_every );
1363
- }
1364
- }
1365
- //size_t after_slot = rrdset_time2slot(st, after_wanted);
1366
-
1367
- // check if they are reversed
1368
- if(unlikely(after_wanted > before_wanted)) {
1369
- #ifdef NETDATA_INTERNAL_CHECKS
1370
- error("INTERNAL ERROR: rrd2rrdr() on %s, reversed wanted after/before", st->name);
1371
- #endif
1372
- time_t tmp = before_wanted;
1373
- before_wanted = after_wanted;
1374
- after_wanted = tmp;
1375
- }
1376
-
1377
- // recalculate points_wanted using the final time-frame
1378
- points_wanted = (before_wanted - after_wanted) / update_every / group + 1;
1379
- if(unlikely(points_wanted < 0)) {
1380
- #ifdef NETDATA_INTERNAL_CHECKS
1381
- error("INTERNAL ERROR: rrd2rrdr() on %s, points_wanted is %ld", st->name, points_wanted);
1382
- #endif
1383
- points_wanted = 0;
1384
- }
1385
-
1386
-#ifdef NETDATA_INTERNAL_CHECKS
1387
- duration = before_wanted - after_wanted;
1388
-
1389
- if(after_wanted < first_entry_t)
1390
- error("INTERNAL CHECK: after_wanted %u is too small, minimum %u", (uint32_t)after_wanted, (uint32_t)first_entry_t);
1391
-
1392
- if(before_wanted < first_entry_t)
1393
- error("INTERNAL CHECK: before_wanted %u is too small, minimum %u", (uint32_t)before_wanted, (uint32_t)first_entry_t);
1394
-
1395
- if(points_wanted > (before_wanted - after_wanted) / group / update_every + 1)
1396
- error("INTERNAL CHECK: points_wanted %ld is more than points %ld", points_wanted, (before_wanted - after_wanted) / group / update_every + 1);
1397
-
1398
- if(group < resampling_group)
1399
- error("INTERNAL CHECK: group %ld is less than the desired group points %ld", group, resampling_group);
1400
-
1401
- if(group > resampling_group && group % resampling_group)
1402
- error("INTERNAL CHECK: group %ld is not a multiple of the desired group points %ld", group, resampling_group);
1403
-#endif
1404
-
1405
- // -------------------------------------------------------------------------
1406
- // initialize our result set
1407
- // this also locks the chart for us
1408
-
1409
- RRDR *r = rrdr_create(owa, st, points_wanted, context_param_list);
1410
- if(unlikely(!r)) {
1411
- #ifdef NETDATA_INTERNAL_CHECKS
1412
- error("INTERNAL CHECK: Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (uint32_t)duration, points_wanted);
1413
- #endif
1414
- freez(region_info_array);
1415
- return NULL;
1416
- }
1417
-
1418
- if(unlikely(!r->d || !points_wanted)) {
1419
- #ifdef NETDATA_INTERNAL_CHECKS
1420
- error("INTERNAL CHECK: Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%zu, points=%ld", st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (size_t)duration, points_wanted);
1421
- #endif
1422
- freez(region_info_array);
1423
- return r;
1424
- }
1425
-
1426
- r->result_options |= RRDR_RESULT_OPTION_VARIABLE_STEP;
1427
- if(unlikely(absolute_period_requested == 1))
1428
- r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
1429
- else
1430
- r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
1431
-
1432
- // find how many dimensions we have
1433
- long dimensions_count = r->d;
1434
-
1435
- // -------------------------------------------------------------------------
1436
- // initialize RRDR
1437
-
1438
- r->group = group;
1439
- r->update_every = (int)group * update_every;
1440
- r->before = before_wanted;
1441
- r->after = after_wanted;
1442
- r->internal.points_wanted = points_wanted;
1443
- r->internal.resampling_group = resampling_group;
1444
- r->internal.resampling_divisor = resampling_divisor;
1445
-
1446
-
1447
- // -------------------------------------------------------------------------
1448
- // assign the processor functions
1449
-
1450
- {
1451
- int i, found = 0;
1452
- for(i = 0; !found && api_v1_data_groups[i].name ;i++) {
1453
- if(api_v1_data_groups[i].value == group_method) {
1454
- r->internal.grouping_create= api_v1_data_groups[i].create;
1455
- r->internal.grouping_reset = api_v1_data_groups[i].reset;
1456
- r->internal.grouping_free = api_v1_data_groups[i].free;
1457
- r->internal.grouping_add = api_v1_data_groups[i].add;
1458
- r->internal.grouping_flush = api_v1_data_groups[i].flush;
1459
- found = 1;
1460
- }
1461
- }
1462
- if(!found) {
1463
- errno = 0;
1464
- #ifdef NETDATA_INTERNAL_CHECKS
1465
- error("INTERNAL ERROR: grouping method %u not found for chart '%s'. Using 'average'", (unsigned int)group_method, r->st->name);
1466
- #endif
1467
- r->internal.grouping_create= grouping_create_average;
1468
- r->internal.grouping_reset = grouping_reset_average;
1469
- r->internal.grouping_free = grouping_free_average;
1470
- r->internal.grouping_add = grouping_add_average;
1471
- r->internal.grouping_flush = grouping_flush_average;
1472
- }
1473
- }
1474
-
1475
- // allocate any memory required by the grouping method
1476
- r->internal.grouping_create(r);
1477
-
1478
-
1479
- // -------------------------------------------------------------------------
1480
- // disable the not-wanted dimensions
1481
- if (context_param_list && !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE))
1482
- rrdset_check_rdlock(st);
1483
-
1484
- if(dimensions)
1485
- rrdr_disable_not_selected_dimensions(r, options, dimensions, context_param_list);
1486
-
1487
-
1488
- // -------------------------------------------------------------------------
1489
- // do the work for each dimension
1490
-
1491
- time_t max_after = 0, min_before = 0;
1492
- long max_rows = 0;
1493
-
1494
- RRDDIM *rd;
1495
- long c, dimensions_used = 0, dimensions_nonzero = 0;
1496
- struct timeval query_start_time;
1497
- struct timeval query_current_time;
1498
- if (timeout)
1499
- now_realtime_timeval(&query_start_time);
1500
- for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1501
-
1502
- // if we need a percentage, we need to calculate all dimensions
1503
- if(unlikely(!(options & RRDR_OPTION_PERCENTAGE) && (r->od[c] & RRDR_DIMENSION_HIDDEN))) {
1504
- if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED)) r->od[c] &= ~RRDR_DIMENSION_SELECTED;
1505
- continue;
1506
- }
1507
- r->od[c] |= RRDR_DIMENSION_SELECTED;
1508
-
1509
- // reset the grouping for the new dimension
1510
- r->internal.grouping_reset(r);
1511
-
1512
- do_dimension_variablestep(
1513
- r
1514
- , points_wanted
1515
- , rd
1516
- , c
1517
- , after_wanted
1518
- , before_wanted
1519
- , options
1520
- );
1521
- if (timeout)
1522
- now_realtime_timeval(&query_current_time);
1523
-
1524
- if(r->od[c] & RRDR_DIMENSION_NONZERO)
1525
- dimensions_nonzero++;
1526
-
1527
- // verify all dimensions are aligned
1528
- if(unlikely(!dimensions_used)) {
1529
- min_before = r->before;
1530
- max_after = r->after;
1531
- max_rows = r->rows;
1532
- }
1533
- else {
1534
- if(r->after != max_after) {
1535
- #ifdef NETDATA_INTERNAL_CHECKS
1536
- error("INTERNAL ERROR: 'after' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
1537
- st->name, (size_t)max_after, rd->name, (size_t)r->after);
1538
- #endif
1539
- r->after = (r->after > max_after) ? r->after : max_after;
1540
- }
1541
-
1542
- if(r->before != min_before) {
1543
- #ifdef NETDATA_INTERNAL_CHECKS
1544
- error("INTERNAL ERROR: 'before' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
1545
- st->name, (size_t)min_before, rd->name, (size_t)r->before);
1546
- #endif
1547
- r->before = (r->before < min_before) ? r->before : min_before;
1548
- }
1549
-
1550
- if(r->rows != max_rows) {
1551
- #ifdef NETDATA_INTERNAL_CHECKS
1552
- error("INTERNAL ERROR: 'rows' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
1553
- st->name, (size_t)max_rows, rd->name, (size_t)r->rows);
1554
- #endif
1555
- r->rows = (r->rows > max_rows) ? r->rows : max_rows;
1556
- }
1557
- }
1558
-
1559
- dimensions_used++;
1560
- if (timeout && (dt_usec(&query_start_time, &query_current_time) / 1000.0) > timeout) {
1561
- log_access("QUERY CANCELED RUNTIME EXCEEDED %0.2f ms (LIMIT %d ms)",
1562
- dt_usec(&query_start_time, &query_current_time) / 1000.0, timeout);
1563
- r->result_options |= RRDR_RESULT_OPTION_CANCEL;
1564
- break;
1565
- }
1566
- }
1567
-
1568
- #ifdef NETDATA_INTERNAL_CHECKS
1569
-
1570
- if (dimensions_used) {
1571
- if(r->internal.log)
1572
- rrd2rrdr_log_request_response_metadata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ r->internal.log);
1573
-
1574
- if(r->rows != points_wanted)
1575
- rrd2rrdr_log_request_response_metadata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "got 'points' is not wanted 'points'");
1576
-
1577
- if(aligned && (r->before % group) != 0)
1578
- rrd2rrdr_log_request_response_metadata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "'before' is not aligned but alignment is required");
1579
-
1580
- // 'after' should not be aligned, since we start inside the first group
1581
- //if(aligned && (r->after % group) != 0)
1582
- // rrd2rrdr_log_request_response_metadata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, "'after' is not aligned but alignment is required");
1583
-
1584
- if(r->before != before_requested)
1585
- rrd2rrdr_log_request_response_metadata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "chart is not aligned to requested 'before'");
1586
-
1587
- if(r->before != before_wanted)
1588
- rrd2rrdr_log_request_response_metadata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "got 'before' is not wanted 'before'");
1589
-
1590
- // reported 'after' varies, depending on group
1591
- if(r->after != after_wanted)
1592
- rrd2rrdr_log_request_response_metadata(r, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, /*after_slot, before_slot,*/ "got 'after' is not wanted 'after'");
1593
- }
1594
- #endif
1595
-
1596
- // free all resources used by the grouping method
1597
- r->internal.grouping_free(r);
1598
-
1599
- // when all the dimensions are zero, we should return all of them
1600
- if(unlikely(options & RRDR_OPTION_NONZERO && !dimensions_nonzero && !(r->result_options & RRDR_RESULT_OPTION_CANCEL))) {
1601
- // all the dimensions are zero
1602
- // mark them as NONZERO to send them all
1603
- for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1604
- if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
1605
- r->od[c] |= RRDR_DIMENSION_NONZERO;
1606
- }
1607
- }
1608
-
1609
- rrdr_query_completed(r->internal.db_points_read, r->internal.result_points_generated);
1610
- freez(region_info_array);
1611
- return r;
1612
-}
1613
-#endif //#ifdef ENABLE_DBENGINE
1614
-
1142
RRDR *rrd2rrdr(
1143
ONEWAYALLOC *owa
1144
, RRDSET *st
1150
, RRDR_OPTIONS options
1151
, const char *dimensions
1152
, struct context_param *context_param_list
1153
+ , const char *group_options
1154
, int timeout
1155
)
1156
{
1205
return NULL;
1206
}
1207
1680
-#ifdef ENABLE_DBENGINE
1681
- if (st->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
1682
- struct rrdeng_region_info *region_info_array;
1683
- unsigned regions, max_interval;
1684
-
1685
- /* This call takes the chart read-lock */
1686
- regions = rrdeng_variable_step_boundaries(st, after_requested, before_requested,
1687
- ®ion_info_array, &max_interval, context_param_list);
1688
- if (1 == regions) {
1689
- if (region_info_array) {
1690
- if (rrd_update_every != region_info_array[0].update_every) {
1691
- rrd_update_every = region_info_array[0].update_every;
1692
- /* recalculate query alignment */
1693
- absolute_period_requested = rrdr_relative_window_to_absolute(&after_requested, &before_requested,
1694
- rrd_update_every, points_requested);
1695
- }
1696
- freez(region_info_array);
1697
- }
1698
- return rrd2rrdr_fixedstep(owa, st, points_requested, after_requested, before_requested, group_method,
1699
- resampling_time_requested, options, dimensions, rrd_update_every,
1700
- first_entry_t, last_entry_t, absolute_period_requested, context_param_list, timeout);
1701
- }
1702
- else {
1703
- if (rrd_update_every != (uint16_t)max_interval) {
1704
- rrd_update_every = (uint16_t) max_interval;
1705
- /* recalculate query alignment */
1706
- absolute_period_requested = rrdr_relative_window_to_absolute(&after_requested, &before_requested,
1707
- rrd_update_every, points_requested);
1708
- }
1709
- return rrd2rrdr_variablestep(owa, st, points_requested, after_requested, before_requested, group_method,
1710
- resampling_time_requested, options, dimensions, rrd_update_every,
1711
- first_entry_t, last_entry_t, absolute_period_requested, region_info_array, context_param_list, timeout);
1712
- }
1713
- }
1714
-#endif
1715
- return rrd2rrdr_fixedstep(owa, st, points_requested, after_requested, before_requested, group_method,
1716
- resampling_time_requested, options, dimensions,
1717
- rrd_update_every, first_entry_t, last_entry_t, absolute_period_requested, context_param_list, timeout);
1208
+ return rrd2rrdr_do_chart(owa, st, points_requested,
1209
+ after_requested, before_requested,
1210
+ group_method, resampling_time_requested, options, dimensions, rrd_update_every,
1211
+ first_entry_t, last_entry_t, absolute_period_requested,
1212
+ context_param_list, group_options, timeout);
1213
}