summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2023-03-10 12:41:14 +0200
committerGitHub <noreply@github.com>2023-03-10 12:41:14 +0200
commitcf85c3b0e9fcda807a2a5e9c1834792d73725a50 (patch)
tree6835795413623f49b01f06ab9a716725df394262 /web
parent37a06960f90c046f21c125c2b4265713da04f851 (diff)
/api/v2/X improvements part 3 (#14665)
* max web request size to 64KB * fix the request too big message * increase max request reading tries to 100 * support for bigger web requests * add "avg" as a shortcut for "average" to both group by aggregation and time aggregation; discard the last partial points of a query in play mode, up to max update every; group by hidden dimensions too * better implementation for partial data trimming * added group_by=selected to return only one dimension for all selected metrics * fix acceptance of group_by=selected * passing option "raw" disables partial data trimming * remove obsolete option "plan"; use "debug" * fix view.min and view.max calculation - there were 2 bugs: a) min and max were reset for every row and b) min and max were corrupted by GBC and AR printing * per row annotations * added time column to point annotations * disable caching for /api/v2/contexts responses * added api format json2 that returns an array for each points, having all the point values and annotations in them * work on swagger about /api/v2 * prevent infinite loop * cleanup and swagger work * allow negative simple pattern expressions to work as expected * do not lookup in the dictionary empty names * garbage collect dictionaries * make query_target allocate less aggressively; queries fill the remaining points with nulls * reusable query ops to save memory on huge queries * move parts of query plans into query ops to save query target memory * remove storage engine from query metric tiers, to save memory, and recalculate it when it is needed
Diffstat (limited to 'web')
-rw-r--r--web/api/formatters/csv/csv.c12
-rw-r--r--web/api/formatters/json/json.c16
-rw-r--r--web/api/formatters/json_wrapper.c236
-rw-r--r--web/api/formatters/json_wrapper.h2
-rw-r--r--web/api/formatters/rrd2json.c299
-rw-r--r--web/api/formatters/rrd2json.h3
-rw-r--r--web/api/formatters/value/value.c12
-rw-r--r--web/api/netdata-swagger.yaml624
-rw-r--r--web/api/queries/query.c232
-rw-r--r--web/api/queries/query.h2
-rw-r--r--web/api/queries/rrdr.h24
-rw-r--r--web/api/queries/weights.c4
-rw-r--r--web/api/web_api_v1.c7
-rw-r--r--web/api/web_api_v2.c10
-rw-r--r--web/server/web_client.c35
-rw-r--r--web/server/web_client.h24
-rw-r--r--web/server/web_client_cache.c4
17 files changed, 1221 insertions, 325 deletions
diff --git a/web/api/formatters/csv/csv.c b/web/api/formatters/csv/csv.c
index 9da2afec28..0c4580fb99 100644
--- a/web/api/formatters/csv/csv.c
+++ b/web/api/formatters/csv/csv.c
@@ -84,7 +84,6 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const
buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
}
- int set_min_max = 0;
if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
total = 0;
for(c = 0; c < used ;c++) {
@@ -99,7 +98,6 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const
}
// prevent a division by zero
if(total == 0) total = 1;
- set_min_max = 1;
}
// for each dimension
@@ -124,13 +122,13 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const
if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
n = n * 100 / total;
- if(unlikely(set_min_max)) {
+ if(unlikely(i == start && c == 0)) {
r->view.min = r->view.max = n;
- set_min_max = 0;
}
-
- if(n < r->view.min) r->view.min = n;
- if(n > r->view.max) r->view.max = n;
+ else {
+ if (n < r->view.min) r->view.min = n;
+ if (n > r->view.max) r->view.max = n;
+ }
}
buffer_print_netdata_double(wb, n);
diff --git a/web/api/formatters/json/json.c b/web/api/formatters/json/json.c
index 016d334e2c..832344ec05 100644
--- a/web/api/formatters/json/json.c
+++ b/web/api/formatters/json/json.c
@@ -211,8 +211,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
buffer_fast_strcat(wb, post_date, post_date_len);
}
- int set_min_max = 0;
- if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
+ if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) {
total = 0;
for(c = 0; c < used ;c++) {
if(unlikely(!(r->od[c] & RRDR_DIMENSION_QUERIED))) continue;
@@ -232,7 +231,6 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
}
// prevent a division by zero
if(total == 0) total = 1;
- set_min_max = 1;
}
// for each dimension
@@ -263,16 +261,16 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
n = -n;
- if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
+ if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) {
n = n * 100 / total;
- if(unlikely(set_min_max)) {
+ if(unlikely(i == start && c == 0)) {
r->view.min = r->view.max = n;
- set_min_max = 0;
}
-
- if(n < r->view.min) r->view.min = n;
- if(n > r->view.max) r->view.max = n;
+ else {
+ if (n < r->view.min) r->view.min = n;
+ if (n > r->view.max) r->view.max = n;
+ }
}
buffer_print_netdata_double(wb, n);
diff --git a/web/api/formatters/json_wrapper.c b/web/api/formatters/json_wrapper.c
index 52eef16019..5e1edc67d0 100644
--- a/web/api/formatters/json_wrapper.c
+++ b/web/api/formatters/json_wrapper.c
@@ -213,7 +213,7 @@ static inline void query_target_data_statistics(BUFFER *wb, QUERY_TARGET *qt, st
return;
buffer_json_member_add_object(wb, "sts");
- if(qt->request.group_by_aggregate_function == RRDR_GROUP_BY_FUNCTION_SUM_COUNT) {
+ if(query_target_aggregatable(qt)) {
buffer_json_member_add_uint64(wb, "cnt", d->group_points);
if(d->sum != 0.0)
@@ -801,7 +801,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR
buffer_json_add_array_item_uint64(wb, qt->db.tiers[tier].points);
buffer_json_array_close(wb);
- if(options & RRDR_OPTION_SHOW_PLAN)
+ if(options & RRDR_OPTION_DEBUG)
jsonwrap_query_plan(r, wb);
}
@@ -1061,7 +1061,7 @@ static void query_target_detailed_objects_tree(BUFFER *wb, RRDR *r, RRDR_OPTIONS
query_target_data_statistics(wb, qt, &qm->query_stats);
- if(options & RRDR_OPTION_SHOW_PLAN)
+ if(options & RRDR_OPTION_DEBUG)
jsonwrap_query_metric_plan(wb, qm);
}
}
@@ -1248,6 +1248,13 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
buffer_json_member_add_time_t(wb, "update_every", r->view.update_every);
buffer_json_member_add_time_t(wb, "after", r->view.after);
buffer_json_member_add_time_t(wb, "before", r->view.before);
+
+ buffer_json_member_add_object(wb, "partial_data_trimming");
+ buffer_json_member_add_time_t(wb, "max_update_every", r->partial_data_trimming.max_update_every);
+ buffer_json_member_add_time_t(wb, "expected_after", r->partial_data_trimming.expected_after);
+ buffer_json_member_add_time_t(wb, "trimmed_after", r->partial_data_trimming.trimmed_after);
+ buffer_json_object_close(wb);
+
buffer_json_member_add_uint64(wb, "points", rows);
query_target_combined_units_v2(wb, qt, contexts);
query_target_combined_chart_type(wb, qt, contexts);
@@ -1266,77 +1273,202 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
buffer_json_object_close(wb);
}
-static void annotations_for_value_flags(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format __maybe_unused, RRDR_OPTIONS options, RRDR_VALUE_FLAGS flags, const char *type) {
- const size_t dims = r->d, rows = r->rows;
- size_t next_d_idx = 0;
- for(size_t d = 0; d < dims ; d++) {
+//static void annotations_range_for_value_flags(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format __maybe_unused, RRDR_OPTIONS options, RRDR_VALUE_FLAGS flags, const char *type) {
+// const size_t dims = r->d, rows = r->rows;
+// size_t next_d_idx = 0;
+// for(size_t d = 0; d < dims ; d++) {
+// if(!rrdr_dimension_should_be_exposed(r->od[d], options))
+// continue;
+//
+// size_t d_idx = next_d_idx++;
+//
+// size_t t = 0;
+// while(t < rows) {
+//
+// // find the beginning
+// time_t started = 0;
+// for(; t < rows ;t++) {
+// RRDR_VALUE_FLAGS o = r->o[t * r->d + d];
+// if(o & flags) {
+// started = r->t[t];
+// break;
+// }
+// }
+//
+// if(started) {
+// time_t ended = 0;
+// for(; t < rows ;t++) {
+// RRDR_VALUE_FLAGS o = r->o[t * r->d + d];
+// if(!(o & flags)) {
+// ended = r->t[t];
+// break;
+// }
+// }
+//
+// if(!ended)
+// ended = r->t[rows - 1];
+//
+// buffer_json_add_array_item_object(wb);
+// buffer_json_member_add_string(wb, "t", type);
+// // buffer_json_member_add_string(wb, "d", string2str(r->dn[d]));
+// buffer_json_member_add_uint64(wb, "d", d_idx);
+// if(started == ended) {
+// if(options & RRDR_OPTION_MILLISECONDS)
+// buffer_json_member_add_time_t2ms(wb, "x", started);
+// else
+// buffer_json_member_add_time_t(wb, "x", started);
+// }
+// else {
+// buffer_json_member_add_array(wb, "x");
+// if(options & RRDR_OPTION_MILLISECONDS) {
+// buffer_json_add_array_item_time_t2ms(wb, started);
+// buffer_json_add_array_item_time_t2ms(wb, ended);
+// }
+// else {
+// buffer_json_add_array_item_time_t(wb, started);
+// buffer_json_add_array_item_time_t(wb, ended);
+// }
+// buffer_json_array_close(wb);
+// }
+// buffer_json_object_close(wb);
+// }
+// }
+// }
+//}
+//
+//void rrdr_json_wrapper_annotations(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format __maybe_unused, RRDR_OPTIONS options) {
+// buffer_json_member_add_array(wb, "annotations");
+//
+// annotations_range_for_value_flags(r, wb, format, options, RRDR_VALUE_EMPTY, "G"); // Gap
+// annotations_range_for_value_flags(r, wb, format, options, RRDR_VALUE_RESET, "O"); // Overflow
+// annotations_range_for_value_flags(r, wb, format, options, RRDR_VALUE_PARTIAL, "P"); // Partial
+//
+// buffer_json_array_close(wb); // annotations
+//}
+
+void rrdr2json_v2(RRDR *r __maybe_unused, BUFFER *wb, DATASOURCE_FORMAT format __maybe_unused, RRDR_OPTIONS options) {
+ bool expose_gbc = query_target_aggregatable(r->internal.qt);
+
+ buffer_json_member_add_object(wb, "result");
+
+ buffer_json_member_add_array(wb, "labels");
+ buffer_json_add_array_item_string(wb, "time");
+ long d, i;
+ const long used = (long)r->d;
+ for(d = 0, i = 0; d < used ; d++) {
if(!rrdr_dimension_should_be_exposed(r->od[d], options))
continue;
- size_t d_idx = next_d_idx++;
+ buffer_json_add_array_item_string(wb, string2str(r->dn[d]));
+ i++;
+ }
+ buffer_json_array_close(wb); // labels
+
+ buffer_json_member_add_object(wb, "point");
+ buffer_json_member_add_uint64(wb, "value", 0);
+ buffer_json_member_add_uint64(wb, "ar", 1);
+ buffer_json_member_add_uint64(wb, "pa", 2);
+ if(expose_gbc)
+ buffer_json_member_add_uint64(wb, "count", 3);
+ buffer_json_object_close(wb);
+
+ buffer_json_member_add_array(wb, "data");
+ if(i) {
+ long start = 0, end = rrdr_rows(r), step = 1;
+ if (!(options & RRDR_OPTION_REVERSED)) {
+ start = rrdr_rows(r) - 1;
+ end = -1;
+ step = -1;
+ }
+
+ // for each line in the array
+ for (i = start; i != end; i += step) {
+ NETDATA_DOUBLE *cn = &r->v[ i * r->d ];
+ RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
+ NETDATA_DOUBLE *ar = &r->ar[ i * r->d ];
+ uint32_t *gbc = &r->gbc [ i * r->d ];
+ time_t now = r->t[i];
+
+ buffer_json_add_array_item_array(wb); // row
+
+ if (options & RRDR_OPTION_MILLISECONDS)
+ buffer_json_add_array_item_time_ms(wb, now); // the time
+ else
+ buffer_json_add_array_item_time_t(wb, now); // the time
+
+ NETDATA_DOUBLE total = 1;
+ if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) {
+ total = 0;
+ for(d = 0; d < used ; d++) {
+ if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED))) continue;
- size_t t = 0;
- while(t < rows) {
+ NETDATA_DOUBLE n = cn[d];
+ if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+ n = -n;
- // find the beginning
- time_t started = 0;
- for(; t < rows ;t++) {
- RRDR_VALUE_FLAGS o = r->o[t * r->d + d];
- if(o & flags) {
- started = r->t[t];
- break;
+ total += n;
}
+
+ // prevent a division by zero
+ if(total == 0) total = 1;
}
- if(started) {
- time_t ended = 0;
- for(; t < rows ;t++) {
- RRDR_VALUE_FLAGS o = r->o[t * r->d + d];
- if(!(o & flags)) {
- ended = r->t[t];
- break;
- }
- }
+ for (d = 0; d < used; d++) {
+ if (!rrdr_dimension_should_be_exposed(r->od[d], options))
+ continue;
+
+ RRDR_VALUE_FLAGS o = co[d];
- if(!ended)
- ended = r->t[rows - 1];
+ buffer_json_add_array_item_array(wb); // point
- buffer_json_add_array_item_object(wb);
- buffer_json_member_add_string(wb, "t", type);
- // buffer_json_member_add_string(wb, "d", string2str(r->dn[d]));
- buffer_json_member_add_uint64(wb, "d", d_idx);
- if(started == ended) {
- if(options & RRDR_OPTION_MILLISECONDS)
- buffer_json_member_add_time_t2ms(wb, "x", started);
+ // add the value
+ NETDATA_DOUBLE n = cn[d];
+
+ if(o & RRDR_VALUE_EMPTY) {
+ if (unlikely(options & RRDR_OPTION_NULL2ZERO))
+ buffer_json_add_array_item_double(wb, 0);
else
- buffer_json_member_add_time_t(wb, "x", started);
+ buffer_json_add_array_item_double(wb, NAN);
}
else {
- buffer_json_member_add_array(wb, "x");
- if(options & RRDR_OPTION_MILLISECONDS) {
- buffer_json_add_array_item_time_t2ms(wb, started);
- buffer_json_add_array_item_time_t2ms(wb, ended);
+ if (unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+ n = -n;
+
+ if (unlikely((options & RRDR_OPTION_PERCENTAGE))) {
+ n = n * 100 / total;
+ }
+
+ if(unlikely(i == start && d == 0)) {
+ r->view.min = r->view.max = n;
}
else {
- buffer_json_add_array_item_time_t(wb, started);
- buffer_json_add_array_item_time_t(wb, ended);
+ if (n < r->view.min) r->view.min = n;
+ if (n > r->view.max) r->view.max = n;
}
- buffer_json_array_close(wb);
+
+ buffer_json_add_array_item_double(wb, n);
}
- buffer_json_object_close(wb);
+
+ // add the anomaly
+ buffer_json_add_array_item_double(wb, ar[d]);
+
+ // add the point annotations
+ buffer_json_add_array_item_uint64(wb, o);
+
+ // add the count
+ if(expose_gbc)
+ buffer_json_add_array_item_uint64(wb, gbc[d]);
+
+ buffer_json_array_close(wb); // point
}
+
+ buffer_json_array_close(wb); // row
}
}
-}
-
-void rrdr_json_wrapper_annotations(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format __maybe_unused, RRDR_OPTIONS options) {
- buffer_json_member_add_array(wb, "annotations");
- annotations_for_value_flags(r, wb, format, options, RRDR_VALUE_EMPTY, "G"); // Gap
- annotations_for_value_flags(r, wb, format, options, RRDR_VALUE_RESET, "O"); // Overflow
- annotations_for_value_flags(r, wb, format, options, RRDR_VALUE_PARTIAL, "P"); // Partial
+ buffer_json_array_close(wb); // data
- buffer_json_array_close(wb); // annotations
+ buffer_json_object_close(wb); // annotations
}
void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format __maybe_unused, RRDR_OPTIONS options __maybe_unused) {
diff --git a/web/api/formatters/json_wrapper.h b/web/api/formatters/json_wrapper.h
index 9a529c422e..797be68fcd 100644
--- a/web/api/formatters/json_wrapper.h
+++ b/web/api/formatters/json_wrapper.h
@@ -10,7 +10,7 @@ typedef void (*wrapper_begin_t)(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, R
typedef void (*wrapper_end_t)(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options);
void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options, RRDR_TIME_GROUPING group_method);
-void rrdr_json_wrapper_annotations(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options);
+void rrdr2json_v2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options);
void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options);
void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options, RRDR_TIME_GROUPING group_method);
diff --git a/web/api/formatters/rrd2json.c b/web/api/formatters/rrd2json.c
index 453e633e34..240498ee61 100644
--- a/web/api/formatters/rrd2json.c
+++ b/web/api/formatters/rrd2json.c
@@ -11,6 +11,10 @@ inline bool query_target_has_percentage_units(struct query_target *qt) {
return false;
}
+bool query_target_aggregatable(struct query_target *qt) {
+ return (qt->request.options & RRDR_OPTION_RETURN_RAW);
+}
+
void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) {
rrdset2json(st, wb, NULL, NULL, 0);
}
@@ -20,6 +24,9 @@ const char *rrdr_format_to_string(DATASOURCE_FORMAT format) {
case DATASOURCE_JSON:
return DATASOURCE_FORMAT_JSON;
+ case DATASOURCE_JSON2:
+ return DATASOURCE_FORMAT_JSON2;
+
case DATASOURCE_DATATABLE_JSON:
return DATASOURCE_FORMAT_DATATABLE_JSON;
@@ -174,58 +181,74 @@ static RRDR *data_query_group_by(RRDR *r) {
if(!qt->group_by.used)
qt->request.group_by &= ~RRDR_GROUP_BY_LABEL;
- if(!(qt->request.group_by & (RRDR_GROUP_BY_NODE | RRDR_GROUP_BY_INSTANCE | RRDR_GROUP_BY_DIMENSION | RRDR_GROUP_BY_LABEL)))
+ if(!(qt->request.group_by & (RRDR_GROUP_BY_NODE | RRDR_GROUP_BY_INSTANCE | RRDR_GROUP_BY_DIMENSION | RRDR_GROUP_BY_LABEL | RRDR_GROUP_BY_SELECTED)))
qt->request.group_by = RRDR_GROUP_BY_DIMENSION;
int added = 0;
BUFFER *key = buffer_create(0, NULL);
QUERY_INSTANCE *last_qi = NULL;
size_t priority = 0;
- for(size_t c = 0; c < qt->query.used ;c++) {
- if(!rrdr_dimension_should_be_exposed(r->od[c], options))
+ time_t update_every_max = 0;
+ for(size_t d = 0; d < qt->query.used ; d++) {
+ if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED)))
continue;
- QUERY_METRIC *qm = query_metric(qt, c);
+ QUERY_METRIC *qm = query_metric(qt, d);
QUERY_INSTANCE *qi = query_instance(qt, qm->link.query_instance_id);
- QUERY_NODE *qn = query_node(qt, qm->link.query_host_id);
+ QUERY_NODE *qn = query_node(qt, qm->link.query_node_id);
if(qi != last_qi) {
priority = 0;
last_qi = qi;
+
+ time_t update_every = rrdinstance_acquired_update_every(qi->ria);
+ if(update_every > update_every_max)
+ update_every_max = update_every;
}
else
priority++;
+ // --------------------------------------------------------------------
// generate the group by key
buffer_flush(key);
- if(qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
- buffer_fast_strcat(key, "|", 1);
- buffer_strcat(key, query_metric_id(qt, qm));
+ if(unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) {
+ buffer_strcat(key, "__hidden_dimensions__");
}
- if(qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
- buffer_fast_strcat(key, "|", 1);
- buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
+ else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
+ buffer_strcat(key, "selected");
}
- if(qt->request.group_by & RRDR_GROUP_BY_LABEL) {
- DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
- for(size_t l = 0; l < qt->group_by.used ;l++) {
+ else {
+ if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
buffer_fast_strcat(key, "|", 1);
- rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
+ buffer_strcat(key, query_metric_id(qt, qm));
}
- }
- if(qt->request.group_by & RRDR_GROUP_BY_NODE) {
- buffer_fast_strcat(key, "|", 1);
- buffer_strcat(key, qn->rrdhost->machine_guid);
- }
- // append the units
- if(query_target_has_percentage_units(qt)) {
- buffer_fast_strcat(key, "|%", 2);
- }
- else {
- buffer_fast_strcat(key, "|", 1);
- buffer_strcat(key, rrdinstance_acquired_units(qi->ria));
+ if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
+ buffer_fast_strcat(key, "|", 1);
+ buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
+ }
+
+ if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
+ DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
+ for (size_t l = 0; l < qt->group_by.used; l++) {
+ buffer_fast_strcat(key, "|", 1);
+ rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
+ }
+ }
+
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
+ buffer_fast_strcat(key, "|", 1);
+ buffer_strcat(key, qn->rrdhost->machine_guid);
+ }
+
+ // append the units
+ if (query_target_has_percentage_units(qt)) {
+ buffer_fast_strcat(key, "|%", 2);
+ } else {
+ buffer_fast_strcat(key, "|", 1);
+ buffer_strcat(key, rrdinstance_acquired_units(qi->ria));
+ }
}
// lookup the key in the dictionary
@@ -237,66 +260,92 @@ static RRDR *data_query_group_by(RRDR *r) {
*set = pos = added++;
+ // ----------------------------------------------------------------
// generate the dimension id
buffer_flush(key);
- if(qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
- buffer_strcat(key, query_metric_id(qt, qm));
+ if(unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) {
+ buffer_strcat(key, "__hidden_dimensions__");
}
- if(qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
- if(buffer_strlen(key) != 0)
- buffer_fast_strcat(key, ",", 1);
-
- if(qt->request.group_by & RRDR_GROUP_BY_NODE)
- buffer_strcat(key, rrdinstance_acquired_id(qi->ria));
- else
- buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
+ else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
+ buffer_strcat(key, "selected");
}
- if(qt->request.group_by & RRDR_GROUP_BY_LABEL) {
- DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
- for(size_t l = 0; l < qt->group_by.used ;l++) {
- if(buffer_strlen(key) != 0)
+ else {
+ if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
+ buffer_strcat(key, query_metric_id(qt, qm));
+ }
+
+ if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
+ if (buffer_strlen(key) != 0)
buffer_fast_strcat(key, ",", 1);
- rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
+
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE)
+ buffer_strcat(key, rrdinstance_acquired_id(qi->ria));
+ else
+ buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
}
- }
- if(qt->request.group_by & RRDR_GROUP_BY_NODE) {
- if(buffer_strlen(key) != 0)
- buffer_fast_strcat(key, ",", 1);
- buffer_strcat(key, qn->rrdhost->machine_guid);
+ if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
+ DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
+ for (size_t l = 0; l < qt->group_by.used; l++) {
+ if (buffer_strlen(key) != 0)
+ buffer_fast_strcat(key, ",", 1);
+ rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
+ }
+ }
+
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
+ if (buffer_strlen(key) != 0)
+ buffer_fast_strcat(key, ",", 1);
+
+ buffer_strcat(key, qn->rrdhost->machine_guid);
+ }
}
+
entries[pos].id = string_strdupz(buffer_tostring(key));
+ // ----------------------------------------------------------------
// generate the dimension name
buffer_flush(key);
- if(qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
- buffer_strcat(key, query_metric_name(qt, qm));
+ if(unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) {
+ buffer_strcat(key, "__hidden_dimensions__");
}
- if(qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
- if(buffer_strlen(key) != 0)
- buffer_fast_strcat(key, ",", 1);
-
- if(qt->request.group_by & RRDR_GROUP_BY_NODE)
- buffer_strcat(key, rrdinstance_acquired_name(qi->ria));
- else
- buffer_strcat(key, string2str(query_instance_name_fqdn(qt, qi)));
+ else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
+ buffer_strcat(key, "selected");
}
- if(qt->request.group_by & RRDR_GROUP_BY_LABEL) {
- DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
- for(size_t l = 0; l < qt->group_by.used ;l++) {
- if(buffer_strlen(key) != 0)
+ else {
+ if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
+ buffer_strcat(key, query_metric_name(qt, qm));
+ }
+
+ if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
+ if (buffer_strlen(key) != 0)
buffer_fast_strcat(key, ",", 1);
- rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
+
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE)
+ buffer_strcat(key, rrdinstance_acquired_name(qi->ria));
+ else
+ buffer_strcat(key, string2str(query_instance_name_fqdn(qt, qi)));
}
- }
- if(qt->request.group_by & RRDR_GROUP_BY_NODE) {
- if(buffer_strlen(key) != 0)
- buffer_fast_strcat(key, ",", 1);
- buffer_strcat(key, rrdhost_hostname(qn->rrdhost));
+ if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
+ DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
+ for (size_t l = 0; l < qt->group_by.used; l++) {
+ if (buffer_strlen(key) != 0)
+ buffer_fast_strcat(key, ",", 1);
+ rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
+ }
+ }
+
+ if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
+ if (buffer_strlen(key) != 0)
+ buffer_fast_strcat(key, ",", 1);
+
+ buffer_strcat(key, rrdhost_hostname(qn->rrdhost));
+ }
}
+
entries[pos].name = string_strdupz(buffer_tostring(key));
// add the rest of the info
@@ -321,8 +370,8 @@ static RRDR *data_query_group_by(RRDR *r) {
// copy the dimension flags decided by the query target
// we need this, because if a dimension is explicitly selected
// the query target adds to it the non-zero flag
- qm->status |= RRDR_DIMENSION_GROUPED | r->od[c];
- entries[pos].od |= RRDR_DIMENSION_GROUPED | r->od[c];
+ qm->status |= RRDR_DIMENSION_GROUPED | r->od[d];
+ entries[pos].od |= RRDR_DIMENSION_GROUPED | r->od[d];
}
// check if we have multiple units
@@ -362,15 +411,22 @@ static RRDR *data_query_group_by(RRDR *r) {
r2->stats.result_points_generated = r2->d * r2->n;
// initialize r2 (dimension options, names, and ids)
- for(size_t c2 = 0; c2 < r2->d ; c2++) {
- r2->od[c2] = entries[c2].od;
- r2->di[c2] = entries[c2].id;
- r2->dn[c2] = entries[c2].name;
- r2->du[c2] = entries[c2].units;
- r2->dp[c2] = entries[c2].priority;
- r2->dgbc[c2] = entries[c2].count;
+ for(size_t d2 = 0; d2 < r2->d ; d2++) {
+ r2->od[d2] = entries[d2].od;
+ r2->di[d2] = entries[d2].id;
+ r2->dn[d2] = entries[d2].name;
+ r2->du[d2] = entries[d2].units;
+ r2->dp[d2] = entries[d2].priority;
+ r2->dgbc[d2] = entries[d2].count;
}
+ r2->partial_data_trimming.max_update_every = update_every_max;
+ r2->partial_data_trimming.expected_after =
+ (!(qt->request.options & RRDR_OPTION_RETURN_RAW) && qt->window.before >= qt->window.now - update_every_max) ?
+ qt->window.before - update_every_max :
+ qt->window.before;
+ r2->partial_data_trimming.trimmed_after = qt->window.before;
+