master
c 191 lines 8.42 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "jsonwrap.h"
4 #include "jsonwrap-internal.h"
5
6 void query_target_summary_instances_v1(BUFFER *wb, QUERY_TARGET *qt, const char *key) {
7 char name[RRD_ID_LENGTH_MAX * 2 + 2];
8
9 buffer_json_member_add_array(wb, key);
10 DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE);
11 for (long c = 0; c < (long) qt->instances.used; c++) {
12 QUERY_INSTANCE *qi = query_instance(qt, c);
13
14 snprintfz(name, RRD_ID_LENGTH_MAX * 2 + 1, "%s:%s",
15 rrdinstance_acquired_id(qi->ria),
16 rrdinstance_acquired_name(qi->ria));
17
18 bool *set = dictionary_set(dict, name, NULL, sizeof(*set));
19 if (!*set) {
20 *set = true;
21 buffer_json_add_array_item_array(wb);
22 buffer_json_add_array_item_string(wb, rrdinstance_acquired_id(qi->ria));
23 buffer_json_add_array_item_string(wb, rrdinstance_acquired_name(qi->ria));
24 buffer_json_array_close(wb);
25 }
26 }
27 dictionary_destroy(dict);
28 buffer_json_array_close(wb);
29 }
30
31 // Helper structure for sorting and limiting instances based on their contribution
32 typedef struct {
33 size_t index; // Original index in the array
34 NETDATA_DOUBLE contribution; // Sorting metric (usually volume contribution)
35 const char *id; // Instance ID
36 const char *name; // Instance name
37 } INSTANCE_CARDINALITY_ITEM;
38
39 // Comparison function for sorting instances by contribution
40 static int instance_cardinality_item_compare(const void *a, const void *b) {
41 const INSTANCE_CARDINALITY_ITEM *item_a = (const INSTANCE_CARDINALITY_ITEM *)a;
42 const INSTANCE_CARDINALITY_ITEM *item_b = (const INSTANCE_CARDINALITY_ITEM *)b;
43
44 // Sort by contribution (highest first)
45 if (item_a->contribution > item_b->contribution) return -1;
46 if (item_a->contribution < item_b->contribution) return 1;
47
48 // If equal contribution, sort alphabetically by id
49 if (item_a->id && item_b->id)
50 return strcmp(item_a->id, item_b->id);
51
52 return 0;
53 }
54
55 void query_target_summary_instances_v2(BUFFER *wb, QUERY_TARGET *qt, const char *key, struct summary_total_counts *totals) {
56 buffer_json_member_add_array(wb, key);
57 long count = (long) qt->instances.used;
58 size_t cardinality_limit = qt->request.cardinality_limit;
59
60 // Check if we need to apply cardinality limiting
61 if (cardinality_limit > 0 && count > (long)cardinality_limit) {
62 // We'll need to sort and limit the instances
63 INSTANCE_CARDINALITY_ITEM *items = mallocz(sizeof(INSTANCE_CARDINALITY_ITEM) * count);
64
65 // Collect contribution data for each instance
66 for (long c = 0; c < count; c++) {
67 QUERY_INSTANCE *qi = query_instance(qt, c);
68 items[c].index = c;
69 items[c].id = rrdinstance_acquired_id(qi->ria);
70 items[c].name = rrdinstance_acquired_name(qi->ria);
71
72 // Use query points as the metric for contribution
73 if (qt->query_points.sum > 0)
74 items[c].contribution = qi->query_points.sum * 100.0 / qt->query_points.sum;
75 else
76 items[c].contribution = 0.0;
77 }
78
79 // Sort by contribution
80 qsort(items, count, sizeof(INSTANCE_CARDINALITY_ITEM), instance_cardinality_item_compare);
81
82 // First add the top (limit-1) instances
83 size_t instances_to_show = cardinality_limit - 1;
84 QUERY_METRICS_COUNTS aggregated_metrics = {0};
85 QUERY_ALERTS_COUNTS aggregated_alerts = {0};
86 STORAGE_POINT aggregated_points = STORAGE_POINT_UNSET;
87 NETDATA_DOUBLE remaining_contribution = 0.0;
88 size_t remaining_count = 0;
89
90 // Output the top instances
91 for (size_t i = 0; (long)i < count; i++) {
92 if (i < instances_to_show) {
93 // Output this instance normally
94 QUERY_INSTANCE *qi = query_instance(qt, items[i].index);
95
96 buffer_json_add_array_item_object(wb);
97 buffer_json_member_add_string(wb, "id", items[i].id);
98
99 if(!rrdinstance_acquired_id_and_name_are_same(qi->ria))
100 buffer_json_member_add_string(wb, JSKEY(name), items[i].name);
101
102 buffer_json_member_add_uint64(wb, JSKEY(node_index), qi->query_host_id);
103
104 if (items[i].contribution > 0.0)
105 buffer_json_member_add_double(wb, JSKEY(contribution), items[i].contribution);
106
107 // Only include detailed statistics if MINIMAL_STATS option is not set
108 if (!(qt->window.options & RRDR_OPTION_MINIMAL_STATS)) {
109 query_target_metric_counts(wb, &qi->metrics);
110 query_target_alerts_counts(wb, &qi->alerts, NULL, false);
111 }
112
113 query_target_points_statistics(wb, qt, &qi->query_points);
114 buffer_json_object_close(wb);
115
116 aggregate_into_summary_totals(totals, &qi->metrics);
117 } else {
118 // Aggregate the remaining instances
119 QUERY_INSTANCE *qi = query_instance(qt, items[i].index);
120 remaining_contribution += items[i].contribution;
121 remaining_count++;
122
123 // Aggregate metrics and alerts counts
124 aggregate_metrics_counts(&aggregated_metrics, &qi->metrics);
125 aggregate_alerts_counts(&aggregated_alerts, &qi->alerts);
126
127 // Aggregate points
128 storage_point_merge_to(aggregated_points, qi->query_points);
129
130 // Still add to summary totals
131 aggregate_into_summary_totals(totals, &qi->metrics);
132 }
133 }
134
135 // Add the aggregated "remaining" entry if there are any
136 if (remaining_count > 0) {
137 char remaining_instance[50];
138 snprintfz(remaining_instance, sizeof(remaining_instance), "remaining %zu instances", remaining_count);
139
140 buffer_json_add_array_item_object(wb);
141 buffer_json_member_add_string(wb, "id", "__remaining_instances__");
142 buffer_json_member_add_string(wb, JSKEY(name), remaining_instance);
143
144 if (remaining_contribution > 0.0)
145 buffer_json_member_add_double(wb, JSKEY(contribution), remaining_contribution);
146
147 // Only include detailed statistics if MINIMAL_STATS option is not set
148 if (!(qt->window.options & RRDR_OPTION_MINIMAL_STATS)) {
149 query_target_metric_counts(wb, &aggregated_metrics);
150 query_target_alerts_counts(wb, &aggregated_alerts, NULL, false);
151 }
152
153 query_target_points_statistics(wb, qt, &aggregated_points);
154 buffer_json_object_close(wb);
155 }
156
157 freez(items);
158 } else {
159 // No limiting needed, output all instances
160 for (long c = 0; c < count; c++) {
161 QUERY_INSTANCE *qi = query_instance(qt, c);
162
163 buffer_json_add_array_item_object(wb);
164 buffer_json_member_add_string(wb, "id", rrdinstance_acquired_id(qi->ria));
165
166 if(!rrdinstance_acquired_id_and_name_are_same(qi->ria))
167 buffer_json_member_add_string(wb, JSKEY(name), rrdinstance_acquired_name(qi->ria));
168
169 buffer_json_member_add_uint64(wb, JSKEY(node_index), qi->query_host_id);
170
171 // Calculate contribution for this instance
172 if (qt->query_points.sum > 0) {
173 NETDATA_DOUBLE contribution = qi->query_points.sum * 100.0 / qt->query_points.sum;
174 if (contribution > 0.0)
175 buffer_json_member_add_double(wb, JSKEY(contribution), contribution);
176 }
177
178 // Only include detailed statistics if MINIMAL_STATS option is not set
179 if (!(qt->window.options & RRDR_OPTION_MINIMAL_STATS)) {
180 query_target_metric_counts(wb, &qi->metrics);
181 query_target_alerts_counts(wb, &qi->alerts, NULL, false);
182 }
183
184 query_target_points_statistics(wb, qt, &qi->query_points);
185 buffer_json_object_close(wb);
186
187 aggregate_into_summary_totals(totals, &qi->metrics);
188 }
189 }
190 buffer_json_array_close(wb);
191 }