24
const char *name;
25
WEIGHTS_METHOD value;
26
} weights_methods[] = {
27
- { "ks2" , WEIGHTS_METHOD_MC_KS2}
28
- , { "volume" , WEIGHTS_METHOD_MC_VOLUME}
29
- , { "anomaly-rate" , WEIGHTS_METHOD_ANOMALY_RATE}
30
- , { NULL , 0 }
27
+ { "ks2" , WEIGHTS_METHOD_MC_KS2}
28
+ , { "volume" , WEIGHTS_METHOD_MC_VOLUME}
29
+ , { "anomaly-rate" , WEIGHTS_METHOD_ANOMALY_RATE}
30
+ , { "value" , WEIGHTS_METHOD_VALUE}
31
+ , { NULL , 0 }
32
};
33
34
WEIGHTS_METHOD weights_string_to_method(const char *method) {
57
58
struct register_result {
59
RESULT_FLAGS flags;
60
+ RRDHOST *host;
61
RRDCONTEXT_ACQUIRED *rca;
62
RRDINSTANCE_ACQUIRED *ria;
63
RRDMETRIC_ACQUIRED *rma;
64
NETDATA_DOUBLE value;
65
+ STORAGE_POINT highlighted;
66
+ STORAGE_POINT baseline;
67
};
68
69
static DICTIONARY *register_result_init() {
66
- DICTIONARY *results = dictionary_create(DICT_OPTION_SINGLE_THREADED);
70
+ DICTIONARY *results = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct register_result));
71
return results;
72
}
73
76
}
77
78
static void register_result(DICTIONARY *results,
79
+ RRDHOST *host,
80
RRDCONTEXT_ACQUIRED *rca,
81
RRDINSTANCE_ACQUIRED *ria,
82
RRDMETRIC_ACQUIRED *rma,
83
NETDATA_DOUBLE value,
84
RESULT_FLAGS flags,
85
+ STORAGE_POINT *highlighted,
86
+ STORAGE_POINT *baseline,
87
WEIGHTS_STATS *stats,
88
bool register_zero) {
89
102
103
struct register_result t = {
104
.flags = flags,
105
+ .host = host,
106
.rca = rca,
107
.ria = ria,
108
.rma = rma,
101
- .value = v
109
+ .value = v,
110
};
111
112
+ if(highlighted)
113
+ t.highlighted = *highlighted;
114
+
115
+ if(baseline)
116
+ t.baseline = *baseline;
117
+
118
// we can use the pointer address or RMA as a unique key for each metric
119
char buf[20 + 1];
120
ssize_t len = snprintfz(buf, 20, "%p", rma);
132
size_t examined_dimensions __maybe_unused, usec_t duration,
133
WEIGHTS_STATS *stats) {
134
121
- buffer_json_initialize(wb, "\"", "\"", 0, true, false);
135
+ buffer_json_initialize(wb, "\"", "\"", 0, true, options & RRDR_OPTION_MINIFY);
136
buffer_json_member_add_time_t(wb, "after", after);
137
buffer_json_member_add_time_t(wb, "before", before);
138
buffer_json_member_add_time_t(wb, "duration", before - after);
305
return total_dimensions;
306
}
307
308
+typedef enum {
309
+ WPT_DIMENSION = 0,
310
+ WPT_INSTANCE = 1,
311
+ WPT_CONTEXT = 2,
312
+ WPT_NODE = 3,
313
+} WEIGHTS_POINT_TYPE;
314
+
315
+static inline void storage_point_to_json(BUFFER *wb, WEIGHTS_POINT_TYPE type, ssize_t di, ssize_t ii, ssize_t ci, ssize_t ni, NETDATA_DOUBLE weight, STORAGE_POINT *highlighted_sp, STORAGE_POINT *baseline_sp, RRDR_OPTIONS options __maybe_unused, bool baseline) {
316
+ buffer_json_add_array_item_array(wb);
317
+
318
+ buffer_json_add_array_item_uint64(wb, type); // "type"
319
+ buffer_json_add_array_item_array(wb);
320
+ if(type == WPT_DIMENSION)
321
+ buffer_json_add_array_item_int64(wb, di);
322
+ if(type == WPT_DIMENSION || type == WPT_INSTANCE)
323
+ buffer_json_add_array_item_int64(wb, ii);
324
+ if(type == WPT_CONTEXT)
325
+ buffer_json_add_array_item_int64(wb, ci);
326
+ buffer_json_add_array_item_int64(wb, ni);
327
+ buffer_json_array_close(wb);
328
+ buffer_json_add_array_item_double(wb, weight); // "weight"
329
+
330
+ buffer_json_add_array_item_array(wb);
331
+ buffer_json_add_array_item_double(wb, highlighted_sp->min); // "min"
332
+ buffer_json_add_array_item_double(wb, (highlighted_sp->count) ? highlighted_sp->sum / (NETDATA_DOUBLE) highlighted_sp->count : 0.0); // "avg"
333
+ buffer_json_add_array_item_double(wb, highlighted_sp->max); // "max"
334
+ buffer_json_add_array_item_double(wb, highlighted_sp->sum); // "sum"
335
+ buffer_json_add_array_item_uint64(wb, highlighted_sp->count); // "count"
336
+ buffer_json_add_array_item_uint64(wb, highlighted_sp->anomaly_count); // "anomaly_count"
337
+ buffer_json_array_close(wb);
338
+
339
+ if(baseline) {
340
+ buffer_json_add_array_item_array(wb);
341
+ buffer_json_add_array_item_double(wb, baseline_sp->min); // "min"
342
+ buffer_json_add_array_item_double(wb, (baseline_sp->count) ? baseline_sp->sum / (NETDATA_DOUBLE) baseline_sp->count : 0.0); // "avg"
343
+ buffer_json_add_array_item_double(wb, baseline_sp->max); // "max"
344
+ buffer_json_add_array_item_double(wb, baseline_sp->sum); // "sum"
345
+ buffer_json_add_array_item_uint64(wb, baseline_sp->count); // "count"
346
+ buffer_json_add_array_item_uint64(wb, baseline_sp->anomaly_count); // "anomaly_count"
347
+ buffer_json_array_close(wb);
348
+ }
349
+
350
+ buffer_json_array_close(wb); // key
351
+}
352
+
353
+static void multinode_data_schema(BUFFER *wb, RRDR_OPTIONS options __maybe_unused, const char *key, bool baseline) {
354
+ size_t idx = 0;
355
+ buffer_json_member_add_object(wb, key); // schema
356
+
357
+ buffer_json_member_add_object(wb, "type");
358
+ buffer_json_member_add_uint64(wb, "idx", idx++);
359
+ buffer_json_object_close(wb); // type
360
+
361
+ buffer_json_member_add_object(wb, "link");
362
+ buffer_json_member_add_uint64(wb, "idx", idx++);
363
+ buffer_json_member_add_object(wb, "dimension");
364
+ {
365
+ buffer_json_member_add_uint64(wb, "type", WPT_DIMENSION);
366
+ size_t pidx = 0;
367
+ buffer_json_member_add_uint64(wb, "di", pidx++);
368
+ buffer_json_member_add_uint64(wb, "ii", pidx++);
369
+ buffer_json_member_add_uint64(wb, "ni", pidx++);
370
+ }
371
+ buffer_json_object_close(wb); // dimension
372
+ buffer_json_member_add_object(wb, "instance");
373
+ {
374
+ buffer_json_member_add_uint64(wb, "type", WPT_INSTANCE);
375
+ size_t pidx = 0;
376
+ buffer_json_member_add_uint64(wb, "ii", pidx++);
377
+ buffer_json_member_add_uint64(wb, "ni", pidx++);
378
+ }
379
+ buffer_json_object_close(wb); // context
380
+ buffer_json_member_add_object(wb, "context");
381
+ {
382
+ buffer_json_member_add_uint64(wb, "type", WPT_CONTEXT);
383
+ size_t pidx = 0;
384
+ buffer_json_member_add_uint64(wb, "ci", pidx++);
385
+ buffer_json_member_add_uint64(wb, "ni", pidx++);
386
+ }
387
+ buffer_json_object_close(wb); // context
388
+ buffer_json_member_add_object(wb, "node");
389
+ {
390
+ buffer_json_member_add_uint64(wb, "type", WPT_NODE);
391
+ size_t pidx = 0;
392
+ buffer_json_member_add_uint64(wb, "ni", pidx++);
393
+ }
394
+ buffer_json_object_close(wb); // node
395
+ buffer_json_object_close(wb); // link
396
+
397
+ buffer_json_member_add_object(wb, "weight");
398
+ buffer_json_member_add_uint64(wb, "idx", idx++);
399
+ buffer_json_object_close(wb); // weight
400
+
401
+ for(size_t i = 0; i < ((baseline) ? 2 : 1) ; i++) {
402
+ if(i == 0)
403
+ buffer_json_member_add_object(wb, "highlighted");
404
+ else
405
+ buffer_json_member_add_object(wb, "baseline");
406
+
407
+ buffer_json_member_add_uint64(wb, "idx", idx++);
408
+ size_t pidx = 0;
409
+ buffer_json_member_add_uint64(wb, "min", pidx++);
410
+ buffer_json_member_add_uint64(wb, "avg", pidx++);
411
+ buffer_json_member_add_uint64(wb, "max", pidx++);
412
+ buffer_json_member_add_uint64(wb, "sum", pidx++);
413
+ buffer_json_member_add_uint64(wb, "count", pidx++);
414
+ buffer_json_member_add_uint64(wb, "anomaly_count", pidx++);
415
+ buffer_json_object_close(wb); // point
416
+ }
417
+
418
+ buffer_json_object_close(wb); // schema
419
+}
420
+
421
+struct dict_unique_name {
422
+ bool existing;
423
+ uint32_t i;
424
+};
425
+
426
+struct dict_unique_id_name {
427
+ bool existing;
428
+ uint32_t i;
429
+ const char *id;
430
+ const char *name;
431
+};
432
+
433
+static inline ssize_t dict_unique_name_add(DICTIONARY *dict, const char *name, ssize_t *max_id) {
434
+ struct dict_unique_name *dun = dictionary_set(dict, name, NULL, sizeof(struct dict_unique_name));
435
+ if(!dun->existing) {
436
+ dun->existing = true;
437
+ dun->i = *max_id;
438
+ (*max_id)++;
439
+ }
440
+
441
+ return (ssize_t)dun->i;
442
+}
443
+
444
+static inline ssize_t dict_unique_id_name_add(DICTIONARY *dict, const char *id, const char *name, ssize_t *max_id) {
445
+ char key[1024 + 1];
446
+ snprintfz(key, 1024, "%s:%s", id, name);
447
+ struct dict_unique_id_name *dun = dictionary_set(dict, key, NULL, sizeof(struct dict_unique_id_name));
448
+ if(!dun->existing) {
449
+ dun->existing = true;
450
+ dun->i = *max_id;
451
+ (*max_id)++;
452
+ dun->id = id;
453
+ dun->name = name;
454
+ }
455
+
456
+ return (ssize_t)dun->i;
457
+}
458
+
459
+static size_t registered_results_to_json_multinode(DICTIONARY *results, BUFFER *wb,
460
+ time_t after, time_t before,
461
+ time_t baseline_after, time_t baseline_before,
462
+ size_t points, WEIGHTS_METHOD method,
463
+ RRDR_TIME_GROUPING group, RRDR_OPTIONS options, uint32_t shifts,
464
+ size_t examined_dimensions, usec_t duration,
465
+ WEIGHTS_STATS *stats,
466
+ struct query_versions *versions) {
467
+ results_header_to_json(results, wb, after, before, baseline_after, baseline_before,
468
+ points, method, group, options, shifts, examined_dimensions, duration, stats);
469
+
470
+ version_hashes_api_v2(wb, versions);
471
+
472
+ bool baseline = method == WEIGHTS_METHOD_MC_KS2 || method == WEIGHTS_METHOD_MC_VOLUME;
473
+ multinode_data_schema(wb, options, "schema", baseline);
474
+
475
+ DICTIONARY *dict_nodes = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct dict_unique_name));
476
+ DICTIONARY *dict_contexts = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct dict_unique_name));
477
+ DICTIONARY *dict_instances = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct dict_unique_id_name));
478
+ DICTIONARY *dict_dimensions = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct dict_unique_id_name));
479
+
480
+ buffer_json_member_add_array(wb, "points");
481
+
482
+ size_t total_dimensions = 0, node_dims = 0, context_dims = 0, instance_dims = 0;
483
+ NETDATA_DOUBLE context_total_weight = 0.0, instance_total_weight = 0.0, node_total_weight = 0.0;
484
+ STORAGE_POINT context_hsp = STORAGE_POINT_UNSET, instance_hsp = STORAGE_POINT_UNSET, node_hsp = STORAGE_POINT_UNSET;
485
+ STORAGE_POINT context_bsp = STORAGE_POINT_UNSET, instance_bsp = STORAGE_POINT_UNSET, node_bsp = STORAGE_POINT_UNSET;
486
+ struct register_result *t;
487
+ RRDHOST *last_host = NULL;
488
+ RRDCONTEXT_ACQUIRED *last_rca = NULL;
489
+ RRDINSTANCE_ACQUIRED *last_ria = NULL;
490
+ ssize_t di = -1, ii = -1, ci = -1, ni = -1;
491
+ ssize_t di_max = 0, ii_max = 0, ci_max = 0, ni_max = 0;
492
+ dfe_start_read(results, t) {
493
+
494
+ // close instance
495
+ if(t->ria != last_ria && last_ria) {
496
+ storage_point_to_json(wb, WPT_INSTANCE, di, ii, ci, ni, instance_total_weight / (double) instance_dims, &instance_hsp, &instance_bsp, options, baseline);
497
+
498
+ last_ria = NULL;
499
+ instance_dims = 0;
500
+ instance_total_weight = 0.0;
501
+ instance_hsp = instance_bsp = STORAGE_POINT_UNSET;
502
+ }
503
+
504
+ // close context
505
+ if(t->rca != last_rca && last_rca) {
506
+ storage_point_to_json(wb, WPT_CONTEXT, di, ii, ci, ni, context_total_weight / (double) context_dims, &context_hsp, &instance_bsp, options, baseline);
507
+ last_rca = NULL;
508
+ context_dims = 0;
509
+ context_total_weight = 0.0;
510
+ context_hsp = context_bsp = STORAGE_POINT_UNSET;
511
+ }
512
+
513
+ // close node
514
+ if(t->host != last_host && last_host) {
515
+ storage_point_to_json(wb, WPT_NODE, di, ii, ci, ni, node_total_weight / (double) node_dims, &node_hsp, &node_bsp, options, baseline);
516
+ last_host = NULL;
517
+ node_dims = 0;
518
+ node_total_weight = 0.0;
519
+ node_hsp = node_bsp = STORAGE_POINT_UNSET;
520
+ }
521
+
522
+ // open node
523
+ if(t->host != last_host) {
524
+ last_host = t->host;
525
+ ni = dict_unique_name_add(dict_nodes, t->host->machine_guid, &ni_max);
526
+ }
527
+
528
+ // open context
529
+ if(t->rca != last_rca) {
530
+ last_rca = t->rca;
531
+ ci = dict_unique_name_add(dict_contexts, rrdcontext_acquired_id(t->rca), &ci_max);
532
+ }
533
+
534
+ // open instance
535
+ if(t->ria != last_ria) {
536
+ last_ria = t->ria;
537
+ ii = dict_unique_id_name_add(dict_instances, rrdinstance_acquired_id(t->ria), rrdinstance_acquired_name(t->ria), &ii_max);
538
+ }
539
+
540
+ di = dict_unique_id_name_add(dict_dimensions, rrdmetric_acquired_id(t->rma), rrdmetric_acquired_name(t->rma), &di_max);
541
+ storage_point_to_json(wb, WPT_DIMENSION, di, ii, ci, ni, t->value, &t->highlighted, &t->baseline, options, baseline);
542
+
543
+ instance_total_weight += t->value;
544
+ context_total_weight += t->value;
545
+ node_total_weight += t->value;
546
+
547
+ storage_point_merge_to(instance_hsp, t->highlighted);
548
+ storage_point_merge_to(context_hsp, t->highlighted);
549
+ storage_point_merge_to(node_hsp, t->highlighted);
550
+
551
+ if(baseline) {
552
+ storage_point_merge_to(instance_bsp, t->baseline);
553
+ storage_point_merge_to(context_bsp, t->baseline);
554
+ storage_point_merge_to(node_bsp, t->baseline);
555
+ }
556
+
557
+ instance_dims++;
558
+ context_dims++;
559
+ node_dims++;
560
+ total_dimensions++;
561
+ }
562
+ dfe_done(t);
563
+
564
+ // close instance
565
+ if(last_ria)
566
+ storage_point_to_json(wb, WPT_INSTANCE, di, ii, ci, ni, instance_total_weight / (double) instance_dims, &instance_hsp, &instance_bsp, options, baseline);
567
+
568
+ // close context
569
+ if(last_rca)
570
+ storage_point_to_json(wb, WPT_CONTEXT, di, ii, ci, ni, context_total_weight / (double) context_dims, &context_hsp, &instance_bsp, options, baseline);
571
+
572
+ // close node
573
+ if(last_host)
574
+ storage_point_to_json(wb, WPT_NODE, di, ii, ci, ni, node_total_weight / (double) node_dims, &node_hsp, &node_bsp, options, baseline);
575
+
576
+ buffer_json_array_close(wb); // points
577
+
578
+ buffer_json_member_add_array(wb, "nodes");
579
+ {
580
+ struct dict_unique_name *dun;
581
+ dfe_start_read(dict_nodes, dun) {
582
+ buffer_json_add_array_item_object(wb);
583
+ buffer_json_member_add_string(wb, "mg", dun_dfe.name);
584
+ buffer_json_member_add_int64(wb, "ni", dun->i);
585
+ buffer_json_object_close(wb);
586
+ }
587
+ dfe_done(dun);
588
+ }
589
+ buffer_json_array_close(wb);
590
+
591
+ buffer_json_member_add_array(wb, "contexts");
592
+ {
593
+ struct dict_unique_name *dun;
594
+ dfe_start_read(dict_contexts, dun) {
595
+ buffer_json_add_array_item_object(wb);
596
+ buffer_json_member_add_string(wb, "id", dun_dfe.name);
597
+ buffer_json_member_add_int64(wb, "ci", dun->i);
598
+ buffer_json_object_close(wb);
599
+ }
600
+ dfe_done(dun);
601
+ }
602
+ buffer_json_array_close(wb);
603
+
604
+ buffer_json_member_add_array(wb, "instances");
605
+ {
606
+ struct dict_unique_id_name *dun;
607
+ dfe_start_read(dict_instances, dun) {
608
+ buffer_json_add_array_item_object(wb);
609
+ buffer_json_member_add_string(wb, "id", dun->id);
610
+ if(dun->id != dun->name)
611
+ buffer_json_member_add_string(wb, "nm", dun->name);
612
+ buffer_json_member_add_int64(wb, "ii", dun->i);
613
+ buffer_json_object_close(wb);
614
+ }
615
+ dfe_done(dun);
616
+ }
617
+ buffer_json_array_close(wb);
618
+
619
+ buffer_json_member_add_array(wb, "dimensions");
620
+ {
621
+ struct dict_unique_id_name *dun;
622
+ dfe_start_read(dict_dimensions, dun) {
623
+ buffer_json_add_array_item_object(wb);
624
+ buffer_json_member_add_string(wb, "id", dun->id);
625
+ if(dun->id != dun->name)
626
+ buffer_json_member_add_string(wb, "nm", dun->name);
627
+ buffer_json_member_add_int64(wb, "di", dun->i);
628
+ buffer_json_object_close(wb);
629
+ }
630
+ dfe_done(dun);
631
+ }
632
+ buffer_json_array_close(wb);
633
+
634
+ buffer_json_member_add_uint64(wb, "correlated_dimensions", total_dimensions);
635
+ buffer_json_member_add_uint64(wb, "total_dimensions_count", examined_dimensions);
636
+ buffer_json_finalize(wb);
637
+
638
+ dictionary_destroy(dict_nodes);
639
+ dictionary_destroy(dict_contexts);
640
+ dictionary_destroy(dict_instances);
641
+ dictionary_destroy(dict_dimensions);
642
+
643
+ return total_dimensions;
644
+}
645
+
646
// ----------------------------------------------------------------------------
647
// KS2 algorithm functions
648
821
ONEWAYALLOC *owa, RRDHOST *host,
822
RRDCONTEXT_ACQUIRED *rca, RRDINSTANCE_ACQUIRED *ria, RRDMETRIC_ACQUIRED *rma,
823
time_t after, time_t before, size_t points, RRDR_OPTIONS options,
472
- RRDR_TIME_GROUPING group_method, const char *group_options, size_t tier,
824
+ RRDR_TIME_GROUPING time_group_method, const char *time_group_options, size_t tier,
825
WEIGHTS_STATS *stats,
474
- size_t *entries
826
+ size_t *entries,
827
+ STORAGE_POINT *sp
828
) {
829
830
NETDATA_DOUBLE *ret = NULL;
839
.before = before,
840
.points = points,
841
.options = options,
489
- .time_group_method = group_method,
490
- .time_group_options = group_options,
842
+ .time_group_method = time_group_method,
843
+ .time_group_options = time_group_options,
844
.tier = tier,
845
.query_source = QUERY_SOURCE_API_WEIGHTS,
846
.priority = STORAGE_PRIORITY_SYNCHRONOUS,
876
*entries = rrdr_rows(r);
877
ret = onewayalloc_mallocz(owa, sizeof(NETDATA_DOUBLE) * rrdr_rows(r));
878
879
+ if(sp)
880
+ *sp = r->drs[0];
881
+
882
// copy the points of the dimension to a contiguous array
883
// there is no need to check for empty values, since empty values are already zero
884
// https://github.com/netdata/netdata/blob/6e3144683a73a2024d51425b20ecfd569034c858/web/api/queries/average/average.c#L41-L43
896
time_t baseline_after, time_t baseline_before,
897
time_t after, time_t before,
898
size_t points, RRDR_OPTIONS options,
543
- RRDR_TIME_GROUPING group_method, const char *group_options, size_t tier,
899
+ RRDR_TIME_GROUPING time_group_method, const char *time_group_options, size_t tier,
900
uint32_t shifts,
901
WEIGHTS_STATS *stats, bool register_zero
902
) {
906
ONEWAYALLOC *owa = onewayalloc_create(16 * 1024);
907
908
size_t high_points = 0;
909
+ STORAGE_POINT highlighted_sp;
910
NETDATA_DOUBLE *highlight = rrd2rrdr_ks2(
911
owa, host, rca, ria, rma, after, before, points,
555
- options, group_method, group_options, tier, stats, &high_points);
912
+ options, time_group_method, time_group_options, tier, stats, &high_points, &highlighted_sp);
913
914
if(!highlight)
915
goto cleanup;
916
917
size_t base_points = 0;
918
+ STORAGE_POINT baseline_sp;
919
NETDATA_DOUBLE *baseline = rrd2rrdr_ks2(
920
owa, host, rca, ria, rma, baseline_after, baseline_before, high_points << shifts,
563
- options, group_method, group_options, tier, stats, &base_points);
921
+ options, time_group_method, time_group_options, tier, stats, &base_points, &baseline_sp);
922
923
if(!baseline)
924
goto cleanup;
940
941
// to spread the results evenly, 0.0 needs to be the less correlated and 1.0 the most correlated
942
// so, we flip the result of kstwo()
585
- register_result(results, rca, ria, rma, 1.0 - prob, RESULT_IS_BASE_HIGH_RATIO, stats, register_zero);
943
+ register_result(results, host, rca, ria, rma, 1.0 - prob, RESULT_IS_BASE_HIGH_RATIO, &highlighted_sp, &baseline_sp, stats, register_zero);
944
}
945
946
cleanup:
950
// ----------------------------------------------------------------------------
951
// VOLUME algorithm functions
952
595
-static void merge_query_value_to_stats(QUERY_VALUE *qv, WEIGHTS_STATS *stats) {
596
- stats->db_queries++;
953
+static void merge_query_value_to_stats(QUERY_VALUE *qv, WEIGHTS_STATS *stats, size_t queries) {
954
+ stats->db_queries += queries;
955
stats->result_points += qv->result_points;
956
stats->db_points += qv->points_read;
957
for(size_t tier = 0; tier < storage_tiers ; tier++)
964
DICTIONARY *results,
965
time_t baseline_after, time_t baseline_before,
966
time_t after, time_t before,
609
- RRDR_OPTIONS options, RRDR_TIME_GROUPING group_method, const char *group_options,
967
+ RRDR_OPTIONS options, RRDR_TIME_GROUPING time_group_method, const char *time_group_options,
968
size_t tier,
969
WEIGHTS_STATS *stats, bool register_zero) {
970
971
options |= RRDR_OPTION_MATCH_IDS | RRDR_OPTION_ABSOLUTE | RRDR_OPTION_NATURAL_POINTS;
972
973
QUERY_VALUE baseline_average = rrdmetric2value(host, rca, ria, rma, baseline_after, baseline_before,
616
- options, group_method, group_options, tier, 0,
974
+ options, time_group_method, time_group_options, tier, 0,
975
QUERY_SOURCE_API_WEIGHTS, STORAGE_PRIORITY_SYNCHRONOUS);
618
- merge_query_value_to_stats(&baseline_average, stats);
976
+ merge_query_value_to_stats(&baseline_average, stats, 1);
977
978
if(!netdata_double_isnumber(baseline_average.value)) {
979
// this means no data for the baseline window, but we may have data for the highlighted one - assume zero
981
}
982
983
QUERY_VALUE highlight_average = rrdmetric2value(host, rca, ria, rma, after, before,
626
- options, group_method, group_options, tier, 0,
984
+ options, time_group_method, time_group_options, tier, 0,
985
QUERY_SOURCE_API_WEIGHTS, STORAGE_PRIORITY_SYNCHRONOUS);
628
- merge_query_value_to_stats(&highlight_average, stats);
986
+ merge_query_value_to_stats(&highlight_average, stats, 1);
987
988
if(!netdata_double_isnumber(highlight_average.value))
989
return;
998
QUERY_VALUE highlight_countif = rrdmetric2value(host, rca, ria, rma, after, before,
999
options, RRDR_GROUPING_COUNTIF, highlight_countif_options, tier, 0,
1000
QUERY_SOURCE_API_WEIGHTS, STORAGE_PRIORITY_SYNCHRONOUS);
643
- merge_query_value_to_stats(&highlight_countif, stats);
1001
+ merge_query_value_to_stats(&highlight_countif, stats, 1);
1002
1003
if(!netdata_double_isnumber(highlight_countif.value)) {
1004
info("WEIGHTS: highlighted countif query failed, but highlighted average worked - strange...");
1021
pcent = highlight_countif.value;
1022
}
1023
666
- register_result(results, rca, ria, rma, pcent, flags, stats, register_zero);
1024
+ register_result(results, host, rca, ria, rma, pcent, flags, &highlight_average.sp, &baseline_average.sp, stats, register_zero);
1025
}
1026
1027
// ----------------------------------------------------------------------------
670
-// ANOMALY RATE algorithm functions
1028
+// VALUE / ANOMALY RATE algorithm functions
1029
672
-static void rrdset_weights_anomaly_rate(
1030
+static void rrdset_weights_value(
1031
RRDHOST *host,
1032
RRDCONTEXT_ACQUIRED *rca, RRDINSTANCE_ACQUIRED *ria, RRDMETRIC_ACQUIRED *rma,
1033
DICTIONARY *results,
1034
time_t after, time_t before,
677
- RRDR_OPTIONS options, RRDR_TIME_GROUPING group_method, const char *group_options,
1035
+ RRDR_OPTIONS options, RRDR_TIME_GROUPING time_group_method, const char *time_group_options,
1036
size_t tier,
1037
WEIGHTS_STATS *stats, bool register_zero) {
1038
681
- options |= RRDR_OPTION_MATCH_IDS | RRDR_OPTION_ANOMALY_BIT | RRDR_OPTION_NATURAL_POINTS;
1039
+ options |= RRDR_OPTION_MATCH_IDS | RRDR_OPTION_NATURAL_POINTS;
1040
1041
QUERY_VALUE qv = rrdmetric2value(host, rca, ria, rma, after, before,
684
- options, group_method, group_options, tier, 0,
1042
+ options, time_group_method, time_group_options, tier, 0,
1043
QUERY_SOURCE_API_WEIGHTS, STORAGE_PRIORITY_SYNCHRONOUS);
1044
687
- merge_query_value_to_stats(&qv, stats);
1045
+ merge_query_value_to_stats(&qv, stats, 1);
1046
1047
if(netdata_double_isnumber(qv.value))
690
- register_result(results, rca, ria, rma, qv.value, 0, stats, register_zero);
1048
+ register_result(results, host, rca, ria, rma, qv.value, 0, &qv.sp, NULL, stats, register_zero);
1049
+}
1050
+
1051
+struct query_weights_data {
1052
+ QUERY_WEIGHTS_REQUEST *qwr;
1053
+
1054
+ SIMPLE_PATTERN *scope_nodes_sp;
1055
+ SIMPLE_PATTERN *scope_contexts_sp;
1056
+ SIMPLE_PATTERN *nodes_sp;
1057
+ SIMPLE_PATTERN *contexts_sp;
1058
+ SIMPLE_PATTERN *instances_sp;
1059
+ SIMPLE_PATTERN *dimensions_sp;
1060
+ SIMPLE_PATTERN *labels_sp;
1061
+ SIMPLE_PATTERN *alerts_sp;
1062
+
1063
+ usec_t now_us;
1064
+ usec_t started_us;
1065
+ usec_t timeout_us;
1066
+ bool timed_out;
1067
+ bool interrupted;
1068
+
1069
+ size_t examined_dimensions;
1070
+ bool register_zero;
1071
+
1072
+ DICTIONARY *results;
1073
+ WEIGHTS_STATS stats;
1074
+
1075
+ uint32_t shifts;
1076
+
1077
+ struct query_versions versions;
1078
+};
1079
+
1080
+static void rrdset_weights_multi_dimensional_value(struct query_weights_data *qwd) {
1081
+ QUERY_TARGET_REQUEST qtr = {
1082
+ .version = 1,
1083
+ .scope_nodes = qwd->qwr->scope_nodes,
1084
+ .scope_contexts = qwd->qwr->scope_contexts,
1085
+ .nodes = qwd->qwr->nodes,
1086
+ .contexts = qwd->qwr->contexts,
1087
+ .instances = qwd->qwr->instances,
1088
+ .dimensions = qwd->qwr->dimensions,
1089
+ .labels = qwd->qwr->labels,
1090
+ .alerts = qwd->qwr->alerts,
1091
+ .after = qwd->qwr->after,
1092
+ .before = qwd->qwr->before,
1093
+ .points = 1,
1094
+ .options = qwd->qwr->options | RRDR_OPTION_NATURAL_POINTS,
1095
+ .time_group_method = qwd->qwr->time_group_method,
1096
+ .time_group_options = qwd->qwr->time_group_options,
1097
+ .tier = qwd->qwr->tier,
1098
+ .timeout_ms = qwd->qwr->timeout_ms,
1099
+ .query_source = QUERY_SOURCE_API_WEIGHTS,
1100
+ .priority = STORAGE_PRIORITY_NORMAL,
1101
+ };
1102
+
1103
+ ONEWAYALLOC *owa = onewayalloc_create(16 * 1024);
1104
+ RRDR *r = rrd2rrdr(owa, query_target_create(&qtr));
1105
+
1106
+ if(rrdr_rows(r) != 1 || !r->d)
1107
+ goto cleanup;
1108
+
1109
+ QUERY_VALUE qv = {
1110
+ .after = r->view.after,
1111
+ .before = r->view.before,
1112
+ .points_read = r->stats.db_points_read,
1113
+ .result_points = r->stats.result_points_generated,
1114
+ };
1115
+
1116
+ size_t queries = 0;
1117
+ for(size_t d = 0; d < r->d ;d++) {
1118
+ if(!rrdr_dimension_should_be_exposed(r->od[d], qwd->qwr->options))
1119
+ continue;
1120
+
1121
+ long i = 0; // only one row
1122
+ NETDATA_DOUBLE *cn = &r->v[ i * r->d ];
1123
+ NETDATA_DOUBLE *ar = &r->ar[ i * r->d ];
1124
+
1125
+ qv.value = cn[d];
1126
+ qv.anomaly_rate = ar[d];
1127
+ qv.sp = *r->drs;
1128
+
1129
+ if(netdata_double_isnumber(qv.value)) {
1130
+ QUERY_METRIC *qm = query_metric(r->internal.qt, d);
1131
+ QUERY_DIMENSION *qd = query_dimension(r->internal.qt, qm->link.query_dimension_id);
1132
+ QUERY_INSTANCE *qi = query_instance(r->internal.qt, qm->link.query_instance_id);
1133
+ QUERY_CONTEXT *qc = query_context(r->internal.qt, qm->link.query_context_id);
1134
+ QUERY_NODE *qn = query_node(r->internal.qt, qm->link.query_node_id);
1135
+
1136
+ register_result(qwd->results, qn->rrdhost, qc->rca, qi->ria, qd->rma, qv.value, 0, &qv.sp,
1137
+ NULL, &qwd->stats, qwd->register_zero);
1138
+ }
1139
+
1140
+ queries++;
1141
+ }
1142
+
1143
+ merge_query_value_to_stats(&qv, &qwd->stats, queries);
1144
+
1145
+cleanup:
1146
+ rrdr_free(owa, r);
1147
+ onewayalloc_destroy(owa);
1148
}
1149
1150
// ----------------------------------------------------------------------------
1234
// ----------------------------------------------------------------------------
1235
// The main function
1236
780
-int web_api_v1_weights(
781
- RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS_FORMAT format,
782
- RRDR_TIME_GROUPING group, const char *group_options,
783
- time_t baseline_after, time_t baseline_before,
784
- time_t after, time_t before,
785
- size_t points, RRDR_OPTIONS options, SIMPLE_PATTERN *contexts, size_t tier, size_t timeout) {
1237
+static ssize_t weights_for_rrdmetric(void *data, RRDHOST *host, RRDCONTEXT_ACQUIRED *rca, RRDINSTANCE_ACQUIRED *ria, RRDMETRIC_ACQUIRED *rma) {
1238
+ struct query_weights_data *qwd = data;
1239
+ QUERY_WEIGHTS_REQUEST *qwr = qwd->qwr;
1240
787
- WEIGHTS_STATS stats = {};
1241
+ qwd->now_us = now_realtime_usec();
1242
+ if(qwd->now_us - qwd->started_us > qwd->timeout_us) {
1243
+ qwd->timed_out = true;
1244
+ return -1;
1245
+ }
1246
+
1247
+ if(qwd->qwr->interrupt_callback && qwd->qwr->interrupt_callback(qwd->qwr->interrupt_callback_data)) {
1248
+ qwd->interrupted = true;
1249
+ return -1;
1250
+ }
1251
+
1252
+ qwd->examined_dimensions++;
1253
+
1254
+ switch(qwr->method) {
1255
+ case WEIGHTS_METHOD_VALUE:
1256
+ rrdset_weights_value(
1257
+ host, rca, ria, rma,
1258
+ qwd->results,
1259
+ qwr->after, qwr->before,
1260
+ qwr->options, qwr->time_group_method, qwr->time_group_options, qwr->tier,
1261
+ &qwd->stats, qwd->register_zero
1262
+ );
1263
+ break;
1264
+
1265
+ case WEIGHTS_METHOD_ANOMALY_RATE:
1266
+ qwr->options |= RRDR_OPTION_ANOMALY_BIT;
1267
+ rrdset_weights_value(
1268
+ host, rca, ria, rma,
1269
+ qwd->results,
1270
+ qwr->after, qwr->before,
1271
+ qwr->options, qwr->time_group_method, qwr->time_group_options, qwr->tier,
1272
+ &qwd->stats, qwd->register_zero
1273
+ );
1274
+ break;
1275
+
1276
+ case WEIGHTS_METHOD_MC_VOLUME:
1277
+ rrdset_metric_correlations_volume(
1278
+ host, rca, ria, rma,
1279
+ qwd->results,
1280
+ qwr->baseline_after, qwr->baseline_before,
1281
+ qwr->after, qwr->before,
1282
+ qwr->options, qwr->time_group_method, qwr->time_group_options, qwr->tier,
1283
+ &qwd->stats, qwd->register_zero
1284
+ );
1285
+ break;
1286
+
1287
+ default:
1288
+ case WEIGHTS_METHOD_MC_KS2:
1289
+ rrdset_metric_correlations_ks2(
1290
+ host, rca, ria, rma,
1291
+ qwd->results,
1292
+ qwr->baseline_after, qwr->baseline_before,
1293
+ qwr->after, qwr->before, qwr->points,
1294
+ qwr->options, qwr->time_group_method, qwr->time_group_options, qwr->tier, qwd->shifts,
1295
+ &qwd->stats, qwd->register_zero
1296
+ );
1297
+ break;
1298
+ }
1299
+
1300
+ return 1;
1301
+}
1302
+
1303
+static ssize_t weights_do_context_callback(void *data, RRDCONTEXT_ACQUIRED *rca, bool queryable_context) {
1304
+ if(!queryable_context)
1305
+ return false;
1306
+
1307
+ struct query_weights_data *qwd = data;
1308
+
1309
+ bool has_retention = false;
1310
+ switch(qwd->qwr->method) {
1311
+ case WEIGHTS_METHOD_VALUE:
1312
+ case WEIGHTS_METHOD_ANOMALY_RATE:
1313
+ has_retention = rrdcontext_retention_match(rca, qwd->qwr->after, qwd->qwr->before);
1314
+ break;
1315
+
1316
+ case WEIGHTS_METHOD_MC_KS2:
1317
+ case WEIGHTS_METHOD_MC_VOLUME:
1318
+ has_retention = rrdcontext_retention_match(rca, qwd->qwr->after, qwd->qwr->before);
1319
+ if(has_retention)
1320
+ has_retention = rrdcontext_retention_match(rca, qwd->qwr->baseline_after, qwd->qwr->baseline_before);
1321
+ break;
1322
+ }
1323
+
1324
+ if(!has_retention)
1325
+ return 0;
1326
+
1327
+ ssize_t ret = weights_foreach_rrdmetric_in_context(rca,
1328
+ qwd->instances_sp,
1329
+ NULL,
1330
+ qwd->labels_sp,
1331
+ qwd->alerts_sp,
1332
+ qwd->dimensions_sp,
1333
+ true, true, qwd->qwr->version,
1334
+ weights_for_rrdmetric, qwd);
1335
+ return ret;
1336
+}
1337
+
1338
+ssize_t weights_do_node_callback(void *data, RRDHOST *host, bool queryable) {
1339
+ if(!queryable)
1340
+ return 0;
1341
+
1342
+ struct query_weights_data *qwd = data;
1343
+
1344
+ ssize_t ret = query_scope_foreach_context(host, qwd->qwr->scope_contexts,
1345
+ qwd->scope_contexts_sp, qwd->contexts_sp,
1346
+ weights_do_context_callback, queryable, qwd);
1347
+
1348
+ return ret;
1349
+}
1350
+
1351
+int web_api_v12_weights(BUFFER *wb, QUERY_WEIGHTS_REQUEST *qwr) {
1352
789
- DICTIONARY *results = register_result_init();
790
- DICTIONARY *metrics = NULL;
1353
char *error = NULL;
1354
int resp = HTTP_RESP_OK;
1355
1356
// if the user didn't give a timeout
1357
// assume 60 seconds
796
- if(!timeout)
797
- timeout = 60 * MSEC_PER_SEC;
1358
+ if(!qwr->timeout_ms)
1359
+ qwr->timeout_ms = 5 * 60 * MSEC_PER_SEC;
1360
1361
// if the timeout is less than 1 second
1362
// make it at least 1 second
801
- if(timeout < (long)(1 * MSEC_PER_SEC))
802
- timeout = 1 * MSEC_PER_SEC;
803
-
804
- usec_t timeout_usec = timeout * USEC_PER_MS;
805
- usec_t started_usec = now_realtime_usec();
1363
+ if(qwr->timeout_ms < (long)(1 * MSEC_PER_SEC))
1364
+ qwr->timeout_ms = 1 * MSEC_PER_SEC;
1365
+
1366
+ struct query_weights_data qwd = {
1367
+ .qwr = qwr,
1368
+
1369
+ .scope_nodes_sp = string_to_simple_pattern(qwr->scope_nodes),
1370
+ .scope_contexts_sp = string_to_simple_pattern(qwr->scope_contexts),
1371
+ .nodes_sp = string_to_simple_pattern(qwr->nodes),
1372
+ .contexts_sp = string_to_simple_pattern(qwr->contexts),
1373
+ .instances_sp = string_to_simple_pattern(qwr->instances),
1374
+ .dimensions_sp = string_to_simple_pattern(qwr->dimensions),
1375
+ .labels_sp = string_to_simple_pattern(qwr->labels),
1376
+ .alerts_sp = string_to_simple_pattern(qwr->alerts),
1377
+ .timeout_us = qwr->timeout_ms * USEC_PER_MS,
1378
+ .started_us = now_realtime_usec(),
1379
+ .timed_out = false,
1380
+ .examined_dimensions = 0,
1381
+ .register_zero = true,
1382
+ .results = register_result_init(),
1383
+ .stats = {},
1384
+ .shifts = 0,
1385
+ };
1386
807
- if(!rrdr_relative_window_to_absolute(&after, &before, NULL))
1387
+ if(!rrdr_relative_window_to_absolute(&qwr->after, &qwr->before, NULL))
1388
buffer_no_cacheable(wb);
1389
810
- if (before <= after) {
1390
+ if (qwr->before <= qwr->after) {
1391
resp = HTTP_RESP_BAD_REQUEST;
1392
error = "Invalid selected time-range.";
1393
goto cleanup;
1394
}
1395
816
- uint32_t shifts = 0;
817
- if(method == WEIGHTS_METHOD_MC_KS2 || method == WEIGHTS_METHOD_MC_VOLUME) {
818
- if(!points) points = 500;
1396
+ if(qwr->method == WEIGHTS_METHOD_MC_KS2 || qwr->method == WEIGHTS_METHOD_MC_VOLUME) {
1397
+ if(!qwr->points) qwr->points = 500;
1398
820
- if(baseline_before <= API_RELATIVE_TIME_MAX)
821
- baseline_before += after;
1399
+ if(qwr->baseline_before <= API_RELATIVE_TIME_MAX)
1400
+ qwr->baseline_before += qwr->after;
1401
823
- rrdr_relative_window_to_absolute(&baseline_after, &baseline_before, NULL);
1402
+ rrdr_relative_window_to_absolute(&qwr->baseline_after, &qwr->baseline_before, NULL);
1403
825
- if (baseline_before <= baseline_after) {
1404
+ if (qwr->baseline_before <= qwr->baseline_after) {
1405
resp = HTTP_RESP_BAD_REQUEST;
1406
error = "Invalid baseline time-range.";
1407
goto cleanup;
1408
}
1409
1410
// baseline should be a power of two multiple of highlight
832
- long long base_delta = baseline_before - baseline_after;
833
- long long high_delta = before - after;
1411
+ long long base_delta = qwr->baseline_before - qwr->baseline_after;
1412
+ long long high_delta = qwr->before - qwr->after;
1413
uint32_t multiplier = (uint32_t)round((double)base_delta / (double)high_delta);
1414
1415
// check if the multiplier is a power of two
1431
// we need to do, to divide baseline numbers to match
1432
// the highlight ones
1433
while(multiplier > 1) {
855
- shifts++;
1434
+ qwd.shifts++;
1435
multiplier = multiplier >> 1;
1436
}
1437
1438
// if the baseline size will not comply to MAX_POINTS
1439
// lower the window of the baseline
861
- while(shifts && (points << shifts) > MAX_POINTS)
862
- shifts--;
1440
+ while(qwd.shifts && (qwr->points << qwd.shifts) > MAX_POINTS)
1441
+ qwd.shifts--;
1442
1443
// if the baseline size still does not comply to MAX_POINTS
1444
// lower the resolution of the highlight and the baseline
866
- while((points << shifts) > MAX_POINTS)
867
- points = points >> 1;
1445
+ while((qwr->points << qwd.shifts) > MAX_POINTS)
1446
+ qwr->points = qwr->points >> 1;
1447
869
- if(points < 15) {
1448
+ if(qwr->points < 15) {
1449
resp = HTTP_RESP_BAD_REQUEST;
1450
error = "Too few points available, at least 15 are needed.";
1451
goto cleanup;
1452
}
1453
1454
// adjust the baseline to be multiplier times bigger than the highlight
876
- baseline_after = baseline_before - (high_delta << shifts);
1455
+ qwr->baseline_after = qwr->baseline_before - (high_delta << qwd.shifts);
1456
}
1457
879
- size_t examined_dimensions = 0;
1458
+ if(qwr->options & RRDR_OPTION_NONZERO) {
1459
+ qwd.register_zero = false;
1460
881
- bool register_zero = true;
882
- if(options & RRDR_OPTION_NONZERO) {
883
- register_zero = false;
884
- options &= ~RRDR_OPTION_NONZERO;
1461
+ // remove it to run the queries without it
1462
+ qwr->options &= ~RRDR_OPTION_NONZERO;
1463
}
1464
887
- metrics = rrdcontext_all_metrics_to_dict(host, contexts);
888
- struct metric_entry *me;
889
-
890
- // for every metric_entry in the dictionary
891
- dfe_start_read(metrics, me) {
892
- usec_t now_usec = now_realtime_usec();
893
- if(now_usec - started_usec > timeout_usec) {
894
- error = "timed out";
895
- resp = HTTP_RESP_GATEWAY_TIMEOUT;
896
- goto cleanup;
1465
+ if(qwr->host && qwr->version == 1)
1466
+ weights_do_node_callback(&qwd, qwr->host, true);
1467
+ else {
1468
+ if((qwd.qwr->method == WEIGHTS_METHOD_VALUE || qwd.qwr->method == WEIGHTS_METHOD_ANOMALY_RATE) && (qwd.contexts_sp || qwd.scope_contexts_sp)) {
1469
+ rrdset_weights_multi_dimensional_value(&qwd);
1470
}
898
-
899
- examined_dimensions++;
900
-
901
- switch(method) {
902
- case WEIGHTS_METHOD_ANOMALY_RATE:
903
- options |= RRDR_OPTION_ANOMALY_BIT;
904
- rrdset_weights_anomaly_rate(
905
- host,
906
- me->rca, me->ria, me->rma,
907
- results,
908
- after, before,
909
- options, group, group_options, tier,
910
- &stats, register_zero
911
- );
912
- break;
913
-
914
- case WEIGHTS_METHOD_MC_VOLUME:
915
- rrdset_metric_correlations_volume(
916
- host,
917
- me->rca, me->ria, me->rma,
918
- results,
919
- baseline_after, baseline_before,
920
- after, before,
921
- options, group, group_options, tier,
922
- &stats, register_zero
923
- );
924
- break;
925
-
926
- default:
927
- case WEIGHTS_METHOD_MC_KS2:
928
- rrdset_metric_correlations_ks2(
929
- host,
930
- me->rca, me->ria, me->rma,
931
- results,
932
- baseline_after, baseline_before,
933
- after, before, points,
934
- options, group, group_options, tier, shifts,
935
- &stats, register_zero
936
- );
937
- break;
1471
+ else {
1472
+ query_scope_foreach_host(qwd.scope_nodes_sp, qwd.nodes_sp,
1473
+ weights_do_node_callback, &qwd,
1474
+ &qwd.versions,
1475
+ NULL);
1476
}
1477
}
940
- dfe_done(me);
1478
942
- if(!register_zero)
943
- options |= RRDR_OPTION_NONZERO;
1479
+ if(!qwd.register_zero) {
1480
+ // put it back, to show it in the response
1481
+ qwr->options |= RRDR_OPTION_NONZERO;
1482
+ }
1483
+
1484
+ if(qwd.timed_out) {
1485
+ error = "timed out";
1486
+ resp = HTTP_RESP_GATEWAY_TIMEOUT;
1487
+ goto cleanup;
1488
+ }
1489
+
1490
+ if(qwd.interrupted) {
1491
+ error = "interrupted";
1492
+ resp = HTTP_RESP_BACKEND_FETCH_FAILED;
1493
+ goto cleanup;
1494
+ }
1495
+
1496
+ if(!qwd.register_zero)
1497
+ qwr->options |= RRDR_OPTION_NONZERO;
1498
945
- if(!(options & RRDR_OPTION_RETURN_RAW))
946
- spread_results_evenly(results, &stats);
1499
+ if(!(qwr->options & RRDR_OPTION_RETURN_RAW) && qwr->method != WEIGHTS_METHOD_VALUE)
1500
+ spread_results_evenly(qwd.results, &qwd.stats);
1501
1502
usec_t ended_usec = now_realtime_usec();
1503
1505
buffer_flush(wb);
1506
1507
size_t added_dimensions = 0;
954
- switch(format) {
1508
+ switch(qwr->format) {
1509
case WEIGHTS_FORMAT_CHARTS:
1510
added_dimensions =
1511
registered_results_to_json_charts(
958
- results, wb,
959
- after, before,
960
- baseline_after, baseline_before,
961
- points, method, group, options, shifts,
962
- examined_dimensions,
963
- ended_usec - started_usec, &stats);
1512
+ qwd.results, wb,
1513
+ qwr->after, qwr->before,
1514
+ qwr->baseline_after, qwr->baseline_before,
1515
+ qwr->points, qwr->method, qwr->time_group_method, qwr->options, qwd.shifts,
1516
+ qwd.examined_dimensions,
1517
+ ended_usec - qwd.started_us, &qwd.stats);
1518
break;
1519
966
- default:
1520
case WEIGHTS_FORMAT_CONTEXTS:
1521
added_dimensions =
1522
registered_results_to_json_contexts(
970
- results, wb,
971
- after, before,
972
- baseline_after, baseline_before,
973
- points, method, group, options, shifts,
974
- examined_dimensions,
975
- ended_usec - started_usec, &stats);
1523
+ qwd.results, wb,
1524
+ qwr->after, qwr->before,
1525
+ qwr->baseline_after, qwr->baseline_before,
1526
+ qwr->points, qwr->method, qwr->time_group_method, qwr->options, qwd.shifts,
1527
+ qwd.examined_dimensions,
1528
+ ended_usec - qwd.started_us, &qwd.stats);
1529
+ break;
1530
+
1531
+ default:
1532
+ case WEIGHTS_FORMAT_MULTINODE:
1533
+ added_dimensions =
1534
+ registered_results_to_json_multinode(
1535
+ qwd.results, wb,
1536
+ qwr->after, qwr->before,
1537
+ qwr->baseline_after, qwr->baseline_before,
1538
+ qwr->points, qwr->method, qwr->time_group_method, qwr->options, qwd.shifts,
1539
+ qwd.examined_dimensions,
1540
+ ended_usec - qwd.started_us, &qwd.stats, &qwd.versions);
1541
break;
1542
}
1543
1547
}
1548
1549
cleanup:
985
- if(metrics) dictionary_destroy(metrics);
986
- if(results) register_result_destroy(results);
1550
+ simple_pattern_free(qwd.scope_nodes_sp);
1551
+ simple_pattern_free(qwd.scope_contexts_sp);
1552
+ simple_pattern_free(qwd.nodes_sp);
1553
+ simple_pattern_free(qwd.contexts_sp);
1554
+ simple_pattern_free(qwd.instances_sp);
1555
+ simple_pattern_free(qwd.dimensions_sp);
1556
+ simple_pattern_free(qwd.labels_sp);
1557
+ simple_pattern_free(qwd.alerts_sp);
1558
+
1559
+ register_result_destroy(qwd.results);
1560
1561
if(error) {
1562
buffer_flush(wb);