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);