@cryptotaxi247 / netdata-1 / commits / bb85c57a3

facets optimizations (#15940)

Costa Tsaousis committed Sep 12, 2023 at 18:19 UTC bb85c57a39674be5fa7d815734aead46fb47a08c
4 files changed +220 -77
collectors/systemd-journal.plugin/systemd-journal.c
+54 -34
@@ -90,52 +90,71 @@ int systemd_journal_query(BUFFER *wb, FACETS *facets, usec_t after_ut, usec_t be
90 uint64_t first_msg_ut = 0;
91 bool timed_out = false;
92 size_t row_counter = 0;
93 - sd_journal_seek_realtime_usec(j, before_ut);
94 - SD_JOURNAL_FOREACH_BACKWARDS(j) {
95 - row_counter++;
93
97 - uint64_t msg_ut;
98 - sd_journal_get_realtime_usec(j, &msg_ut);
94 + // the entries are not guaranteed to be sorted, so we process up to 100 entries beyond
95 + // the end of the query to find possibly useful logs for our time-frame
96 + size_t excess_rows_allowed = 100;
97
100 - if(unlikely(!first_msg_ut)) {
101 - if(msg_ut == if_modified_since) {
102 - sd_journal_close(j);
103 - return HTTP_RESP_NOT_MODIFIED;
104 - }
98 + if(sd_journal_seek_realtime_usec(j, before_ut) < 0) {
99 + netdata_log_error("SYSTEMD-JOURNAL: Failed to seek to %llu", before_ut);
100 + if(sd_journal_seek_tail(j) < 0) {
101 + netdata_log_error("SYSTEMD-JOURNAL: Failed to seek to journal's tail");
102 + goto finalize;
103 + }
104 + }
105 + while (sd_journal_previous(j) > 0) {
106 + row_counter++;
107 +
108 + uint64_t msg_ut;
109 + sd_journal_get_realtime_usec(j, &msg_ut);
110
106 - first_msg_ut = msg_ut;
111 + if(unlikely(!first_msg_ut)) {
112 + if(msg_ut == if_modified_since) {
113 + sd_journal_close(j);
114 + return HTTP_RESP_NOT_MODIFIED;
115 }
116
109 - if (msg_ut < after_ut)
117 + first_msg_ut = msg_ut;
118 + }
119 +
120 + if (msg_ut > before_ut)
121 + continue;
122 +
123 + if (msg_ut < after_ut) {
124 + if(--excess_rows_allowed == 0)
125 break;
126
112 - const void *data;
113 - size_t length;
114 - SD_JOURNAL_FOREACH_DATA(j, data, length) {
115 - const char *key = data;
116 - const char *equal = strchr(key, '=');
117 - if(unlikely(!equal))
118 - continue;
127 + continue;
128 + }
129
120 - const char *value = ++equal;
121 - size_t key_length = value - key; // including '\0'
130 + const void *data;
131 + size_t length;
132 + SD_JOURNAL_FOREACH_DATA(j, data, length) {
133 + const char *key = data;
134 + const char *equal = strchr(key, '=');
135 + if(unlikely(!equal))
136 + continue;
137
123 - char key_copy[key_length];
124 - memcpy(key_copy, key, key_length - 1);
125 - key_copy[key_length - 1] = '\0';
138 + const char *value = ++equal;
139 + size_t key_length = value - key; // including '\0'
140
127 - size_t value_length = length - key_length; // without '\0'
128 - facets_add_key_value_length(facets, key_copy, value, value_length <= FACET_MAX_VALUE_LENGTH ? value_length : FACET_MAX_VALUE_LENGTH);
129 - }
141 + char key_copy[key_length];
142 + memcpy(key_copy, key, key_length - 1);
143 + key_copy[key_length - 1] = '\0';
144
131 - facets_row_finished(facets, msg_ut);
145 + size_t value_length = length - key_length; // without '\0'
146 + facets_add_key_value_length(facets, key_copy, key_length - 1, value, value_length <= FACET_MAX_VALUE_LENGTH ? value_length : FACET_MAX_VALUE_LENGTH);
147 + }
148
133 - if((row_counter % 100) == 0 && now_monotonic_usec() > stop_monotonic_ut) {
134 - timed_out = true;
135 - break;
136 - }
149 + facets_row_finished(facets, msg_ut);
150 +
151 + if((row_counter % 100) == 0 && now_monotonic_usec() > stop_monotonic_ut) {
152 + timed_out = true;
153 + break;
154 }
155 + }
156
157 +finalize:
158 sd_journal_close(j);
159
160 buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
@@ -369,11 +388,11 @@ static void function_systemd_journal(const char *transaction, char *function, ch
388 // register the fields in the order you want them on the dashboard
389
390 facets_register_dynamic_key_name(facets, "ND_JOURNAL_PROCESS",
372 - FACET_KEY_OPTION_NO_FACET | FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS,
391 + FACET_KEY_OPTION_NEVER_FACET | FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS,
392 systemd_journal_dynamic_row_id, NULL);
393
394 facets_register_key_name(facets, "MESSAGE",
376 - FACET_KEY_OPTION_NO_FACET | FACET_KEY_OPTION_MAIN_TEXT | FACET_KEY_OPTION_VISIBLE |
395 + FACET_KEY_OPTION_NEVER_FACET | FACET_KEY_OPTION_MAIN_TEXT | FACET_KEY_OPTION_VISIBLE |
396 FACET_KEY_OPTION_FTS);
397
398 facets_register_key_name_transformation(facets, "PRIORITY", FACET_KEY_OPTION_FACET | FACET_KEY_OPTION_FTS,
@@ -654,6 +673,7 @@ int main(int argc __maybe_unused, char **argv __maybe_unused) {
673
674 if(argc == 2 && strcmp(argv[1], "debug") == 0) {
675 char buf[] = "systemd-journal after:-864000 before:0 last:500";
676 + // char buf[] = "systemd-journal after:1694511062 before:1694514662 anchor:1694514122024403";
677 function_systemd_journal("123", buf, "", 0, 30);
678 exit(1);
679 }
libnetdata/clocks/clocks.h
+4 -4
@@ -16,10 +16,10 @@ struct timespec {
16 typedef int clockid_t;
17 #endif
18
19 -typedef unsigned long long nsec_t;
20 -typedef unsigned long long msec_t;
21 -typedef unsigned long long usec_t;
22 -typedef long long susec_t;
19 +typedef uint64_t nsec_t;
20 +typedef uint64_t msec_t;
21 +typedef uint64_t usec_t;
22 +typedef int64_t susec_t;
23
24 typedef struct heartbeat {
25 usec_t realtime;
libnetdata/facets/facets.c
+152 -31
@@ -31,12 +31,20 @@ inline void facets_string_hash(const char *src, size_t len, char *out) {
31 out[FACET_STRING_HASH_SIZE - 1] = '\0';
32 }
33
34 +static inline void facets_zero_hash(char *out) {
35 + for(int i = 0; i < FACET_STRING_HASH_SIZE ;i++)
36 + out[i] = '0';
37 +
38 + out[FACET_STRING_HASH_SIZE - 1] = '\0';
39 +}
40 +
41 // ----------------------------------------------------------------------------
42
43 typedef struct facet_value {
44 const char *name;
45
46 bool selected;
47 + bool empty;
48
49 uint32_t rows_matching_facet_value;
50 uint32_t final_facet_value_counter;
@@ -46,6 +54,8 @@ typedef struct facet_value {
54 } FACET_VALUE;
55
56 struct facet_key {
57 + FACETS *facets;
58 +
59 const char *name;
60
61 DICTIONARY *values;
@@ -61,9 +71,14 @@ struct facet_key {
71 struct {
72 char hash[FACET_STRING_HASH_SIZE];
73 bool updated;
74 + bool empty;
75 BUFFER *b;
76 } current_value;
77
78 + struct {
79 + FACET_VALUE *empty_value;
80 + } empty_value;
81 +
82 uint32_t order;
83
84 struct {
@@ -117,9 +132,6 @@ struct facets {
132 struct {
133 FACET_ROW *last_added;
134
120 - size_t evaluated;
121 - size_t matched;
122 -
135 size_t first;
136 size_t forwards;
137 size_t backwards;
@@ -128,6 +140,32 @@ struct facets {
140 size_t prepends;
141 size_t appends;
142 size_t shifts;
143 +
144 + struct {
145 + size_t evaluated;
146 + size_t matched;
147 + size_t created;
148 + size_t reused;
149 + } rows;
150 +
151 + struct {
152 + size_t registered;
153 + size_t unique;
154 + } keys;
155 +
156 + struct {
157 + size_t registered;
158 + size_t transformed;
159 + size_t dynamic;
160 + size_t empty;
161 + size_t indexed;
162 + size_t inserts;
163 + size_t conflicts;
164 + } values;
165 +
166 + struct {
167 + size_t searches;
168 + } fts;
169 } operations;
170 };
171
@@ -673,12 +711,12 @@ static inline void facet_value_is_used(FACET_KEY *k, FACET_VALUE *v) {
711 static inline bool facets_key_is_facet(FACETS *facets, FACET_KEY *k) {
712 bool included = true, excluded = false;
713
676 - if(k->options & (FACET_KEY_OPTION_FACET | FACET_KEY_OPTION_NO_FACET)) {
714 + if(k->options & (FACET_KEY_OPTION_FACET | FACET_KEY_OPTION_NO_FACET | FACET_KEY_OPTION_NEVER_FACET)) {
715 if(k->options & FACET_KEY_OPTION_FACET) {
716 included = true;
717 excluded = false;
718 }
681 - else if(k->options & FACET_KEY_OPTION_NO_FACET) {
719 + else if(k->options & (FACET_KEY_OPTION_NO_FACET | FACET_KEY_OPTION_NEVER_FACET)) {
720 included = false;
721 excluded = true;
722 }
@@ -721,6 +759,8 @@ static void facet_value_insert_callback(const DICTIONARY_ITEM *item __maybe_unus
759 v->name = strdupz(v->name);
760 facet_value_is_used(k, v);
761 }
762 +
763 + k->facets->operations.values.inserts++;
764 }
765
766 static bool facet_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data) {
@@ -738,6 +778,8 @@ static bool facet_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_un
778 internal_fatal(v->name && nv->name && strcmp(v->name, nv->name) != 0, "value hash conflict: '%s' and '%s' have the same hash '%s'", v->name, nv->name,
779 dictionary_acquired_item_name(item));
780
781 + k->facets->operations.values.conflicts++;
782 +
783 return false;
784 }
785
@@ -768,6 +810,8 @@ static void facet_key_insert_callback(const DICTIONARY_ITEM *item __maybe_unused
810 FACET_KEY *k = value;
811 FACETS *facets = data;
812
813 + k->facets = facets;
814 +
815 if(!(k->options & FACET_KEY_OPTION_REORDER))
816 k->order = facets->order++;
817
@@ -783,6 +827,9 @@ static void facet_key_insert_callback(const DICTIONARY_ITEM *item __maybe_unused
827 k->current_value.b = buffer_create(0, NULL);
828
829 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(facets->keys_ll, k, prev, next);
830 +
831 + facets->operations.keys.registered++;
832 + facets->operations.keys.unique++;
833 }
834
835 static bool facet_key_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data) {
@@ -804,6 +851,8 @@ static bool facet_key_conflict_callback(const DICTIONARY_ITEM *item __maybe_unus
851 k->options &= ~FACET_KEY_OPTION_REORDER;
852 }
853
854 + facets->operations.keys.registered++;
855 +
856 return false;
857 }
858
@@ -870,17 +919,21 @@ void facets_accepted_param(FACETS *facets, const char *param) {
919 dictionary_set(facets->accepted_params, param, NULL, 0);
920 }
921
873 -inline FACET_KEY *facets_register_key_name(FACETS *facets, const char *key, FACET_KEY_OPTIONS options) {
922 +static inline FACET_KEY *facets_register_key_name_length(FACETS *facets, const char *key, size_t key_length, FACET_KEY_OPTIONS options) {
923 FACET_KEY tk = {
924 .name = key,
925 .options = options,
926 .default_selected_for_values = true,
927 };
928 char hash[FACET_STRING_HASH_SIZE];
880 - facets_string_hash(tk.name, strlen(key), hash);
929 + facets_string_hash(tk.name, key_length, hash);
930 return dictionary_set(facets->keys, hash, &tk, sizeof(tk));
931 }
932
933 +inline FACET_KEY *facets_register_key_name(FACETS *facets, const char *key, FACET_KEY_OPTIONS options) {
934 + return facets_register_key_name_length(facets, key, strlen(key), options);
935 +}
936 +
937 inline FACET_KEY *facets_register_key_name_transformation(FACETS *facets, const char *key, FACET_KEY_OPTIONS options, facets_key_transformer_t cb, void *data) {
938 FACET_KEY *k = facets_register_key_name(facets, key, options);
939 k->transform.cb = cb;
@@ -938,32 +991,60 @@ void facets_register_facet_id_filter(FACETS *facets, const char *key_id, char *v
991 // ----------------------------------------------------------------------------
992
993 static inline void facets_check_value(FACETS *facets __maybe_unused, FACET_KEY *k) {
994 + facets->operations.values.registered++;
995 +
996 if(!k->current_value.updated)
997 buffer_flush(k->current_value.b);
998
944 - if(k->transform.cb)
999 + if(k->transform.cb) {
1000 + facets->operations.values.transformed++;
1001 k->transform.cb(facets, k->current_value.b, k->transform.data);
1002 + }
1003
1004 if(!k->current_value.updated) {
1005 buffer_fast_strcat(k->current_value.b, FACET_VALUE_UNSET, sizeof(FACET_VALUE_UNSET) - 1);
1006 k->current_value.updated = true;
1007 + k->current_value.empty = true;
1008 + facets->operations.values.empty++;
1009 }
1010 + else
1011 + k->current_value.empty = false;
1012
1013 // bool found = false;
1014 // if(strstr(buffer_tostring(k->current_value), "fprintd") != NULL)
1015 // found = true;
1016
956 - if(facets->query && ((k->options & FACET_KEY_OPTION_FTS) || facets->options & FACETS_OPTION_ALL_KEYS_FTS)) {
1017 + if(facets->query && !k->current_value.empty && ((k->options & FACET_KEY_OPTION_FTS) || facets->options & FACETS_OPTION_ALL_KEYS_FTS)) {
1018 + facets->operations.fts.searches++;
1019 if(simple_pattern_matches(facets->query, buffer_tostring(k->current_value.b)))
1020 facets->keys_matched_by_query++;
1021 }
1022
1023 if(k->values) {
962 - FACET_VALUE tk = {
963 - .name = buffer_tostring(k->current_value.b),
964 - };
965 - facets_string_hash(tk.name, buffer_strlen(k->current_value.b), k->current_value.hash);
966 - dictionary_set(k->values, k->current_value.hash, &tk, sizeof(tk));
1024 + if(k->current_value.empty) {
1025 + facets_zero_hash(k->current_value.hash);
1026 +
1027 + FACET_VALUE tk = {
1028 + .name = FACET_VALUE_UNSET,
1029 + };
1030 +
1031 + if(k->empty_value.empty_value) {
1032 + facet_value_conflict_callback(NULL, k->empty_value.empty_value, &tk, k);
1033 + }
1034 + else {
1035 + FACET_VALUE *tkv = dictionary_set(k->values, k->current_value.hash, &tk, sizeof(tk));
1036 + tkv->empty = true;
1037 + k->empty_value.empty_value = tkv;
1038 + }
1039 + }
1040 + else {
1041 + FACET_VALUE tk = {
1042 + .name = buffer_tostring(k->current_value.b),
1043 + };
1044 + facets_string_hash(tk.name, buffer_strlen(k->current_value.b), k->current_value.hash);
1045 + dictionary_set(k->values, k->current_value.hash, &tk, sizeof(tk));
1046 + facets->operations.values.indexed++;
1047 + }
1048 }
1049 else {
1050 k->key_found_in_row++;
@@ -980,8 +1061,8 @@ void facets_add_key_value(FACETS *facets, const char *key, const char *value) {
1061 facets_check_value(facets, k);
1062 }
1063
983 -void facets_add_key_value_length(FACETS *facets, const char *key, const char *value, size_t value_len) {
984 - FACET_KEY *k = facets_register_key_name(facets, key, 0);
1064 +void facets_add_key_value_length(FACETS *facets, const char *key, size_t key_len, const char *value, size_t value_len) {
1065 + FACET_KEY *k = facets_register_key_name_length(facets, key, key_len, 0);
1066 buffer_flush(k->current_value.b);
1067 buffer_strncat(k->current_value.b, value, value_len);
1068 k->current_value.updated = true;
@@ -997,7 +1078,7 @@ static void facet_row_key_value_insert_callback(const DICTIONARY_ITEM *item __ma
1078 FACET_ROW *row = data; (void)row;
1079
1080 rkv->wb = buffer_create(0, NULL);
1000 - buffer_strcat(rkv->wb, rkv->tmp && *rkv->tmp ? rkv->tmp : FACET_VALUE_UNSET);
1081 + buffer_strcat(rkv->wb, rkv->tmp);
1082 }
1083
1084 static bool facet_row_key_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data) {
@@ -1006,7 +1087,7 @@ static bool facet_row_key_value_conflict_callback(const DICTIONARY_ITEM *item __
1087 FACET_ROW *row = data; (void)row;
1088
1089 buffer_flush(rkv->wb);
1009 - buffer_strcat(rkv->wb, n_rkv->tmp && *n_rkv->tmp ? n_rkv->tmp : FACET_VALUE_UNSET);
1090 + buffer_strcat(rkv->wb, n_rkv->tmp);
1091
1092 return false;
1093 }
@@ -1029,14 +1110,17 @@ static void facets_row_free(FACETS *facets __maybe_unused, FACET_ROW *row) {
1110 static FACET_ROW *facets_row_create(FACETS *facets, usec_t usec, FACET_ROW *into) {
1111 FACET_ROW *row;
1112
1032 - if(into)
1113 + if(into) {
1114 row = into;
1115 + facets->operations.rows.reused++;
1116 + }
1117 else {
1118 row = callocz(1, sizeof(FACET_ROW));
1119 row->dict = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE|DICT_OPTION_FIXED_SIZE, NULL, sizeof(FACET_ROW_KEY_VALUE));
1120 dictionary_register_insert_callback(row->dict, facet_row_key_value_insert_callback, row);
1121 dictionary_register_conflict_callback(row->dict, facet_row_key_value_conflict_callback, row);
1122 dictionary_register_delete_callback(row->dict, facet_row_key_value_delete_callback, row);
1123 + facets->operations.rows.created++;
1124 }
1125
1126 row->usec = usec;
@@ -1044,9 +1128,9 @@ static FACET_ROW *facets_row_create(FACETS *facets, usec_t usec, FACET_ROW *into
1128 FACET_KEY *k;
1129 dfe_start_read(facets->keys, k) {
1130 FACET_ROW_KEY_VALUE t = {
1047 - .tmp = (k->current_value.updated && buffer_strlen(k->current_value.b)) ?
1048 - buffer_tostring(k->current_value.b) : FACET_VALUE_UNSET,
1131 + .tmp = (k->current_value.updated) ? buffer_tostring(k->current_value.b) : FACET_VALUE_UNSET,
1132 .wb = NULL,
1133 + .empty = k->current_value.empty,
1134 };
1135 dictionary_set(row->dict, k->name, &t, sizeof(t));
1136 }
@@ -1086,7 +1170,7 @@ static void facets_row_keep_first_entry(FACETS *facets, usec_t usec) {
1170 }
1171
1172 static void facets_row_keep(FACETS *facets, usec_t usec) {
1089 - facets->operations.matched++;
1173 + facets->operations.rows.matched++;
1174
1175 if(facets->anchor.key) {
1176 // we have an anchor key
@@ -1095,14 +1179,16 @@ static void facets_row_keep(FACETS *facets, usec_t usec) {
1179 switch (facets->anchor.direction) {
1180 default:
1181 case FACETS_ANCHOR_DIRECTION_BACKWARD:
1098 - if (usec < facets->anchor.key) {
1182 + // we need to keep only the smaller timestamps
1183 + if (usec >= facets->anchor.key) {
1184 facets->operations.skips_before++;
1185 return;
1186 }
1187 break;
1188
1189 case FACETS_ANCHOR_DIRECTION_FORWARD:
1105 - if (usec > facets->anchor.key) {
1190 + // we need to keep only the bigger timestamps
1191 + if (usec <= facets->anchor.key) {
1192 facets->operations.skips_after++;
1193 return;
1194 }
@@ -1182,6 +1268,7 @@ void facets_rows_begin(FACETS *facets) {
1268 k->key_found_in_row = 0;
1269 k->key_values_selected_in_row = 0;
1270 k->current_value.updated = false;
1271 + k->current_value.empty = false;
1272 k->current_value.hash[0] = '\0';
1273 }
1274 // dfe_done(k);
@@ -1193,7 +1280,7 @@ void facets_row_finished(FACETS *facets, usec_t usec) {
1280 if(facets->query && facets->keys_filtered_by_query && !facets->keys_matched_by_query)
1281 goto cleanup;
1282
1196 - facets->operations.evaluated++;
1283 + facets->operations.rows.evaluated++;
1284
1285 uint32_t total_keys = 0;
1286 uint32_t selected_by = 0;
@@ -1375,7 +1462,7 @@ void facets_report(FACETS *facets, BUFFER *wb) {
1462 RRDF_FIELD_SORT_ASCENDING,
1463 NULL,
1464 RRDF_FIELD_SUMMARY_COUNT,
1378 - k->values ? RRDF_FIELD_FILTER_FACET : RRDF_FIELD_FILTER_NONE,
1465 + (k->options & FACET_KEY_OPTION_NEVER_FACET) ? RRDF_FIELD_FILTER_NONE : RRDF_FIELD_FILTER_FACET,
1466 options,
1467 FACET_VALUE_UNSET);
1468 }
@@ -1399,9 +1486,14 @@ void facets_report(FACETS *facets, BUFFER *wb) {
1486 rkv = dictionary_set(row->dict, k->name, NULL, sizeof(*rkv));
1487
1488 k->dynamic.cb(facets, wb, rkv, row, k->dynamic.data);
1489 + facets->operations.values.dynamic++;
1490 + }
1491 + else {
1492 + if(!rkv || rkv->empty)
1493 + buffer_json_add_array_item_string(wb, FACET_VALUE_UNSET);
1494 + else
1495 + buffer_json_add_array_item_string(wb, buffer_tostring(rkv->wb));
1496 }
1403 - else
1404 - buffer_json_add_array_item_string(wb, rkv ? buffer_tostring(rkv->wb) : FACET_VALUE_UNSET);
1497 }
1498 dfe_done(k);
1499 buffer_json_array_close(wb); // each row
@@ -1458,8 +1550,8 @@ void facets_report(FACETS *facets, BUFFER *wb) {
1550
1551 buffer_json_member_add_object(wb, "items");
1552 {
1461 - buffer_json_member_add_uint64(wb, "evaluated", facets->operations.evaluated);
1462 - buffer_json_member_add_uint64(wb, "matched", facets->operations.matched);
1553 + buffer_json_member_add_uint64(wb, "evaluated", facets->operations.rows.evaluated);
1554 + buffer_json_member_add_uint64(wb, "matched", facets->operations.rows.matched);
1555 buffer_json_member_add_uint64(wb, "returned", facets->items_to_return);
1556 buffer_json_member_add_uint64(wb, "max_to_return", facets->max_items_to_return);
1557 buffer_json_member_add_uint64(wb, "before", facets->operations.skips_before);
@@ -1477,7 +1569,36 @@ void facets_report(FACETS *facets, BUFFER *wb) {
1569 buffer_json_member_add_uint64(wb, "prepends", facets->operations.prepends);
1570 buffer_json_member_add_uint64(wb, "appends", facets->operations.appends);
1571 buffer_json_member_add_uint64(wb, "shifts", facets->operations.shifts);
1572 + buffer_json_member_add_object(wb, "rows");
1573 + {
1574 + buffer_json_member_add_uint64(wb, "created", facets->operations.rows.created);
1575 + buffer_json_member_add_uint64(wb, "reused", facets->operations.rows.reused);
1576 + buffer_json_member_add_uint64(wb, "evaluated", facets->operations.rows.evaluated);
1577 + buffer_json_member_add_uint64(wb, "matched", facets->operations.rows.matched);
1578 + }
1579 + buffer_json_object_close(wb); // rows
1580 + buffer_json_member_add_object(wb, "keys");
1581 + {
1582 + buffer_json_member_add_uint64(wb, "registered", facets->operations.keys.registered);
1583 + buffer_json_member_add_uint64(wb, "unique", facets->operations.keys.unique);
1584 + }
1585 + buffer_json_object_close(wb); // keys
1586 + buffer_json_member_add_object(wb, "values");
1587 + {
1588 + buffer_json_member_add_uint64(wb, "registered", facets->operations.values.registered);
1589 + buffer_json_member_add_uint64(wb, "transformed", facets->operations.values.transformed);
1590 + buffer_json_member_add_uint64(wb, "dynamic", facets->operations.values.dynamic);
1591 + buffer_json_member_add_uint64(wb, "empty", facets->operations.values.empty);
1592 + buffer_json_member_add_uint64(wb, "indexed", facets->operations.values.indexed);
1593 + buffer_json_member_add_uint64(wb, "inserts", facets->operations.values.inserts);
1594 + buffer_json_member_add_uint64(wb, "conflicts", facets->operations.values.conflicts);
1595 + }
1596 + buffer_json_object_close(wb); // values
1597 + buffer_json_member_add_object(wb, "fts");
1598 + {
1599 + buffer_json_member_add_uint64(wb, "searches", facets->operations.fts.searches);
1600 + }
1601 + buffer_json_object_close(wb); // fts
1602 }
1603 buffer_json_object_close(wb); // items
1482 -
1604 }
libnetdata/facets/facets.h
+10 -8
@@ -11,18 +11,20 @@ typedef enum __attribute__((packed)) {
11 } FACETS_ANCHOR_DIRECTION;
12
13 typedef enum __attribute__((packed)) {
14 - FACET_KEY_OPTION_FACET = (1 << 0), // filterable values
15 - FACET_KEY_OPTION_NO_FACET = (1 << 1), // non-filterable value
16 - FACET_KEY_OPTION_STICKY = (1 << 2), // should be sticky in the table
17 - FACET_KEY_OPTION_VISIBLE = (1 << 3), // should be in the default table
18 - FACET_KEY_OPTION_FTS = (1 << 4), // the key is filterable by full text search (FTS)
19 - FACET_KEY_OPTION_MAIN_TEXT = (1 << 5), // full width and wrap
20 - FACET_KEY_OPTION_REORDER = (1 << 6), // give the key a new order id on first encounter
14 + FACET_KEY_OPTION_FACET = (1 << 0), // filterable values
15 + FACET_KEY_OPTION_NO_FACET = (1 << 1), // non-filterable value
16 + FACET_KEY_OPTION_NEVER_FACET = (1 << 2), // never enable this field as facet
17 + FACET_KEY_OPTION_STICKY = (1 << 3), // should be sticky in the table
18 + FACET_KEY_OPTION_VISIBLE = (1 << 4), // should be in the default table
19 + FACET_KEY_OPTION_FTS = (1 << 5), // the key is filterable by full text search (FTS)
20 + FACET_KEY_OPTION_MAIN_TEXT = (1 << 6), // full width and wrap
21 + FACET_KEY_OPTION_REORDER = (1 << 7), // give the key a new order id on first encounter
22 } FACET_KEY_OPTIONS;
23
24 typedef struct facet_row_key_value {
25 const char *tmp;
26 BUFFER *wb;
27 + bool empty;
28 } FACET_ROW_KEY_VALUE;
29
30 typedef struct facet_row {
@@ -64,7 +66,7 @@ void facets_register_facet_id_filter(FACETS *facets, const char *key_id, char *v
66 void facets_set_histogram(FACETS *facets, const char *chart, usec_t after_ut, usec_t before_ut);
67
68 void facets_add_key_value(FACETS *facets, const char *key, const char *value);
67 -void facets_add_key_value_length(FACETS *facets, const char *key, const char *value, size_t value_len);
69 +void facets_add_key_value_length(FACETS *facets, const char *key, size_t key_len, const char *value, size_t value_len);
70
71 void facets_report(FACETS *facets, BUFFER *wb);
72 void facets_accepted_parameters_to_json_array(FACETS *facets, BUFFER *wb, bool with_keys);