/api/v2 part 9 (#14888)
* /api/v2/weights now supports group-by * /api/v2/weights now follows the same principles for describing responses as /api/v2/data
Costa Tsaousis committed
Apr 11, 2023 at 17:32 UTC
66fd79777c90e737887d76409cf7bdaa6cb9dbd4
3 files changed
+493
-159
web/api/queries/weights.c
+466
-159
@@ -303,116 +303,325 @@ static size_t registered_results_to_json_contexts(DICTIONARY *results, BUFFER *w
303
return total_dimensions;
304
}
305
306
+struct query_weights_data {
307
+ QUERY_WEIGHTS_REQUEST *qwr;
308
+
309
+ SIMPLE_PATTERN *scope_nodes_sp;
310
+ SIMPLE_PATTERN *scope_contexts_sp;
311
+ SIMPLE_PATTERN *nodes_sp;
312
+ SIMPLE_PATTERN *contexts_sp;
313
+ SIMPLE_PATTERN *instances_sp;
314
+ SIMPLE_PATTERN *dimensions_sp;
315
+ SIMPLE_PATTERN *labels_sp;
316
+ SIMPLE_PATTERN *alerts_sp;
317
+
318
+ usec_t timeout_us;
319
+ bool timed_out;
320
+ bool interrupted;
321
+
322
+ struct query_timings timings;
323
+
324
+ size_t examined_dimensions;
325
+ bool register_zero;
326
+
327
+ DICTIONARY *results;
328
+ WEIGHTS_STATS stats;
329
+
330
+ uint32_t shifts;
331
+
332
+ struct query_versions versions;
333
+};
334
+
335
+#define AGGREGATED_WEIGHT_EMPTY (struct aggregated_weight) { \
336
+ .min = NAN, \
337
+ .max = NAN, \
338
+ .sum = NAN, \
339
+ .count = 0, \
340
+ .hsp = STORAGE_POINT_UNSET, \
341
+ .bsp = STORAGE_POINT_UNSET, \
342
+}
343
+
344
+#define merge_into_aw(aw, t) do { \
345
+ if(!(aw).count) { \
346
+ (aw).count = 1; \
347
+ (aw).min = (aw).max = (aw).sum = (t)->value; \
348
+ (aw).hsp = (t)->highlighted; \
349
+ if(baseline) \
350
+ (aw).bsp = (t)->baseline; \
351
+ } \
352
+ else { \
353
+ (aw).count++; \
354
+ (aw).sum += (t)->value; \
355
+ if((t)->value < (aw).min) \
356
+ (aw).min = (t)->value; \
357
+ if((t)->value > (aw).max) \
358
+ (aw).max = (t)->value; \
359
+ storage_point_merge_to((aw).hsp, (t)->highlighted); \
360
+ if(baseline) \
361
+ storage_point_merge_to((aw).bsp, (t)->baseline); \
362
+ } \
363
+} while(0)
364
+
365
+static void results_header_to_json_v2(DICTIONARY *results __maybe_unused, BUFFER *wb, struct query_weights_data *qwd,
366
+ time_t after, time_t before,
367
+ time_t baseline_after, time_t baseline_before,
368
+ size_t points, WEIGHTS_METHOD method,
369
+ RRDR_TIME_GROUPING group, RRDR_OPTIONS options, uint32_t shifts,
370
+ size_t examined_dimensions __maybe_unused, usec_t duration,
371
+ WEIGHTS_STATS *stats, bool group_by) {
372
+
373
+ buffer_json_member_add_object(wb, "request");
374
+ buffer_json_member_add_string(wb, "method", weights_method_to_string(method));
375
+ web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", options);
376
+
377
+ buffer_json_member_add_object(wb, "scope");
378
+ buffer_json_member_add_string(wb, "scope_nodes", qwd->qwr->scope_nodes ? qwd->qwr->scope_nodes : "*");
379
+ buffer_json_member_add_string(wb, "scope_contexts", qwd->qwr->scope_contexts ? qwd->qwr->scope_contexts : "*");
380
+ buffer_json_object_close(wb);
381
+
382
+ buffer_json_member_add_object(wb, "selectors");
383
+ buffer_json_member_add_string(wb, "nodes", qwd->qwr->nodes ? qwd->qwr->nodes : "*");
384
+ buffer_json_member_add_string(wb, "contexts", qwd->qwr->contexts ? qwd->qwr->contexts : "*");
385
+ buffer_json_member_add_string(wb, "instances", qwd->qwr->instances ? qwd->qwr->instances : "*");
386
+ buffer_json_member_add_string(wb, "dimensions", qwd->qwr->dimensions ? qwd->qwr->dimensions : "*");
387
+ buffer_json_member_add_string(wb, "labels", qwd->qwr->labels ? qwd->qwr->labels : "*");
388
+ buffer_json_member_add_string(wb, "alerts", qwd->qwr->alerts ? qwd->qwr->alerts : "*");
389
+ buffer_json_object_close(wb);
390
+
391
+ buffer_json_member_add_object(wb, "window");
392
+ buffer_json_member_add_time_t(wb, "after", qwd->qwr->after);
393
+ buffer_json_member_add_time_t(wb, "before", qwd->qwr->before);
394
+ buffer_json_member_add_uint64(wb, "points", qwd->qwr->points);
395
+ if(qwd->qwr->options & RRDR_OPTION_SELECTED_TIER)
396
+ buffer_json_member_add_uint64(wb, "tier", qwd->qwr->tier);
397
+ else
398
+ buffer_json_member_add_string(wb, "tier", NULL);
399
+ buffer_json_object_close(wb);
400
+
401
+ if(method == WEIGHTS_METHOD_MC_KS2 || method == WEIGHTS_METHOD_MC_VOLUME) {
402
+ buffer_json_member_add_object(wb, "baseline");
403
+ buffer_json_member_add_time_t(wb, "baseline_after", qwd->qwr->baseline_after);
404
+ buffer_json_member_add_time_t(wb, "baseline_before", qwd->qwr->baseline_before);
405
+ buffer_json_object_close(wb);
406
+ }
407
+
408
+ buffer_json_member_add_object(wb, "aggregations");
409
+ buffer_json_member_add_object(wb, "time");
410
+ buffer_json_member_add_string(wb, "time_group", time_grouping_tostring(qwd->qwr->time_group_method));
411
+ buffer_json_member_add_string(wb, "time_group_options", qwd->qwr->time_group_options);
412
+ buffer_json_object_close(wb); // time
413
+
414
+ buffer_json_member_add_array(wb, "metrics");
415
+ buffer_json_add_array_item_object(wb);
416
+ {
417
+ buffer_json_member_add_array(wb, "group_by");
418
+ buffer_json_group_by_to_array(wb, qwd->qwr->group_by.group_by);
419
+ buffer_json_array_close(wb);
420
+
421
+// buffer_json_member_add_array(wb, "group_by_label");
422
+// buffer_json_array_close(wb);
423
+
424
+ buffer_json_member_add_string(wb, "aggregation", group_by_aggregate_function_to_string(qwd->qwr->group_by.aggregation));
425
+ }
426
+ buffer_json_object_close(wb); // 1st group by
427
+ buffer_json_array_close(wb); // array
428
+ buffer_json_object_close(wb); // aggregations
429
+
430
+ buffer_json_member_add_uint64(wb, "timeout", qwd->qwr->timeout_ms);
431
+ buffer_json_object_close(wb); // request
432
+
433
+ buffer_json_member_add_object(wb, "view");
434
+ buffer_json_member_add_string(wb, "format", (group_by)?"grouped":"full");
435
+ buffer_json_member_add_string(wb, "time_group", time_grouping_tostring(group));
436
+
437
+ buffer_json_member_add_object(wb, "window");
438
+ buffer_json_member_add_time_t(wb, "after", after);
439
+ buffer_json_member_add_time_t(wb, "before", before);
440
+ buffer_json_member_add_time_t(wb, "duration", before - after);
441
+ buffer_json_member_add_uint64(wb, "points", points);
442
+ buffer_json_object_close(wb);
443
+
444
+ if(method == WEIGHTS_METHOD_MC_KS2 || method == WEIGHTS_METHOD_MC_VOLUME) {
445
+ buffer_json_member_add_object(wb, "baseline");
446
+ buffer_json_member_add_time_t(wb, "baseline_after", baseline_after);
447
+ buffer_json_member_add_time_t(wb, "baseline_before", baseline_before);
448
+ buffer_json_member_add_time_t(wb, "baseline_duration", baseline_before - baseline_after);
449
+ buffer_json_member_add_uint64(wb, "baseline_points", points << shifts);
450
+ buffer_json_object_close(wb);
451
+ }
452
+
453
+ buffer_json_object_close(wb); // view
454
+
455
+ buffer_json_member_add_object(wb, "db");
456
+ {
457
+ buffer_json_member_add_double(wb, "query_time_ms", (double) duration / (double) USEC_PER_MS);
458
+ buffer_json_member_add_uint64(wb, "db_queries", stats->db_queries);
459
+ buffer_json_member_add_uint64(wb, "query_result_points", stats->result_points);
460
+ buffer_json_member_add_uint64(wb, "binary_searches", stats->binary_searches);
461
+ buffer_json_member_add_uint64(wb, "db_points_read", stats->db_points);
462
+
463
+ buffer_json_member_add_array(wb, "db_points_per_tier");
464
+ {
465
+ for (size_t tier = 0; tier < storage_tiers; tier++)
466
+ buffer_json_add_array_item_uint64(wb, stats->db_points_per_tier[tier]);
467
+ }
468
+ buffer_json_array_close(wb);
469
+ }
470
+ buffer_json_object_close(wb); // db
471
+}
472
+
473
typedef enum {
474
WPT_DIMENSION = 0,
475
WPT_INSTANCE = 1,
476
WPT_CONTEXT = 2,
477
WPT_NODE = 3,
478
+ WPT_GROUP = 4,
479
} WEIGHTS_POINT_TYPE;
480
313
-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) {
314
- buffer_json_add_array_item_array(wb);
481
+struct aggregated_weight {
482
+ const char *name;
483
+ NETDATA_DOUBLE min;
484
+ NETDATA_DOUBLE max;
485
+ NETDATA_DOUBLE sum;
486
+ size_t count;
487
+ STORAGE_POINT hsp;
488
+ STORAGE_POINT bsp;
489
+};
490
316
- buffer_json_add_array_item_uint64(wb, type); // "type"
317
- buffer_json_add_array_item_int64(wb, ni);
318
- if(type != WPT_NODE) {
319
- buffer_json_add_array_item_int64(wb, ci);
320
- if(type != WPT_CONTEXT) {
321
- buffer_json_add_array_item_int64(wb, ii);
322
- if(type != WPT_INSTANCE)
323
- buffer_json_add_array_item_int64(wb, di);
324
- else
491
+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, struct aggregated_weight *aw, RRDR_OPTIONS options __maybe_unused, bool baseline) {
492
+ if(type != WPT_GROUP) {
493
+ buffer_json_add_array_item_array(wb);
494
+ buffer_json_add_array_item_uint64(wb, type); // "type"
495
+ buffer_json_add_array_item_int64(wb, ni);
496
+ if (type != WPT_NODE) {
497
+ buffer_json_add_array_item_int64(wb, ci);
498
+ if (type != WPT_CONTEXT) {
499
+ buffer_json_add_array_item_int64(wb, ii);
500
+ if (type != WPT_INSTANCE)
501
+ buffer_json_add_array_item_int64(wb, di);
502
+ else
503
+ buffer_json_add_array_item_string(wb, NULL);
504
+ }
505
+ else {
506
+ buffer_json_add_array_item_string(wb, NULL);
507
buffer_json_add_array_item_string(wb, NULL);
326
- } else {
508
+ }
509
+ }
510
+ else {
511
+ buffer_json_add_array_item_string(wb, NULL);
512
buffer_json_add_array_item_string(wb, NULL);
513
buffer_json_add_array_item_string(wb, NULL);
514
}
515
+ buffer_json_add_array_item_double(wb, (aw->count) ? aw->sum / (NETDATA_DOUBLE)aw->count : 0.0); // "weight"
516
}
517
else {
332
- buffer_json_add_array_item_string(wb, NULL);
333
- buffer_json_add_array_item_string(wb, NULL);
334
- buffer_json_add_array_item_string(wb, NULL);
518
+ buffer_json_member_add_array(wb, "v");
519
+ buffer_json_add_array_item_array(wb);
520
+ buffer_json_add_array_item_double(wb, aw->min); // "min"
521
+ buffer_json_add_array_item_double(wb, (aw->count) ? aw->sum / (NETDATA_DOUBLE)aw->count : 0.0); // "avg"
522
+ buffer_json_add_array_item_double(wb, aw->max); // "max"
523
+ buffer_json_add_array_item_double(wb, aw->sum); // "sum"
524
+ buffer_json_add_array_item_uint64(wb, aw->count); // "count"
525
+ buffer_json_array_close(wb);
526
}
336
- buffer_json_add_array_item_double(wb, weight); // "weight"
527
528
buffer_json_add_array_item_array(wb);
339
- buffer_json_add_array_item_double(wb, highlighted_sp->min); // "min"
340
- buffer_json_add_array_item_double(wb, (highlighted_sp->count) ? highlighted_sp->sum / (NETDATA_DOUBLE) highlighted_sp->count : 0.0); // "avg"
341
- buffer_json_add_array_item_double(wb, highlighted_sp->max); // "max"
342
- buffer_json_add_array_item_double(wb, highlighted_sp->sum); // "sum"
343
- buffer_json_add_array_item_uint64(wb, highlighted_sp->count); // "count"
344
- buffer_json_add_array_item_uint64(wb, highlighted_sp->anomaly_count); // "anomaly_count"
529
+ buffer_json_add_array_item_double(wb, aw->hsp.min); // "min"
530
+ buffer_json_add_array_item_double(wb, (aw->hsp.count) ? aw->hsp.sum / (NETDATA_DOUBLE) aw->hsp.count : 0.0); // "avg"
531
+ buffer_json_add_array_item_double(wb, aw->hsp.max); // "max"
532
+ buffer_json_add_array_item_double(wb, aw->hsp.sum); // "sum"
533
+ buffer_json_add_array_item_uint64(wb, aw->hsp.count); // "count"
534
+ buffer_json_add_array_item_uint64(wb, aw->hsp.anomaly_count); // "anomaly_count"
535
buffer_json_array_close(wb);
536
537
if(baseline) {
538
buffer_json_add_array_item_array(wb);
349
- buffer_json_add_array_item_double(wb, baseline_sp->min); // "min"
350
- buffer_json_add_array_item_double(wb, (baseline_sp->count) ? baseline_sp->sum / (NETDATA_DOUBLE) baseline_sp->count : 0.0); // "avg"
351
- buffer_json_add_array_item_double(wb, baseline_sp->max); // "max"
352
- buffer_json_add_array_item_double(wb, baseline_sp->sum); // "sum"
353
- buffer_json_add_array_item_uint64(wb, baseline_sp->count); // "count"
354
- buffer_json_add_array_item_uint64(wb, baseline_sp->anomaly_count); // "anomaly_count"
539
+ buffer_json_add_array_item_double(wb, aw->bsp.min); // "min"
540
+ buffer_json_add_array_item_double(wb, (aw->bsp.count) ? aw->bsp.sum / (NETDATA_DOUBLE) aw->bsp.count : 0.0); // "avg"
541
+ buffer_json_add_array_item_double(wb, aw->bsp.max); // "max"
542
+ buffer_json_add_array_item_double(wb, aw->bsp.sum); // "sum"
543
+ buffer_json_add_array_item_uint64(wb, aw->bsp.count); // "count"
544
+ buffer_json_add_array_item_uint64(wb, aw->bsp.anomaly_count); // "anomaly_count"
545
buffer_json_array_close(wb);
546
}
547
358
- buffer_json_array_close(wb); // key
548
+ buffer_json_array_close(wb);
549
}
550
361
-static void multinode_data_schema(BUFFER *wb, RRDR_OPTIONS options __maybe_unused, const char *key, bool baseline) {
551
+static void multinode_data_schema(BUFFER *wb, RRDR_OPTIONS options __maybe_unused, const char *key, bool baseline, bool group_by) {
552
buffer_json_member_add_object(wb, key); // schema
553
554
buffer_json_member_add_string(wb, "type", "array");
555
buffer_json_member_add_array(wb, "items");
556
367
- buffer_json_add_array_item_object(wb);
368
- buffer_json_member_add_string(wb, "name", "row_type");
369
- buffer_json_member_add_string(wb, "type", "integer");
370
- buffer_json_member_add_array(wb, "value");
371
- buffer_json_add_array_item_string(wb, "dimension");
372
- buffer_json_add_array_item_string(wb, "instance");
373
- buffer_json_add_array_item_string(wb, "context");
374
- buffer_json_add_array_item_string(wb, "node");
375
- buffer_json_array_close(wb);
376
- buffer_json_object_close(wb);
377
-
378
- buffer_json_add_array_item_object(wb);
379
- {
380
- buffer_json_member_add_string(wb, "name", "ni");
381
- buffer_json_member_add_string(wb, "type", "integer");
382
- buffer_json_member_add_string(wb, "dictionary", "nodes");
557
+ if(group_by) {
558
+ buffer_json_add_array_item_object(wb);
559
+ {
560
+ buffer_json_member_add_string(wb, "name", "weight");
561
+ buffer_json_member_add_string(wb, "type", "array");
562
+ buffer_json_member_add_array(wb, "labels");
563
+ {
564
+ buffer_json_add_array_item_string(wb, "min");
565
+ buffer_json_add_array_item_string(wb, "avg");
566
+ buffer_json_add_array_item_string(wb, "max");
567
+ buffer_json_add_array_item_string(wb, "sum");
568
+ buffer_json_add_array_item_string(wb, "count");
569
+ }
570
+ buffer_json_array_close(wb);
571
+ }
572
+ buffer_json_object_close(wb);
573
}
384
- buffer_json_object_close(wb);
385
-
386
- buffer_json_add_array_item_object(wb);
387
- {
388
- buffer_json_member_add_string(wb, "name", "ci");
574
+ else {
575
+ buffer_json_add_array_item_object(wb);
576
+ buffer_json_member_add_string(wb, "name", "row_type");
577
buffer_json_member_add_string(wb, "type", "integer");
390
- buffer_json_member_add_string(wb, "dictionary", "contexts");
391
- }
392
- buffer_json_object_close(wb);
578
+ buffer_json_member_add_array(wb, "value");
579
+ buffer_json_add_array_item_string(wb, "dimension");
580
+ buffer_json_add_array_item_string(wb, "instance");
581
+ buffer_json_add_array_item_string(wb, "context");
582
+ buffer_json_add_array_item_string(wb, "node");
583
+ buffer_json_array_close(wb);
584
+ buffer_json_object_close(wb);
585
394
- buffer_json_add_array_item_object(wb);
395
- {
396
- buffer_json_member_add_string(wb, "name", "ii");
397
- buffer_json_member_add_string(wb, "type", "integer");
398
- buffer_json_member_add_string(wb, "dictionary", "instances");
399
- }
400
- buffer_json_object_close(wb);
586
+ buffer_json_add_array_item_object(wb);
587
+ {
588
+ buffer_json_member_add_string(wb, "name", "ni");
589
+ buffer_json_member_add_string(wb, "type", "integer");
590
+ buffer_json_member_add_string(wb, "dictionary", "nodes");
591
+ }
592
+ buffer_json_object_close(wb);
593
402
- buffer_json_add_array_item_object(wb);
403
- {
404
- buffer_json_member_add_string(wb, "name", "di");
405
- buffer_json_member_add_string(wb, "type", "integer");
406
- buffer_json_member_add_string(wb, "dictionary", "dimensions");
407
- }
408
- buffer_json_object_close(wb);
594
+ buffer_json_add_array_item_object(wb);
595
+ {
596
+ buffer_json_member_add_string(wb, "name", "ci");
597
+ buffer_json_member_add_string(wb, "type", "integer");
598
+ buffer_json_member_add_string(wb, "dictionary", "contexts");
599
+ }
600
+ buffer_json_object_close(wb);
601
410
- buffer_json_add_array_item_object(wb);
411
- {
412
- buffer_json_member_add_string(wb, "name", "weight");
413
- buffer_json_member_add_string(wb, "type", "number");
602
+ buffer_json_add_array_item_object(wb);
603
+ {
604
+ buffer_json_member_add_string(wb, "name", "ii");
605
+ buffer_json_member_add_string(wb, "type", "integer");
606
+ buffer_json_member_add_string(wb, "dictionary", "instances");
607
+ }
608
+ buffer_json_object_close(wb);
609
+
610
+ buffer_json_add_array_item_object(wb);
611
+ {
612
+ buffer_json_member_add_string(wb, "name", "di");
613
+ buffer_json_member_add_string(wb, "type", "integer");
614
+ buffer_json_member_add_string(wb, "dictionary", "dimensions");
615
+ }
616
+ buffer_json_object_close(wb);
617
+
618
+ buffer_json_add_array_item_object(wb);
619
+ {
620
+ buffer_json_member_add_string(wb, "name", "weight");
621
+ buffer_json_member_add_string(wb, "type", "number");
622
+ }
623
+ buffer_json_object_close(wb);
624
}
415
- buffer_json_object_close(wb);
625
626
buffer_json_add_array_item_object(wb);
627
{
@@ -518,53 +727,26 @@ static inline ssize_t dict_unique_id_name_add(DICTIONARY *dict, const char *id,
727
728
return (ssize_t)dun->i;
729
}
521
-struct query_weights_data {
522
- QUERY_WEIGHTS_REQUEST *qwr;
523
-
524
- SIMPLE_PATTERN *scope_nodes_sp;
525
- SIMPLE_PATTERN *scope_contexts_sp;
526
- SIMPLE_PATTERN *nodes_sp;
527
- SIMPLE_PATTERN *contexts_sp;
528
- SIMPLE_PATTERN *instances_sp;
529
- SIMPLE_PATTERN *dimensions_sp;
530
- SIMPLE_PATTERN *labels_sp;
531
- SIMPLE_PATTERN *alerts_sp;
532
-
533
- usec_t timeout_us;
534
- bool timed_out;
535
- bool interrupted;
536
-
537
- struct query_timings timings;
538
-
539
- size_t examined_dimensions;
540
- bool register_zero;
541
-
542
- DICTIONARY *results;
543
- WEIGHTS_STATS stats;
544
-
545
- uint32_t shifts;
546
-
547
- struct query_versions versions;
548
-};
549
-
550
-static size_t registered_results_to_json_multinode(DICTIONARY *results, BUFFER *wb,
551
- time_t after, time_t before,
552
- time_t baseline_after, time_t baseline_before,
553
- size_t points, WEIGHTS_METHOD method,
554
- RRDR_TIME_GROUPING group, RRDR_OPTIONS options, uint32_t shifts,
555
- size_t examined_dimensions, struct query_weights_data *qwd,
556
- WEIGHTS_STATS *stats,
557
- struct query_versions *versions) {
730
+static size_t registered_results_to_json_multinode_no_group_by(
731
+ DICTIONARY *results, BUFFER *wb,
732
+ time_t after, time_t before,
733
+ time_t baseline_after, time_t baseline_before,
734
+ size_t points, WEIGHTS_METHOD method,
735
+ RRDR_TIME_GROUPING group, RRDR_OPTIONS options, uint32_t shifts,
736
+ size_t examined_dimensions, struct query_weights_data *qwd,
737
+ WEIGHTS_STATS *stats,
738
+ struct query_versions *versions) {
739
buffer_json_initialize(wb, "\"", "\"", 0, true, options & RRDR_OPTION_MINIFY);
740
buffer_json_member_add_uint64(wb, "api", 2);
741
561
- results_header_to_json(results, wb, after, before, baseline_after, baseline_before,
562
- points, method, group, options, shifts, examined_dimensions, qwd->timings.executed_ut - qwd->timings.received_ut, stats);
742
+ results_header_to_json_v2(results, wb, qwd, after, before, baseline_after, baseline_before,
743
+ points, method, group, options, shifts, examined_dimensions,
744
+ qwd->timings.executed_ut - qwd->timings.received_ut, stats, false);
745
746
version_hashes_api_v2(wb, versions);
747
748
bool baseline = method == WEIGHTS_METHOD_MC_KS2 || method == WEIGHTS_METHOD_MC_VOLUME;
567
- multinode_data_schema(wb, options, "schema", baseline);
749
+ multinode_data_schema(wb, options, "schema", baseline, false);
750
751
DICTIONARY *dict_nodes = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct dict_unique_node));
752
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_units));
@@ -573,10 +755,7 @@ static size_t registered_results_to_json_multinode(DICTIONARY *results, BUFFER *
755
756
buffer_json_member_add_array(wb, "result");
757
576
- size_t total_dimensions = 0, node_dims = 0, context_dims = 0, instance_dims = 0;
577
- NETDATA_DOUBLE context_total_weight = 0.0, instance_total_weight = 0.0, node_total_weight = 0.0;
578
- STORAGE_POINT context_hsp = STORAGE_POINT_UNSET, instance_hsp = STORAGE_POINT_UNSET, node_hsp = STORAGE_POINT_UNSET;
579
- STORAGE_POINT context_bsp = STORAGE_POINT_UNSET, instance_bsp = STORAGE_POINT_UNSET, node_bsp = STORAGE_POINT_UNSET;
758
+ struct aggregated_weight node_aw = AGGREGATED_WEIGHT_EMPTY, context_aw = AGGREGATED_WEIGHT_EMPTY, instance_aw = AGGREGATED_WEIGHT_EMPTY;
759
struct register_result *t;
760
RRDHOST *last_host = NULL;
761
RRDCONTEXT_ACQUIRED *last_rca = NULL;
@@ -584,34 +763,28 @@ static size_t registered_results_to_json_multinode(DICTIONARY *results, BUFFER *
763
struct dict_unique_node *node_dun = NULL;
764
ssize_t di = -1, ii = -1, ci = -1, ni = -1;
765
ssize_t di_max = 0, ii_max = 0, ci_max = 0, ni_max = 0;
766
+ size_t total_dimensions = 0;
767
dfe_start_read(results, t) {
768
769
// close instance
770
if(t->ria != last_ria && last_ria) {
591
- storage_point_to_json(wb, WPT_INSTANCE, di, ii, ci, ni, instance_total_weight / (double) instance_dims, &instance_hsp, &instance_bsp, options, baseline);
592
-
771
+ storage_point_to_json(wb, WPT_INSTANCE, di, ii, ci, ni, &instance_aw, options, baseline);
772
last_ria = NULL;
594
- instance_dims = 0;
595
- instance_total_weight = 0.0;
596
- instance_hsp = instance_bsp = STORAGE_POINT_UNSET;
773
+ instance_aw = AGGREGATED_WEIGHT_EMPTY;
774
}
775
776
// close context
777
if(t->rca != last_rca && last_rca) {
601
- storage_point_to_json(wb, WPT_CONTEXT, di, ii, ci, ni, context_total_weight / (double) context_dims, &context_hsp, &instance_bsp, options, baseline);
778
+ storage_point_to_json(wb, WPT_CONTEXT, di, ii, ci, ni, &context_aw, options, baseline);
779
last_rca = NULL;
603
- context_dims = 0;
604
- context_total_weight = 0.0;
605
- context_hsp = context_bsp = STORAGE_POINT_UNSET;
780
+ context_aw = AGGREGATED_WEIGHT_EMPTY;
781
}
782
783
// close node
784
if(t->host != last_host && last_host) {
610
- storage_point_to_json(wb, WPT_NODE, di, ii, ci, ni, node_total_weight / (double) node_dims, &node_hsp, &node_bsp, options, baseline);
785
+ storage_point_to_json(wb, WPT_NODE, di, ii, ci, ni, &node_aw, options, baseline);
786
last_host = NULL;
612
- node_dims = 0;
613
- node_total_weight = 0.0;
614
- node_hsp = node_bsp = STORAGE_POINT_UNSET;
787
+ node_aw = AGGREGATED_WEIGHT_EMPTY;
788
}
789
790
// open node
@@ -635,42 +808,37 @@ static size_t registered_results_to_json_multinode(DICTIONARY *results, BUFFER *
808
}
809
810
di = dict_unique_id_name_add(dict_dimensions, rrdmetric_acquired_id(t->rma), rrdmetric_acquired_name(t->rma), &di_max);
638
- storage_point_to_json(wb, WPT_DIMENSION, di, ii, ci, ni, t->value, &t->highlighted, &t->baseline, options, baseline);
811
640
- instance_total_weight += t->value;
641
- context_total_weight += t->value;
642
- node_total_weight += t->value;
812
+ struct aggregated_weight aw = {
813
+ .min = t->value,
814
+ .max = t->value,
815
+ .sum = t->value,
816
+ .count = 1,
817
+ .hsp = t->highlighted,
818
+ .bsp = t->baseline,
819
+ };
820
+ storage_point_to_json(wb, WPT_DIMENSION, di, ii, ci, ni, &aw, options, baseline);
821
644
- node_dun->duration_ut += t->duration_ut;
645
-
646
- storage_point_merge_to(instance_hsp, t->highlighted);
647
- storage_point_merge_to(context_hsp, t->highlighted);
648
- storage_point_merge_to(node_hsp, t->highlighted);
822
+ merge_into_aw(instance_aw, t);
823
+ merge_into_aw(context_aw, t);
824
+ merge_into_aw(node_aw, t);
825
650
- if(baseline) {
651
- storage_point_merge_to(instance_bsp, t->baseline);
652
- storage_point_merge_to(context_bsp, t->baseline);
653
- storage_point_merge_to(node_bsp, t->baseline);
654
- }
655
-
656
- instance_dims++;
657
- context_dims++;
658
- node_dims++;
826
+ node_dun->duration_ut += t->duration_ut;
827
total_dimensions++;
828
}
829
dfe_done(t);
830
831
// close instance
832
if(last_ria)
665
- storage_point_to_json(wb, WPT_INSTANCE, di, ii, ci, ni, instance_total_weight / (double) instance_dims, &instance_hsp, &instance_bsp, options, baseline);
833
+ storage_point_to_json(wb, WPT_INSTANCE, di, ii, ci, ni, &instance_aw, options, baseline);
834
835
// close context
836
if(last_rca)
669
- storage_point_to_json(wb, WPT_CONTEXT, di, ii, ci, ni, context_total_weight / (double) context_dims, &context_hsp, &instance_bsp, options, baseline);
837
+ storage_point_to_json(wb, WPT_CONTEXT, di, ii, ci, ni, &context_aw, options, baseline);
838
839
// close node
840
if(last_host)
673
- storage_point_to_json(wb, WPT_NODE, di, ii, ci, ni, node_total_weight / (double) node_dims, &node_hsp, &node_bsp, options, baseline);
841
+ storage_point_to_json(wb, WPT_NODE, di, ii, ci, ni, &node_aw, options, baseline);
842
843
buffer_json_array_close(wb); // points
844
@@ -746,6 +914,131 @@ static size_t registered_results_to_json_multinode(DICTIONARY *results, BUFFER *
914
return total_dimensions;
915
}
916
917
+static size_t registered_results_to_json_multinode_group_by(
918
+ DICTIONARY *results, BUFFER *wb,
919
+ time_t after, time_t before,
920
+ time_t baseline_after, time_t baseline_before,
921
+ size_t points, WEIGHTS_METHOD method,
922
+ RRDR_TIME_GROUPING group, RRDR_OPTIONS options, uint32_t shifts,
923
+ size_t examined_dimensions, struct query_weights_data *qwd,
924
+ WEIGHTS_STATS *stats,
925
+ struct query_versions *versions) {
926
+ buffer_json_initialize(wb, "\"", "\"", 0, true, options & RRDR_OPTION_MINIFY);
927
+ buffer_json_member_add_uint64(wb, "api", 2);
928
+
929
+ results_header_to_json_v2(results, wb, qwd, after, before, baseline_after, baseline_before,
930
+ points, method, group, options, shifts, examined_dimensions,
931
+ qwd->timings.executed_ut - qwd->timings.received_ut, stats, true);
932
+
933
+ version_hashes_api_v2(wb, versions);
934
+
935
+ bool baseline = method == WEIGHTS_METHOD_MC_KS2 || method == WEIGHTS_METHOD_MC_VOLUME;
936
+ multinode_data_schema(wb, options, "v_schema", baseline, true);
937
+
938
+ DICTIONARY *group_by = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
939
+ NULL, sizeof(struct aggregated_weight));
940
+
941
+ struct register_result *t;
942
+ size_t total_dimensions = 0;
943
+ BUFFER *key = buffer_create(0, NULL);
944
+ BUFFER *name = buffer_create(0, NULL);
945
+ dfe_start_read(results, t) {
946
+
947
+ buffer_flush(key);
948
+ buffer_flush(name);
949
+
950
+ if(qwd->qwr->group_by.group_by & RRDR_GROUP_BY_DIMENSION) {
951
+ buffer_strcat(key, rrdmetric_acquired_name(t->rma));
952
+ buffer_strcat(name, rrdmetric_acquired_name(t->rma));
953
+ }
954
+ if(qwd->qwr->group_by.group_by & RRDR_GROUP_BY_INSTANCE) {
955
+ if(buffer_strlen(key)) {
956
+ buffer_fast_strcat(key, ",", 1);
957
+ buffer_fast_strcat(name, ",", 1);
958
+ }
959
+
960
+ buffer_strcat(key, rrdinstance_acquired_id(t->ria));
961
+ buffer_strcat(name, rrdinstance_acquired_name(t->ria));
962
+
963
+ if(!(qwd->qwr->group_by.group_by & RRDR_GROUP_BY_NODE)) {
964
+ buffer_fast_strcat(key, "@", 1);
965
+ buffer_fast_strcat(name, "@", 1);
966
+ buffer_strcat(key, t->host->machine_guid);
967
+ buffer_strcat(name, rrdhost_hostname(t->host));
968
+ }
969
+ }
970
+ if(qwd->qwr->group_by.group_by & RRDR_GROUP_BY_NODE) {
971
+ if(buffer_strlen(key)) {
972
+ buffer_fast_strcat(key, ",", 1);
973
+ buffer_fast_strcat(name, ",", 1);
974
+ }
975
+
976
+ buffer_strcat(key, t->host->machine_guid);
977
+ buffer_strcat(name, rrdhost_hostname(t->host));
978
+ }
979
+ if(qwd->qwr->group_by.group_by & RRDR_GROUP_BY_CONTEXT) {
980
+ if(buffer_strlen(key)) {
981
+ buffer_fast_strcat(key, ",", 1);
982
+ buffer_fast_strcat(name, ",", 1);
983
+ }
984
+
985
+ buffer_strcat(key, rrdcontext_acquired_id(t->rca));
986
+ buffer_strcat(name, rrdcontext_acquired_id(t->rca));
987
+ }
988
+ if(qwd->qwr->group_by.group_by & RRDR_GROUP_BY_UNITS) {
989
+ if(buffer_strlen(key)) {
990
+ buffer_fast_strcat(key, ",", 1);
991
+ buffer_fast_strcat(name, ",", 1);
992
+ }
993
+
994
+ buffer_strcat(key, rrdcontext_acquired_units(t->rca));
995
+ buffer_strcat(name, rrdcontext_acquired_units(t->rca));
996
+ }
997
+
998
+ struct aggregated_weight *aw = dictionary_set(group_by, buffer_tostring(key), NULL, sizeof(struct aggregated_weight));
999
+ if(!aw->name) {
1000
+ aw->name = strdupz(buffer_tostring(name));
1001
+ aw->min = aw->max = aw->sum = t->value;
1002
+ aw->count = 1;
1003
+ aw->hsp = t->highlighted;
1004
+ aw->bsp = t->baseline;
1005
+ }
1006
+ else
1007
+ merge_into_aw(*aw, t);
1008
+
1009
+ total_dimensions++;
1010
+ }
1011
+ dfe_done(t);
1012
+ buffer_free(key); key = NULL;
1013
+ buffer_free(name); name = NULL;
1014
+
1015
+ struct aggregated_weight *aw;
1016
+ buffer_json_member_add_array(wb, "result");
1017
+ dfe_start_read(group_by, aw) {
1018
+ const char *k = aw_dfe.name;
1019
+ const char *n = aw->name;
1020
+
1021
+ buffer_json_add_array_item_object(wb);
1022
+ buffer_json_member_add_string(wb, "id", k);
1023
+ buffer_json_member_add_string(wb, "nm", n);
1024
+ storage_point_to_json(wb, WPT_GROUP, 0, 0, 0, 0, aw, options, baseline);
1025
+ buffer_json_object_close(wb);
1026
+
1027
+ freez((void *)aw->name);
1028
+ }
1029
+ dfe_done(aw);
1030
+ buffer_json_array_close(wb); // result
1031
+
1032
+ buffer_json_agents_array_v2(wb, &qwd->timings, 0);
1033
+ buffer_json_member_add_uint64(wb, "correlated_dimensions", total_dimensions);
1034
+ buffer_json_member_add_uint64(wb, "total_dimensions_count", examined_dimensions);
1035
+ buffer_json_finalize(wb);
1036
+
1037
+ dictionary_destroy(group_by);
1038
+
1039
+ return total_dimensions;
1040
+}
1041
+
1042
// ----------------------------------------------------------------------------
1043
// KS2 algorithm functions
1044
@@ -1616,14 +1909,28 @@ int web_api_v12_weights(BUFFER *wb, QUERY_WEIGHTS_REQUEST *qwr) {
1909
1910
default:
1911
case WEIGHTS_FORMAT_MULTINODE:
1619
- added_dimensions =
1620
- registered_results_to_json_multinode(
1621
- qwd.results, wb,
1622
- qwr->after, qwr->before,
1623
- qwr->baseline_after, qwr->baseline_before,
1624
- qwr->points, qwr->method, qwr->time_group_method, qwr->options, qwd.shifts,
1625
- qwd.examined_dimensions,
1626
- &qwd, &qwd.stats, &qwd.versions);
1912
+ // we don't support these groupings in weights
1913
+ qwr->group_by.group_by &= ~(RRDR_GROUP_BY_LABEL|RRDR_GROUP_BY_SELECTED|RRDR_GROUP_BY_PERCENTAGE_OF_INSTANCE);
1914
+ if(qwr->group_by.group_by == RRDR_GROUP_BY_NONE) {
1915
+ added_dimensions =
1916
+ registered_results_to_json_multinode_no_group_by(
1917
+ qwd.results, wb,
1918
+ qwr->after, qwr->before,
1919
+ qwr->baseline_after, qwr->baseline_before,
1920
+ qwr->points, qwr->method, qwr->time_group_method, qwr->options, qwd.shifts,
1921
+ qwd.examined_dimensions,
1922
+ &qwd, &qwd.stats, &qwd.versions);
1923
+ }
1924
+ else {
1925
+ added_dimensions =
1926
+ registered_results_to_json_multinode_group_by(
1927
+ qwd.results, wb,
1928
+ qwr->after, qwr->before,
1929
+ qwr->baseline_after, qwr->baseline_before,
1930
+ qwr->points, qwr->method, qwr->time_group_method, qwr->options, qwd.shifts,
1931
+ qwd.examined_dimensions,
1932
+ &qwd, &qwd.stats, &qwd.versions);
1933
+ }
1934
break;
1935
}
1936
web/api/queries/weights.h
+7
@@ -35,6 +35,13 @@ typedef struct query_weights_request {
35
const char *dimensions;
36
const char *labels;
37
const char *alerts;
38
+
39
+ struct {
40
+ RRDR_GROUP_BY group_by;
41
+ char *group_by_label;
42
+ RRDR_GROUP_BY_FUNCTION aggregation;
43
+ } group_by;
44
+
45
WEIGHTS_METHOD method;
46
WEIGHTS_FORMAT format;
47
RRDR_TIME_GROUPING time_group_method;
web/api/web_api.c
+20
@@ -71,6 +71,12 @@ int web_client_api_request_weights(RRDHOST *host, struct web_client *w, char *ur
71
const char *time_group_options = NULL, *scope_contexts = NULL, *scope_nodes = NULL, *contexts = NULL, *nodes = NULL,
72
*instances = NULL, *dimensions = NULL, *labels = NULL, *alerts = NULL;
73
74
+ struct group_by_pass group_by = {
75
+ .group_by = RRDR_GROUP_BY_NONE,
76
+ .group_by_label = NULL,
77
+ .aggregation = RRDR_GROUP_BY_FUNCTION_AVERAGE,
78
+ };
79
+
80
while (url) {
81
char *value = mystrsep(&url, "&");
82
if (!value || !*value)
@@ -123,6 +129,15 @@ int web_client_api_request_weights(RRDHOST *host, struct web_client *w, char *ur
129
else if(api_version >= 2 && !strcmp(name, "dimensions")) dimensions = value;
130
else if(api_version >= 2 && !strcmp(name, "labels")) labels = value;
131
else if(api_version >= 2 && !strcmp(name, "alerts")) alerts = value;
132
+ else if(api_version >= 2 && (!strcmp(name, "group_by") || !strcmp(name, "group_by[0]"))) {
133
+ group_by.group_by = group_by_parse(value);
134
+ }
135
+ else if(api_version >= 2 && (!strcmp(name, "group_by_label") || !strcmp(name, "group_by_label[0]"))) {
136
+ group_by.group_by_label = value;
137
+ }
138
+ else if(api_version >= 2 && (!strcmp(name, "aggregation") || !strcmp(name, "aggregation[0]"))) {
139
+ group_by.aggregation = group_by_aggregate_function_parse(value);
140
+ }
141
142
else if(!strcmp(name, "tier")) {
143
tier = str2ul(value);
@@ -161,6 +176,11 @@ int web_client_api_request_weights(RRDHOST *host, struct web_client *w, char *ur
176
.dimensions = dimensions,
177
.labels = labels,
178
.alerts = alerts,
179
+ .group_by = {
180
+ .group_by = group_by.group_by,
181
+ .group_by_label = group_by.group_by_label,
182
+ .aggregation = group_by.aggregation,
183
+ },
184
.method = method,
185
.format = format,
186
.time_group_method = time_group_method,