summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2023-03-13 23:39:06 +0200
committerGitHub <noreply@github.com>2023-03-13 23:39:06 +0200
commitcd50bf42367ed49ed12e944d66a445653c6f038c (patch)
tree381768f311ad42ee9dceb5cbe63fc8969a02f515
parent8068c952d8e1812caf348b77f38ae3df1014cc65 (diff)
/api/v2 part 4 (#14706)
* expose the order of group by * key renames in json wrapper v2 * added group by context and group by units * added view_average_values * fix for view_average_values when percentage is specified * option group-by-labels is enabling the exposure of all the labels that are used for each of the final grouped dimensions * when executing group by queries, allocate one dimension data at a time - not all of them * respect hidden dimensions * cancel running data query on socket error * use poll to detect socket errors * use POLLRDHUP to detect half closed connections * make sure POLLRDHUP is available * do not destroy aral-by-size arals * completed documentation of /api/v2/data. * moved min, max back to view; updated swagger yaml and json * default format for /api/v2/data is json2
-rw-r--r--database/contexts/rrdcontext.h5
-rw-r--r--database/rrdlabels.c2
-rw-r--r--libnetdata/aral/aral.c8
-rw-r--r--libnetdata/buffer/buffer.h53
-rw-r--r--libnetdata/socket/socket.c48
-rw-r--r--libnetdata/socket/socket.h3
-rw-r--r--libnetdata/string/utf8.h4
-rw-r--r--web/api/formatters/json/json.c144
-rw-r--r--web/api/formatters/json/json.h1
-rw-r--r--web/api/formatters/json_wrapper.c328
-rw-r--r--web/api/formatters/json_wrapper.h12
-rw-r--r--web/api/formatters/rrd2json.c527
-rw-r--r--web/api/formatters/rrd2json.h2
-rw-r--r--web/api/netdata-swagger.json1058
-rw-r--r--web/api/netdata-swagger.yaml281
-rw-r--r--web/api/queries/average/average.c16
-rw-r--r--web/api/queries/countif/countif.c12
-rw-r--r--web/api/queries/des/des.c14
-rw-r--r--web/api/queries/incremental_sum/incremental_sum.c12
-rw-r--r--web/api/queries/max/max.c12
-rw-r--r--web/api/queries/median/median.c14
-rw-r--r--web/api/queries/min/min.c12
-rw-r--r--web/api/queries/percentile/percentile.c14
-rw-r--r--web/api/queries/query.c912
-rw-r--r--web/api/queries/query.h12
-rw-r--r--web/api/queries/rrdr.c23
-rw-r--r--web/api/queries/rrdr.h28
-rw-r--r--web/api/queries/ses/ses.c14
-rw-r--r--web/api/queries/stddev/stddev.c14
-rw-r--r--web/api/queries/sum/sum.c12
-rw-r--r--web/api/queries/trimmed_mean/trimmed_mean.c14
-rw-r--r--web/api/web_api.c5
-rw-r--r--web/api/web_api.h2
-rw-r--r--web/api/web_api_v1.c2
-rw-r--r--web/api/web_api_v2.c7
35 files changed, 2669 insertions, 958 deletions
diff --git a/database/contexts/rrdcontext.h b/database/contexts/rrdcontext.h
index 0261c78227..152422aa79 100644
--- a/database/contexts/rrdcontext.h
+++ b/database/contexts/rrdcontext.h
@@ -258,6 +258,8 @@ typedef struct query_metric {
#define MAX_QUERY_TARGET_ID_LENGTH 255
+typedef bool (*interrupt_callback_t)(void *data);
+
typedef struct query_target_request {
size_t version;
@@ -303,6 +305,9 @@ typedef struct query_target_request {
RRDR_GROUP_BY_FUNCTION group_by_aggregate_function;
usec_t received_ut;
+
+ interrupt_callback_t interrupt_callback;
+ void *interrupt_callback_data;
} QUERY_TARGET_REQUEST;
#define GROUP_BY_MAX_LABEL_KEYS 10
diff --git a/database/rrdlabels.c b/database/rrdlabels.c
index 3b728db95a..f6abd60232 100644
--- a/database/rrdlabels.c
+++ b/database/rrdlabels.c
@@ -399,7 +399,7 @@ size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_si
// find how big this character is (2-4 bytes)
size_t utf_character_size = 2;
- while(utf_character_size <= 4 && src[utf_character_size] && IS_UTF8_BYTE(src[utf_character_size]) && !IS_UTF8_STARTBYTE(src[utf_character_size]))
+ while(utf_character_size < 4 && src[utf_character_size] && IS_UTF8_BYTE(src[utf_character_size]) && !IS_UTF8_STARTBYTE(src[utf_character_size]))
utf_character_size++;
if(utf) {
diff --git a/libnetdata/aral/aral.c b/libnetdata/aral/aral.c
index 2a4b0687dd..60fe5e39a7 100644
--- a/libnetdata/aral/aral.c
+++ b/libnetdata/aral/aral.c
@@ -885,10 +885,10 @@ void aral_by_size_release(ARAL *ar) {
fatal("ARAL BY SIZE: double release detected");
aral_by_size_globals.array[size].refcount--;
- if(!aral_by_size_globals.array[size].refcount) {
- aral_destroy(aral_by_size_globals.array[size].ar);
- aral_by_size_globals.array[size].ar = NULL;
- }
+// if(!aral_by_size_globals.array[size].refcount) {
+// aral_destroy(aral_by_size_globals.array[size].ar);
+// aral_by_size_globals.array[size].ar = NULL;
+// }
netdata_spinlock_unlock(&aral_by_size_globals.spinlock);
}
diff --git a/libnetdata/buffer/buffer.h b/libnetdata/buffer/buffer.h
index 1cf1ec981d..d19aad81e8 100644
--- a/libnetdata/buffer/buffer.h
+++ b/libnetdata/buffer/buffer.h
@@ -3,6 +3,7 @@
#ifndef NETDATA_WEB_BUFFER_H
#define NETDATA_WEB_BUFFER_H 1
+#include "../string/utf8.h"
#include "../libnetdata.h"
#define WEB_DATA_LENGTH_INCREASE_STEP 1024
@@ -203,18 +204,56 @@ static inline void buffer_strcat(BUFFER *wb, const char *txt) {
static inline void buffer_json_strcat(BUFFER *wb, const char *txt) {
if(unlikely(!txt || !*txt)) return;
- const char *t = txt;
+ const unsigned char *t = (const unsigned char *)txt;
while(*t) {
- buffer_need_bytes(wb, 100);
- char *s = &wb->buffer[wb->len];
- char *d = s;
- const char *e = &wb->buffer[wb->size - 1]; // remove 1 to make room for the escape character
+ buffer_need_bytes(wb, 110);
+ unsigned char *s = (unsigned char *)&wb->buffer[wb->len];
+ unsigned char *d = s;
+ const unsigned char *e = (unsigned char *)&wb->buffer[wb->size - 10]; // make room for the max escape sequence
while(*t && d < e) {
- if(unlikely(*t == '\\' || *t == '\"'))
+#ifdef BUFFER_JSON_ESCAPE_UTF
+ if(unlikely(IS_UTF8_STARTBYTE(*t) && IS_UTF8_BYTE(t[1]))) {
+ // UTF-8 multi-byte encoded character
+
+ // find how big this character is (2-4 bytes)
+ size_t utf_character_size = 2;
+ while(utf_character_size < 4 && t[utf_character_size] && IS_UTF8_BYTE(t[utf_character_size]) && !IS_UTF8_STARTBYTE(t[utf_character_size]))
+ utf_character_size++;
+
+ uint32_t code_point = 0;
+ for (size_t i = 0; i < utf_character_size; i++) {
+ code_point <<= 6;
+ code_point |= (t[i] & 0x3F);
+ }
+
+ t += utf_character_size;
+
+ // encode as \u escape sequence
+ *d++ = '\\';
+ *d++ = 'u';
+ *d++ = hex_digits[(code_point >> 12) & 0xf];
+ *d++ = hex_digits[(code_point >> 8) & 0xf];
+ *d++ = hex_digits[(code_point >> 4) & 0xf];
+ *d++ = hex_digits[code_point & 0xf];
+ }
+ else
+#endif
+ if(unlikely(*t < ' ')) {
+ uint32_t v = *t++;
*d++ = '\\';
+ *d++ = 'u';
+ *d++ = hex_digits[(v >> 12) & 0xf];
+ *d++ = hex_digits[(v >> 8) & 0xf];
+ *d++ = hex_digits[(v >> 4) & 0xf];
+ *d++ = hex_digits[v & 0xf];
+ }
+ else {
+ if (unlikely(*t == '\\' || *t == '\"'))
+ *d++ = '\\';
- *d++ = *t++;
+ *d++ = *t++;
+ }
}
wb->len += d - s;
diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c
index eb8e3a93da..dd36da105e 100644
--- a/libnetdata/socket/socket.c
+++ b/libnetdata/socket/socket.c
@@ -1,5 +1,13 @@
// SPDX-License-Identifier: GPL-3.0-or-later
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // for POLLRDHUP
+#endif
+
+#ifndef __BSD_VISIBLE
+#define __BSD_VISIBLE // for POLLRDHUP
+#endif
+
#include "../libnetdata.h"
// --------------------------------------------------------------------------------------------------------------------
@@ -11,6 +19,46 @@
#define LARGE_SOCK_SIZE 4096
#endif
+bool fd_is_socket(int fd) {
+ int type;
+ socklen_t len = sizeof(type);
+ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) == -1)
+ return false;
+
+ return true;
+}
+
+bool sock_has_output_error(int fd) {
+ if(fd < 0) {
+ //internal_error(true, "invalid socket %d", fd);
+ return false;
+ }
+
+// if(!fd_is_socket(fd)) {
+// //internal_error(true, "fd %d is not a socket", fd);
+// return false;
+// }
+
+ short int errors = POLLERR | POLLHUP | POLLNVAL;
+
+#ifdef POLLRDHUP
+ errors |= POLLRDHUP;
+#endif
+
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLOUT | errors,
+ .revents = 0,
+ };
+
+ if(poll(&pfd, 1, 0) == -1) {
+ //internal_error(true, "poll() failed");
+ return false;
+ }
+
+ return ((pfd.revents & errors) || !(pfd.revents & POLLOUT));
+}
+
int sock_setnonblock(int fd) {
int flags;
diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h
index 9577453d53..3a8369fba0 100644
--- a/libnetdata/socket/socket.h
+++ b/libnetdata/socket/socket.h
@@ -74,6 +74,9 @@ ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
#endif
+bool fd_is_socket(int fd);
+bool sock_has_output_error(int fd);
+
int sock_setnonblock(int fd);
int sock_delnonblock(int fd);
int sock_setreuse(int fd, int reuse);
diff --git a/libnetdata/string/utf8.h b/libnetdata/string/utf8.h
index 133ec710b6..3e6c8c2883 100644
--- a/libnetdata/string/utf8.h
+++ b/libnetdata/string/utf8.h
@@ -3,7 +3,7 @@
#ifndef NETDATA_STRING_UTF8_H
#define NETDATA_STRING_UTF8_H 1
-#define IS_UTF8_BYTE(x) (x & 0x80)
-#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&(x & 0x40))
+#define IS_UTF8_BYTE(x) ((x) & 0x80)
+#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&((x) & 0x40))
#endif /* NETDATA_STRING_UTF8_H */
diff --git a/web/api/formatters/json/json.c b/web/api/formatters/json/json.c
index 832344ec05..dae0cc99cf 100644
--- a/web/api/formatters/json/json.c
+++ b/web/api/formatters/json/json.c
@@ -154,7 +154,6 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
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];
@@ -211,15 +210,13 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
buffer_fast_strcat(wb, post_date, post_date_len);
}
- if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) {
+ if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_AR)))) {
total = 0;
for(c = 0; c < used ;c++) {
if(unlikely(!(r->od[c] & RRDR_DIMENSION_QUERIED))) continue;
NETDATA_DOUBLE n;
- if(unlikely(options & RRDR_OPTION_INTERNAL_GBC))
- n = gbc[c];
- else if(unlikely(options & RRDR_OPTION_INTERNAL_AR))
+ if(unlikely(options & RRDR_OPTION_INTERNAL_AR))
n = ar[c];
else
n = cn[c];
@@ -239,9 +236,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
continue;
NETDATA_DOUBLE n;
- if(unlikely(options & RRDR_OPTION_INTERNAL_GBC))
- n = gbc[c];
- else if(unlikely(options & RRDR_OPTION_INTERNAL_AR))
+ if(unlikely(options & RRDR_OPTION_INTERNAL_AR))
n = ar[c];
else
n = cn[c];
@@ -251,7 +246,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
if(unlikely( options & RRDR_OPTION_OBJECTSROWS ))
buffer_sprintf(wb, "%s%s%s: ", kq, string2str(r->dn[c]), kq);
- if(co[c] & RRDR_VALUE_EMPTY && !(options & (RRDR_OPTION_INTERNAL_AR | RRDR_OPTION_INTERNAL_GBC))) {
+ if(co[c] & RRDR_VALUE_EMPTY && !(options & (RRDR_OPTION_INTERNAL_AR))) {
if(unlikely(options & RRDR_OPTION_NULL2ZERO))
buffer_fast_strcat(wb, "0", 1);
else
@@ -261,7 +256,7 @@ 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) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) {
+ if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_AR)))) {
n = n * 100 / total;
if(unlikely(i == start && c == 0)) {
@@ -285,3 +280,132 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
buffer_strcat(wb, finish);
//info("RRD2JSON(): %s: END", r->st->id);
}
+
+
+void rrdr2json_v2(RRDR *r, BUFFER *wb) {
+ QUERY_TARGET *qt = r->internal.qt;
+ RRDR_OPTIONS options = qt->request.options;
+
+ bool expose_gbc = query_target_aggregatable(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;
+
+ 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))) {
+ total = 0;
+ for(d = 0; d < used ; d++) {
+ if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED))) continue;
+
+ NETDATA_DOUBLE n = cn[d];
+ if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+ n = -n;
+
+ total += n;
+ }
+
+ // prevent a division by zero
+ if(total == 0) total = 1;
+ }
+
+ for (d = 0; d < used; d++) {
+ if (!rrdr_dimension_should_be_exposed(r->od[d], options))
+ continue;
+
+ RRDR_VALUE_FLAGS o = co[d];
+
+ buffer_json_add_array_item_array(wb); // point
+
+ // 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_add_array_item_double(wb, NAN);
+ }
+ else {
+ 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 {
+ if (n < r->view.min) r->view.min = n;
+ if (n > r->view.max) r->view.max = n;
+ }
+
+ buffer_json_add_array_item_double(wb, n);
+ }
+
+ // 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
+ }
+ }
+
+ buffer_json_array_close(wb); // data
+
+ buffer_json_object_close(wb); // annotations
+}
diff --git a/web/api/formatters/json/json.h b/web/api/formatters/json/json.h
index fb59e5c9a6..d1ab4f9014 100644
--- a/web/api/formatters/json/json.h
+++ b/web/api/formatters/json/json.h
@@ -6,5 +6,6 @@
#include "../rrd2json.h"
void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable);
+void rrdr2json_v2(RRDR *r, BUFFER *wb);
#endif //NETDATA_API_FORMATTER_JSON_H
diff --git a/web/api/formatters/json_wrapper.c b/web/api/formatters/json_wrapper.c
index 5e1edc67d0..b7c5720589 100644
--- a/web/api/formatters/json_wrapper.c
+++ b/web/api/formatters/json_wrapper.c
@@ -676,7 +676,7 @@ static inline long query_target_metrics_latest_values(BUFFER *wb, const char *ke
return i;
}
-static inline size_t rrdr_latest_values(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
+static inline size_t rrdr_dimension_latest_values(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
size_t c, i;
buffer_json_member_add_array(wb, key);
@@ -732,11 +732,54 @@ static inline size_t rrdr_latest_values(BUFFER *wb, const char *key, RRDR *r, RR
return i;
}
-void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options,
- RRDR_TIME_GROUPING group_method)
-{
+static inline void rrdr_dimension_average_values(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
+ if(!r->dv)
+ return;
+
+ buffer_json_member_add_array(wb, key);
+
+ bool percentage = r->internal.qt->request.options & RRDR_OPTION_PERCENTAGE;
+ NETDATA_DOUBLE total = 0;
+ if(percentage) {
+ for(size_t c = 0; c < r->d ; c++) {
+ if(!(r->od[c] & RRDR_DIMENSION_QUERIED))
+ continue;
+
+ total += r->dv[c];
+ }
+ }
+
+ for(size_t c = 0; c < r->d ; c++) {
+ if(!rrdr_dimension_should_be_exposed(r->od[c], options))
+ continue;
+
+ if(percentage)
+ buffer_json_add_array_item_double(wb, r->dv[c] * 100.0 / total);
+ else
+ buffer_json_add_array_item_double(wb, r->dv[c]);
+ }
+
+ buffer_json_array_close(wb);
+}
+
+static void rrdr_timings_v12(BUFFER *wb, const char *key, RRDR *r) {
QUERY_TARGET *qt = r->internal.qt;
+ qt->timings.finished_ut = now_monotonic_usec();
+ buffer_json_member_add_object(wb, key);
+ buffer_json_member_add_double(wb, "prep_ms", (NETDATA_DOUBLE)(qt->timings.preprocessed_ut - qt->timings.received_ut) / USEC_PER_MS);
+ buffer_json_member_add_double(wb, "query_ms", (NETDATA_DOUBLE)(qt->timings.executed_ut - qt->timings.preprocessed_ut) / USEC_PER_MS);
+ buffer_json_member_add_double(wb, "group_by_ms", (NETDATA_DOUBLE)(qt->timings.group_by_ut - qt->timings.executed_ut) / USEC_PER_MS);
+ buffer_json_member_add_double(wb, "output_ms", (NETDATA_DOUBLE)(qt->timings.finished_ut - qt->timings.group_by_ut) / USEC_PER_MS);
+ buffer_json_member_add_double(wb, "total_ms", (NETDATA_DOUBLE)(qt->timings.finished_ut - qt->timings.received_ut) / USEC_PER_MS);
+ buffer_json_object_close(wb);
+}
+
+void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb) {
+ QUERY_TARGET *qt = r->internal.qt;
+ DATASOURCE_FORMAT format = qt->request.format;
+ RRDR_OPTIONS options = qt->request.options;
+
long rows = rrdr_rows(r);
char kq[2] = "", // key quote
@@ -762,7 +805,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR
buffer_json_member_add_time_t(wb, "last_entry", qt->db.last_time_s);
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_string(wb, "group", time_grouping_tostring(group_method));
+ buffer_json_member_add_string(wb, "group", time_grouping_tostring(qt->request.time_group_method));
web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", r->view.options);
if(!rrdr_dimension_names(wb, "dimension_names", r, options))
@@ -788,7 +831,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR
if(!query_target_metrics_latest_values(wb, "latest_values", r, options))
rows = 0;
- size_t dimensions = rrdr_latest_values(wb, "view_latest_values", r, options);
+ size_t dimensions = rrdr_dimension_latest_values(wb, "view_latest_values", r, options);
if(!dimensions)
rows = 0;
@@ -858,13 +901,53 @@ static void query_target_combined_chart_type(BUFFER *wb, QUERY_TARGET *qt, size_
buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(rrdcontext_acquired_chart_type(qt->contexts.array[0].rca)));
}
-static void rrdr_dimension_units_array_v2(BUFFER *wb, RRDR *r, RRDR_OPTIONS options) {
+static void rrdr_grouped_by_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options __maybe_unused) {
+ QUERY_TARGET *qt = r->internal.qt;
+
+ buffer_json_member_add_array(wb, key);
+
+ if(qt->request.group_by & RRDR_GROUP_BY_SELECTED)
+ buffer_json_add_array_item_string(wb, "selected");
+
+ else {
+
+ if(qt->request.group_by & RRDR_GROUP_BY_DIMENSION)
+ buffer_json_add_array_item_string(wb, "dimension");
+
+ if(qt->request.group_by & RRDR_GROUP_BY_INSTANCE)
+ buffer_json_add_array_item_string(wb, "instance");
+
+ if(qt->request.group_by & RRDR_GROUP_BY_LABEL) {
+ BUFFER *b = buffer_create(0, NULL);
+ for (size_t l = 0; l < qt->group_by.used; l++) {
+ buffer_flush(b);
+ buffer_fast_strcat(b, "label:", 6);
+ buffer_strcat(b, qt->group_by.label_keys[l]);
+ buffer_json_add_array_item_string(wb, buffer_tostring(b));
+ }
+ buffer_free(b);
+ }
+
+ if(qt->request.group_by & RRDR_GROUP_BY_NODE)
+ buffer_json_add_array_item_string(wb, "node");
+
+ if(qt->request.group_by & RRDR_GROUP_BY_CONTEXT)
+ buffer_json_add_array_item_string(wb, "context");
+
+ if(qt->request.group_by & RRDR_GROUP_BY_UNITS)
+ buffer_json_add_array_item_string(wb, "units");
+ }
+
+ buffer_json_array_close(wb); // group_by_order
+}
+
+static void rrdr_dimension_units_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
if(!r->du)
return;
bool percentage = query_target_has_percentage_units(r->internal.qt);
- buffer_json_member_add_array(wb, "units");
+ buffer_json_member_add_array(wb, key);
for(size_t c = 0; c < r->d ; c++) {
if(!rrdr_dimension_should_be_exposed(r->od[c], options))
continue;
@@ -877,11 +960,11 @@ static void rrdr_dimension_units_array_v2(BUFFER *wb, RRDR *r, RRDR_OPTIONS opti
buffer_json_array_close(wb);
}
-static void rrdr_dimension_priority_array(BUFFER *wb, RRDR *r, RRDR_OPTIONS options) {
+static void rrdr_dimension_priority_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
if(!r->dp)
return;
- buffer_json_member_add_array(wb, "priorities");
+ buffer_json_member_add_array(wb, key);
for(size_t c = 0; c < r->d ; c++) {
if(!rrdr_dimension_should_be_exposed(r->od[c], options))
continue;
@@ -891,11 +974,11 @@ static void rrdr_dimension_priority_array(BUFFER *wb, RRDR *r, RRDR_OPTIONS opti
buffer_json_array_close(wb);
}
-static void rrdr_dimension_grouped_array(BUFFER *wb, RRDR *r, RRDR_OPTIONS options) {
+static void rrdr_dimension_aggregated_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
if(!r->dgbc)
return;
- buffer_json_member_add_array(wb, "grouped");
+ buffer_json_member_add_array(wb, key);
for(size_t c = 0; c < r->d ;c++) {
if(!rrdr_dimension_should_be_exposed(r->od[c], options))
continue;
@@ -1089,12 +1172,9 @@ static void query_target_detailed_objects_tree(BUFFER *wb, RRDR *r, RRDR_OPTIONS
buffer_json_object_close(wb); // hosts
}
-void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options,
- RRDR_TIME_GROUPING group_method)
-{
+void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb) {
QUERY_TARGET *qt = r->internal.qt;
-
- long rows = rrdr_rows(r);
+ RRDR_OPTIONS options = qt->request.options;
char kq[2] = "\"", // key quote
sq[2] = "\""; // string quote
@@ -1181,7 +1261,6 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
buffer_json_member_add_uint64(wb, "contexts_soft_hash", qt->versions.contexts_soft_hash);
buffer_json_object_close(wb);
- size_t contexts;
buffer_json_member_add_object(wb, "summary");
struct summary_total_counts
nodes_totals = { 0 },
@@ -1192,7 +1271,7 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
label_key_value_totals = { 0 };
{
query_target_summary_nodes_v2(wb, qt, "nodes", &nodes_totals);
- contexts = query_target_summary_contexts_v2(wb, qt, "contexts", &contexts_totals);
+ r->internal.contexts = query_target_summary_contexts_v2(wb, qt, "contexts", &contexts_totals);
query_target_summary_instances_v2(wb, qt, "instances", &instances_totals);
query_target_summary_dimensions_v12(wb, qt, "dimensions", true, &metrics_totals);
query_target_summary_labels_v12(wb, qt, "labels", true, &label_key_totals, &label_key_value_totals);
@@ -1224,7 +1303,7 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
buffer_json_member_add_time_t(wb, "first_entry", qt->db.first_time_s);
buffer_json_member_add_time_t(wb, "last_entry", qt->db.last_time_s);
- buffer_json_member_add_array(wb, "tiers");
+ buffer_json_member_add_array(wb, "per_tier");
for(size_t tier = 0; tier < storage_tiers ; tier++) {
buffer_json_add_array_item_object(wb);
buffer_json_member_add_uint64(wb, "tier", tier);
@@ -1238,39 +1317,6 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
buffer_json_array_close(wb);
}