@cryptotaxi247 / netdata-1 / commits / 2084dc70e

Faster facets (#16190)

* do not copy values, unless needed * linear probing hashtables * abstract hashtable implementation to avoid dereferencing pointers to compare the hash * use 2 hashes and linear probing on hashtable collisions * turn bool flags into an enum, to speed up reseting between rows

Costa Tsaousis committed Oct 14, 2023 at 13:20 UTC 2084dc70e15790f04dee9d8e0dda6b3cc1c5c768
3 files changed +204 -152
collectors/systemd-journal.plugin/systemd-journal.c
+1 -1
@@ -2734,7 +2734,7 @@ int main(int argc __maybe_unused, char **argv __maybe_unused) {
2734
2735 if(argc == 2 && strcmp(argv[1], "debug") == 0) {
2736 bool cancelled = false;
2737 - char buf[] = "systemd-journal after:-8000000 before:0 last:1";
2737 + char buf[] = "systemd-journal after:-16000000 before:0 last:1";
2738 // char buf[] = "systemd-journal after:1695332964 before:1695937764 direction:backward last:100 slice:true source:all DHKucpqUoe1:PtVoyIuX.MU";
2739 // char buf[] = "systemd-journal after:1694511062 before:1694514662 anchor:1694514122024403";
2740 function_systemd_journal("123", buf, 600, &cancelled);
libnetdata/facets/facets.c
+201 -150
@@ -5,8 +5,8 @@
5 #define FACETS_KEYS_WITH_VALUES_MAX 200 // the max number of keys that can be facets
6 #define FACETS_KEYS_IN_ROW_MAX 500 // the max number of keys in a row
7
8 -#define FACETS_KEYS_HASHTABLE_ENTRIES 128
9 -#define FACETS_VALUES_HASHTABLE_ENTRIES 32
8 +#define FACETS_KEYS_HASHTABLE_ENTRIES 127
9 +#define FACETS_VALUES_HASHTABLE_ENTRIES 31
10
11 // ----------------------------------------------------------------------------
12
@@ -98,11 +98,74 @@ static inline bool is_valid_string_hash(const char *s) {
98 return true;
99 }
100
101 +// ----------------------------------------------------------------------------
102 +
103 +typedef uint64_t SIMPLE_HASHTABLE_HASH;
104 +#define SIMPLE_HASHTABLE_HASH_SECOND_HASH_SHIFTS 32
105 +
106 +typedef struct simple_hashtable_slot {
107 + SIMPLE_HASHTABLE_HASH hash;
108 + void *data;
109 +} SIMPLE_HASHTABLE_SLOT;
110 +
111 +typedef struct simple_hashtable {
112 + size_t size;
113 + SIMPLE_HASHTABLE_SLOT *hashtable;
114 +} SIMPLE_HASHTABLE;
115 +
116 +static void simple_hashtable_init(SIMPLE_HASHTABLE *ht, size_t size) {
117 + ht->size = size;
118 + ht->hashtable = callocz(ht->size, sizeof(*ht->hashtable));
119 +}
120 +
121 +static void simple_hashtable_free(SIMPLE_HASHTABLE *ht) {
122 + freez(ht->hashtable);
123 + ht->hashtable = NULL;
124 + ht->size = 0;
125 +}
126 +
127 +static inline SIMPLE_HASHTABLE_SLOT *simple_hashtable_get_slot(SIMPLE_HASHTABLE *ht, SIMPLE_HASHTABLE_HASH hash) {
128 + // IMPORTANT:
129 + // If the hashtable supported deletions, we would need to have a special slot.data value
130 + // to mark deleted values and assume they are occupied during lookup, but empty during insert.
131 + // But for our case, we don't need it, since we never delete items from the hashtable.
132 +
133 + size_t slot = hash % ht->size;
134 + if(!ht->hashtable[slot].data || ht->hashtable[slot].hash == hash)
135 + return &ht->hashtable[slot];
136 +
137 + slot = ((hash >> SIMPLE_HASHTABLE_HASH_SECOND_HASH_SHIFTS) + 1) % ht->size;
138 + // Linear probing until we find it
139 + while (ht->hashtable[slot].data && ht->hashtable[slot].hash != hash)
140 + slot = (slot + 1) % ht->size; // Wrap around if necessary
141 +
142 + return &ht->hashtable[slot];
143 +}
144 +
145 +static void simple_hashtable_resize_double(SIMPLE_HASHTABLE *ht) {
146 + SIMPLE_HASHTABLE_SLOT *old = ht->hashtable;
147 + size_t old_size = ht->size;
148 +
149 + ht->size = (ht->size * 2) + 1;
150 + ht->hashtable = callocz(ht->size, sizeof(*ht->hashtable));
151 + for(size_t i = 0 ; i < old_size ; i++) {
152 + if(!old[i].data)
153 + continue;
154 +
155 + SIMPLE_HASHTABLE_SLOT *slot = simple_hashtable_get_slot(ht, old[i].hash);
156 + *slot = old[i];
157 + }
158 +
159 + freez(old);
160 +}
161 +
162 +
163 // ----------------------------------------------------------------------------
164
165 typedef struct facet_value {
166 FACETS_HASH hash;
167 const char *name;
168 + uint32_t name_len;
169
170 bool selected;
171 bool empty;
@@ -114,13 +177,19 @@ typedef struct facet_value {
177 uint32_t *histogram;
178 uint32_t min, max, sum;
179
117 - struct {
118 - struct facet_value *next;
119 - } parent_hashtable;
120 -
180 struct facet_value *prev, *next;
181 } FACET_VALUE;
182
183 +typedef enum {
184 + FACET_KEY_VALUE_NONE = 0,
185 + FACET_KEY_VALUE_UPDATED = (1 << 0),
186 + FACET_KEY_VALUE_EMPTY = (1 << 1),
187 + FACET_KEY_VALUE_COPIED = (1 << 2),
188 +} FACET_KEY_VALUE_FLAGS;
189 +
190 +#define facet_key_value_updated(k) ((k)->current_value.flags & FACET_KEY_VALUE_UPDATED)
191 +#define facet_key_value_empty(k) ((k)->current_value.flags & FACET_KEY_VALUE_EMPTY)
192 +#define facet_key_value_copied(k) ((k)->current_value.flags & FACET_KEY_VALUE_COPIED)
193
194 struct facet_key {
195 FACETS *facets;
@@ -139,16 +208,16 @@ struct facet_key {
208
209 struct {
210 bool enabled;
142 - uint32_t size;
211 uint32_t used;
144 - FACET_VALUE **hashtable;
212 FACET_VALUE *ll;
213 + SIMPLE_HASHTABLE ht;
214 } values;
215
216 struct {
217 FACETS_HASH hash;
150 - bool updated;
151 - bool empty;
218 + FACET_KEY_VALUE_FLAGS flags;
219 + const char *raw;
220 + uint32_t raw_len;
221 BUFFER *b;
222 FACET_VALUE *v;
223 } current_value;
@@ -168,10 +237,6 @@ struct facet_key {
237 void *data;
238 } transform;
239
171 - struct {
172 - struct facet_key *next;
173 - } parent_hashtable;
174 -
240 struct facet_key *prev, *next;
241 };
242
@@ -195,9 +260,8 @@ struct facets {
260
261 struct {
262 size_t count;
198 - size_t size;
199 - FACET_KEY **hashtable;
263 FACET_KEY *ll;
264 + SIMPLE_HASHTABLE ht;
265 } keys;
266
267 struct {
@@ -321,8 +385,7 @@ static inline bool facets_key_is_facet(FACETS *facets, FACET_KEY *k);
385 static inline void FACETS_VALUES_INDEX_CREATE(FACET_KEY *k) {
386 k->values.ll = NULL;
387 k->values.used = 0;
324 - k->values.size = FACETS_VALUES_HASHTABLE_ENTRIES;
325 - k->values.hashtable = callocz(k->values.size, sizeof(FACET_VALUE *));
388 + simple_hashtable_init(&k->values.ht, FACETS_VALUES_HASHTABLE_ENTRIES);
389 }
390
391 static inline void FACETS_VALUES_INDEX_DESTROY(FACET_KEY *k) {
@@ -335,62 +398,67 @@ static inline void FACETS_VALUES_INDEX_DESTROY(FACET_KEY *k) {
398 v = next;
399 }
400 k->values.ll = NULL;
338 - k->values.size = 0;
401 k->values.used = 0;
340 - freez(k->values.hashtable);
402 k->values.enabled = false;
403 +
404 + simple_hashtable_free(&k->values.ht);
405 +}
406 +
407 +static inline const char *facets_key_get_value(FACET_KEY *k) {
408 + return facet_key_value_copied(k) ? buffer_tostring(k->current_value.b) : k->current_value.raw;
409 +}
410 +
411 +static inline uint32_t facets_key_get_value_length(FACET_KEY *k) {
412 + return facet_key_value_copied(k) ? buffer_strlen(k->current_value.b) : k->current_value.raw_len;
413 +}
414 +
415 +static inline void facets_key_value_copy_to_buffer(FACET_KEY *k) {
416 + if(!facet_key_value_copied(k)) {
417 + buffer_contents_replace(k->current_value.b, k->current_value.raw, k->current_value.raw_len);
418 + k->current_value.flags |= FACET_KEY_VALUE_COPIED;
419 + }
420 +}
421 +
422 +static const char *facets_value_dup(const char *v, uint32_t len) {
423 + char *s = mallocz(len + 1);
424 +
425 + if(len)
426 + memcpy(s, v, len);
427 +
428 + s[len] = '\0';
429 +
430 + return s;
431 }
432
433 static inline void FACET_VALUE_ADD_CONFLICT(FACET_KEY *k, FACET_VALUE *v, const FACET_VALUE * const nv) {
345 - if(!v->name && nv->name)
434 + if(!v->name && nv->name && nv->name_len) {
435 // an actual value, not a filter
347 - v->name = strdupz(nv->name);
436 + v->name = facets_value_dup(nv->name, nv->name_len);
437 + v->name_len = nv->name_len;
438 + }
439
349 - if(v->name)
440 + if(v->name && v->name_len)
441 facet_value_is_used(k, v);
442
352 - internal_fatal(v->name && nv->name && strcmp(v->name, nv->name) != 0,
443 + internal_fatal(v->name && nv->name && v->name_len == nv->name_len && memcmp(v->name, nv->name, v->name_len) != 0,
444 "value hash conflict: '%s' and '%s' have the same hash '%s'",
445 v->name, nv->name, hash_to_static_string(v->hash));
446
447 k->facets->operations.values.conflicts++;
448 }
449
359 -static inline FACET_VALUE **facets_values_hashtable_slot(FACET_KEY *k, FACETS_HASH hash) {
360 - size_t slot = hash % k->values.size;
361 - FACET_VALUE **v = &k->values.hashtable[slot];
362 -
363 - while(*v && (*v)->hash != hash)
364 - v = &((*v)->parent_hashtable.next);
365 -
366 - return v;
367 -}
368 -
450 static inline FACET_VALUE *FACET_VALUE_GET_FROM_INDEX(FACET_KEY *k, FACETS_HASH hash) {
370 - FACET_VALUE **v_ptr = facets_values_hashtable_slot(k, hash);
371 - return *v_ptr;
372 -}
373 -
374 -static void FACET_VALUES_HASHTABLE_DOUBLE(FACET_KEY *k) {
375 - // increase the hashtable size
376 - freez(k->values.hashtable);
377 - k->values.size *= 2;
378 - k->values.hashtable = callocz(k->values.size, sizeof(FACET_VALUE *));
379 - for(FACET_VALUE *v = k->values.ll ; v ;v = v->next) {
380 - FACET_VALUE **v_ptr = facets_values_hashtable_slot(k, v->hash);
381 - *v_ptr = v;
382 - v->parent_hashtable.next = NULL;
383 - }
384 - k->facets->operations.values.hashtable_increases++;
451 + SIMPLE_HASHTABLE_SLOT *slot = simple_hashtable_get_slot(&k->values.ht, hash);
452 + return slot->data;
453 }
454
455 static inline FACET_VALUE *FACET_VALUE_ADD_TO_INDEX(FACET_KEY *k, const FACET_VALUE * const tv) {
388 - FACET_VALUE **v_ptr = facets_values_hashtable_slot(k, tv->hash);
456 + SIMPLE_HASHTABLE_SLOT *slot = simple_hashtable_get_slot(&k->values.ht, tv->hash);
457
390 - if(*v_ptr) {
458 + if(slot->data) {
459 // already exists
460
393 - FACET_VALUE *v = *v_ptr;
461 + FACET_VALUE *v = slot->data;
462 FACET_VALUE_ADD_CONFLICT(k, v, tv);
463 return v;
464 }
@@ -398,28 +466,29 @@ static inline FACET_VALUE *FACET_VALUE_ADD_TO_INDEX(FACET_KEY *k, const FACET_VA
466 // we have to add it
467
468 FACET_VALUE *v = mallocz(sizeof(*v));
401 - *v_ptr = v;
469 + slot->hash = tv->hash;
470 + slot->data = v;
471
472 memcpy(v, tv, sizeof(*v));
473
405 - v->parent_hashtable.next = NULL; // make sure this is NULL
406 -
474 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(k->values.ll, v, prev, next);
475 k->values.used++;
476
477 if(!v->selected)
478 v->selected = k->default_selected_for_values;
479
413 - if(v->name) {
480 + if(v->name && v->name_len) {
481 // an actual value, not a filter
415 - v->name = strdupz(v->name);
482 + v->name = facets_value_dup(v->name, v->name_len);
483 facet_value_is_used(k, v);
484 }
485
486 k->facets->operations.values.inserts++;
487
421 - if(unlikely(k->values.used > k->values.size / 2))
422 - FACET_VALUES_HASHTABLE_DOUBLE(k);
488 + if(unlikely(k->values.used > k->values.ht.size / 2)) {
489 + simple_hashtable_resize_double(&k->values.ht);
490 + k->facets->operations.values.hashtable_increases++;
491 + }
492
493 return v;
494 }
@@ -428,6 +497,7 @@ static inline void FACET_VALUE_ADD_EMPTY_VALUE_TO_INDEX(FACET_KEY *k) {
497 static const FACET_VALUE tv = {
498 .hash = FACETS_HASH_ZERO,
499 .name = FACET_VALUE_UNSET,
500 + .name_len = sizeof(FACET_VALUE_UNSET) - 1,
501 };
502
503 k->current_value.hash = FACETS_HASH_ZERO;
@@ -447,8 +517,9 @@ static inline void FACET_VALUE_ADD_EMPTY_VALUE_TO_INDEX(FACET_KEY *k) {
517 static inline void FACET_VALUE_ADD_CURRENT_VALUE_TO_INDEX(FACET_KEY *k) {
518 static __thread FACET_VALUE tv = { 0 };
519
450 - tv.hash = FACETS_HASH_FUNCTION(buffer_tostring(k->current_value.b), buffer_strlen(k->current_value.b));
451 - tv.name = buffer_tostring(k->current_value.b);
520 + tv.name = facets_key_get_value(k);
521 + tv.name_len = facets_key_get_value_length(k);
522 + tv.hash = FACETS_HASH_FUNCTION(tv.name, tv.name_len);
523
524 k->current_value.v = FACET_VALUE_ADD_TO_INDEX(k, &tv);
525 k->facets->operations.values.indexed++;
@@ -462,13 +533,6 @@ static inline void FACET_VALUE_ADD_OR_UPDATE_SELECTED(FACET_KEY *k, FACETS_HASH
533 FACET_VALUE_ADD_TO_INDEX(k, &tv);
534 }
535
465 -static inline FACET_VALUE *FACET_VALUE_GET_CURRENT_VALUE(FACET_KEY *k) {
466 - if(unlikely(!k->current_value.v))
467 - FACET_VALUE_ADD_EMPTY_VALUE_TO_INDEX(k);
468 -
469 - return k->current_value.v;
470 -}
471 -
536 // ----------------------------------------------------------------------------
537 // The FACET_KEY index within each FACET
538
@@ -491,10 +555,10 @@ static inline void facet_key_late_init(FACETS *facets, FACET_KEY *k) {
555
556 static inline void FACETS_KEYS_INDEX_CREATE(FACETS *facets) {
557 facets->keys.ll = NULL;
494 - facets->keys.size = FACETS_KEYS_HASHTABLE_ENTRIES;
495 - facets->keys.hashtable = callocz(facets->keys.size, sizeof(FACET_KEY *));
558 facets->keys.count = 0;
559 facets->keys_with_values.used = 0;
560 +
561 + simple_hashtable_init(&facets->keys.ht, FACETS_KEYS_HASHTABLE_ENTRIES);
562 }
563
564 static inline void FACETS_KEYS_INDEX_DESTROY(FACETS *facets) {
@@ -509,40 +573,16 @@ static inline void FACETS_KEYS_INDEX_DESTROY(FACETS *facets) {
573
574 k = next;
575 }
512 - freez(facets->keys.hashtable);
513 - facets->keys.hashtable = NULL;
514 - facets->keys.size = 0;
576 facets->keys.ll = NULL;
577 facets->keys.count = 0;
578 facets->keys_with_values.used = 0;
518 -}
579
520 -static inline FACET_KEY **facets_keys_hashtable_slot(FACETS *facets, FACETS_HASH hash) {
521 - size_t slot = hash % facets->keys.size;
522 - FACET_KEY **k = &facets->keys.hashtable[slot];
523 -
524 - while(*k && (*k)->hash != hash)
525 - k = &((*k)->parent_hashtable.next);
526 -
527 - return k;
528 -}
529 -
530 -static void FACET_KEYS_HASHTABLE_DOUBLE(FACETS *facets) {
531 - // increase the hashtable size
532 - freez(facets->keys.hashtable);
533 - facets->keys.size *= 2;
534 - facets->keys.hashtable = callocz(facets->keys.size, sizeof(FACET_KEY *));
535 - for(FACET_KEY *k = facets->keys.ll ; k ; k = k->next) {
536 - FACET_KEY **k_ptr = facets_keys_hashtable_slot(facets, k->hash);
537 - *k_ptr = k;
538 - k->parent_hashtable.next = NULL;
539 - }
540 - facets->operations.keys.hashtable_increases++;
580 + simple_hashtable_free(&facets->keys.ht);
581 }
582
583 static inline FACET_KEY *FACETS_KEY_GET_FROM_INDEX(FACETS *facets, FACETS_HASH hash) {
544 - FACET_KEY **k = facets_keys_hashtable_slot(facets, hash);
545 - return *k;
584 + SIMPLE_HASHTABLE_SLOT *slot = simple_hashtable_get_slot(&facets->keys.ht, hash);
585 + return slot->data;
586 }
587
588 bool facets_key_name_value_length_is_selected(FACETS *facets, const char *key, size_t key_length, const char *value, size_t value_length) {
@@ -565,16 +605,12 @@ void facets_add_possible_value_name_to_key(FACETS *facets, const char *key, size
605 FACET_VALUE *v = FACET_VALUE_GET_FROM_INDEX(k, hash);
606 if(v && v->name) return;
607
568 - BUFFER *wb = buffer_create(0, NULL);
569 - buffer_contents_replace(wb, value, value_length);
570 -
608 FACET_VALUE tv = {
609 .hash = hash,
573 - .name = buffer_tostring(wb),
610 + .name = value,
611 + .name_len = value_length,
612 };
613 FACET_VALUE_ADD_TO_INDEX(k, &tv);
576 -
577 - buffer_free(wb);
614 }
615
616 static void facet_key_set_name(FACET_KEY *k, const char *name, size_t name_length) {
@@ -582,7 +618,7 @@ static void facet_key_set_name(FACET_KEY *k, const char *name, size_t name_lengt
618 "key hash conflict: '%s' and '%s' have the same hash",
619 k->name, name);
620
585 - if(k->name || !name || !name_length)
621 + if(likely(k->name || !name || !name_length))
622 return;
623
624 // an actual value, not a filter
@@ -625,21 +661,26 @@ static inline FACET_KEY *FACETS_KEY_CREATE(FACETS *facets, FACETS_HASH hash, con
661 static inline FACET_KEY *FACETS_KEY_ADD_TO_INDEX(FACETS *facets, FACETS_HASH hash, const char *name, size_t name_length, FACET_KEY_OPTIONS options) {
662 facets->operations.keys.registered++;
663
628 - FACET_KEY **k_ptr = facets_keys_hashtable_slot(facets, hash);
664 + SIMPLE_HASHTABLE_SLOT *slot = simple_hashtable_get_slot(&facets->keys.ht, hash);
665
630 - if(unlikely(!(*k_ptr))) {
666 + if(unlikely(!slot->data)) {
667 // we have to add it
632 - FACET_KEY *k = *k_ptr = FACETS_KEY_CREATE(facets, hash, name, name_length, options);
668 + FACET_KEY *k = FACETS_KEY_CREATE(facets, hash, name, name_length, options);
669 +
670 + slot->hash = hash;
671 + slot->data = k;
672
634 - if(facets->keys.count > facets->keys.size / 2)
635 - FACET_KEYS_HASHTABLE_DOUBLE(facets);
673 + if(facets->keys.count > facets->keys.ht.size / 2) {
674 + simple_hashtable_resize_double(&facets->keys.ht);
675 + facets->operations.keys.hashtable_increases++;
676 + }
677
678 return k;
679 }
680
681 // already in the index
682
642 - FACET_KEY *k = *k_ptr;
683 + FACET_KEY *k = slot->data;
684
685 facet_key_set_name(k, name, name_length);
686
@@ -742,7 +783,7 @@ static inline void facets_histogram_update_value(FACETS *facets, usec_t usec) {
783 if(!facets->histogram.enabled ||
784 !facets->histogram.key ||
785 !facets->histogram.key->values.enabled ||
745 - !facets->histogram.key->current_value.v ||
786 + !facet_key_value_updated(facets->histogram.key) ||
787 usec < facets->histogram.after_ut ||
788 usec > facets->histogram.before_ut)
789 return;
@@ -786,8 +827,7 @@ static inline void facets_histogram_value_names(BUFFER *wb, FACETS *facets __may
827 if(!tb)
828 tb = buffer_create(0, NULL);
829
789 - buffer_flush(tb);
790 - buffer_strcat(tb, v->name);
830 + buffer_contents_replace(tb, v->name, v->name_len);
831 k->transform.cb(facets, tb, FACETS_TRANSFORM_HISTOGRAM, k->transform.data);
832 buffer_json_add_array_item_string(wb, buffer_tostring(tb));
833 }
@@ -1477,19 +1517,20 @@ void facets_set_additional_options(FACETS *facets, FACETS_OPTIONS options) {
1517 // ----------------------------------------------------------------------------
1518
1519 static inline void facets_key_set_empty_value(FACETS *facets, FACET_KEY *k) {
1480 - if(likely(!k->current_value.updated && facets->keys_in_row.used < FACETS_KEYS_IN_ROW_MAX))
1520 + if(likely(!facet_key_value_updated(k) && facets->keys_in_row.used < FACETS_KEYS_IN_ROW_MAX))
1521 facets->keys_in_row.array[facets->keys_in_row.used++] = k;
1522
1483 - k->current_value.updated = true;
1484 - k->current_value.empty = true;
1523 + k->current_value.flags |= FACET_KEY_VALUE_UPDATED | FACET_KEY_VALUE_EMPTY;
1524
1525 facets->operations.values.registered++;
1526 facets->operations.values.empty++;
1527
1528 // no need to copy the UNSET value
1529 // empty values are exported as empty
1530 + k->current_value.raw = NULL;
1531 + k->current_value.raw_len = 0;
1532 k->current_value.b->len = 0;
1492 - // buffer_contents_replace(k->current_value.b, FACET_VALUE_UNSET, sizeof(FACET_VALUE_UNSET) - 1);
1533 + k->current_value.flags &= ~FACET_KEY_VALUE_COPIED;
1534
1535 if(unlikely(k->values.enabled))
1536 FACET_VALUE_ADD_EMPTY_VALUE_TO_INDEX(k);
@@ -1500,16 +1541,17 @@ static inline void facets_key_set_empty_value(FACETS *facets, FACET_KEY *k) {
1541 }
1542
1543 static inline void facets_key_check_value(FACETS *facets, FACET_KEY *k) {
1503 - if(likely(!k->current_value.updated && facets->keys_in_row.used < FACETS_KEYS_IN_ROW_MAX))
1544 + if(likely(!facet_key_value_updated(k) && facets->keys_in_row.used < FACETS_KEYS_IN_ROW_MAX))
1545 facets->keys_in_row.array[facets->keys_in_row.used++] = k;
1546
1506 - k->current_value.updated = true;
1507 - k->current_value.empty = false;
1547 + k->current_value.flags |= FACET_KEY_VALUE_UPDATED;
1548 + k->current_value.flags &= ~FACET_KEY_VALUE_EMPTY;
1549
1550 facets->operations.values.registered++;
1551
1552 if(k->transform.cb && !k->transform.view_only) {
1553 facets->operations.values.transformed++;
1554 + facets_key_value_copy_to_buffer(k);
1555 k->transform.cb(facets, k->current_value.b, FACETS_TRANSFORM_VALUE, k->transform.data);
1556 }
1557
@@ -1517,8 +1559,9 @@ static inline void facets_key_check_value(FACETS *facets, FACET_KEY *k) {
1559 // if(strstr(buffer_tostring(k->current_value), "fprintd") != NULL)
1560 // found = true;
1561
1520 - if(facets->query && !k->current_value.empty && ((k->options & FACET_KEY_OPTION_FTS) || facets->options & FACETS_OPTION_ALL_KEYS_FTS)) {
1562 + if(facets->query && !facet_key_value_empty(k) && ((k->options & FACET_KEY_OPTION_FTS) || facets->options & FACETS_OPTION_ALL_KEYS_FTS)) {
1563 facets->operations.fts.searches++;
1564 + facets_key_value_copy_to_buffer(k);
1565 if(simple_pattern_matches(facets->query, buffer_tostring(k->current_value.b)))
1566 facets->current_row.keys_matched_by_query++;
1567 }
@@ -1533,15 +1576,16 @@ static inline void facets_key_check_value(FACETS *facets, FACET_KEY *k) {
1576
1577 void facets_add_key_value(FACETS *facets, const char *key, const char *value) {
1578 FACET_KEY *k = facets_register_key_name(facets, key, 0);
1536 - buffer_flush(k->current_value.b);
1537 - buffer_strcat(k->current_value.b, value);
1579 + k->current_value.raw = value;
1580 + k->current_value.raw_len = strlen(value);
1581
1582 facets_key_check_value(facets, k);
1583 }
1584
1585 void facets_add_key_value_length(FACETS *facets, const char *key, size_t key_len, const char *value, size_t value_len) {
1586 FACET_KEY *k = facets_register_key_name_length(facets, key, key_len, 0);
1544 - buffer_contents_replace(k->current_value.b, value, value_len);
1587 + k->current_value.raw = value;
1588 + k->current_value.raw_len = value_len;
1589
1590 facets_key_check_value(facets, k);
1591 }
@@ -1555,7 +1599,7 @@ static void facet_row_key_value_insert_callback(const DICTIONARY_ITEM *item __ma
1599
1600 rkv->wb = buffer_create(0, NULL);
1601 if(!rkv->empty)
1558 - buffer_strcat(rkv->wb, rkv->tmp);
1602 + buffer_contents_replace(rkv->wb, rkv->tmp, rkv->tmp_len);
1603 }
1604
1605 static bool facet_row_key_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data) {
@@ -1565,9 +1609,10 @@ static bool facet_row_key_value_conflict_callback(const DICTIONARY_ITEM *item __
1609
1610 rkv->empty = n_rkv->empty;
1611
1568 - buffer_flush(rkv->wb);
1612 if(!rkv->empty)
1570 - buffer_strcat(rkv->wb, n_rkv->tmp);
1613 + buffer_contents_replace(rkv->wb, n_rkv->tmp, n_rkv->tmp_len);
1614 + else
1615 + buffer_flush(rkv->wb);
1616
1617 return false;
1618 }
@@ -1609,10 +1654,18 @@ static FACET_ROW *facets_row_create(FACETS *facets, usec_t usec, FACET_ROW *into
1654 FACET_KEY *k;
1655 foreach_key_in_facets(facets, k) {
1656 FACET_ROW_KEY_VALUE t = {
1612 - .tmp = (k->current_value.updated && !k->current_value.empty) ? buffer_tostring(k->current_value.b) : NULL,
1657 + .tmp = NULL,
1658 + .tmp_len = 0,
1659 .wb = NULL,
1614 - .empty = !k->current_value.updated || k->current_value.empty,
1660 + .empty = true,
1661 };
1662 +
1663 + if(facet_key_value_updated(k) && !facet_key_value_empty(k)) {
1664 + t.tmp = facets_key_get_value(k);
1665 + t.tmp_len = facets_key_get_value_length(k);
1666 + t.empty = false;
1667 + }
1668 +
1669 dictionary_set(row->dict, k->name, &t, sizeof(t));
1670 }
1671 foreach_key_in_facets_done(k);
@@ -1757,10 +1810,8 @@ static void facets_row_keep(FACETS *facets, usec_t usec) {
1810 static inline void facets_reset_key(FACET_KEY *k) {
1811 k->key_found_in_row = 0;
1812 k->key_values_selected_in_row = 0;
1760 - k->current_value.updated = false;
1761 - k->current_value.empty = false;
1813 + k->current_value.flags = FACET_KEY_VALUE_NONE;
1814 k->current_value.hash = FACETS_HASH_ZERO;
1763 - k->current_value.v = NULL;
1815 }
1816
1817 static void facets_reset_keys_with_value_and_row(FACETS *facets) {
@@ -1813,13 +1864,9 @@ bool facets_row_finished(FACETS *facets, usec_t usec) {
1864 for(size_t p = 0; p < entries ;p++) {
1865 FACET_KEY *k = facets->keys_with_values.array[p];
1866
1816 - if(!k->key_found_in_row) {
1867 + if(!facet_key_value_updated(k))
1868 // put the FACET_VALUE_UNSET value into it
1869 facets_key_set_empty_value(facets, k);
1819 - }
1820 -
1821 - internal_fatal(k->key_found_in_row != 1, "all keys should be matched exactly once at this point");
1822 - internal_fatal(k->key_values_selected_in_row > 1, "key values are selected in row more than once");
1870
1871 total_keys++;
1872
@@ -1843,9 +1890,7 @@ bool facets_row_finished(FACETS *facets, usec_t usec) {
1890 counted_by++;
1891
1892 if(counted_by == total_keys) {
1846 - FACET_VALUE *v = FACET_VALUE_GET_CURRENT_VALUE(k);
1847 - v->final_facet_value_counter++;
1848 -
1893 + k->current_value.v->final_facet_value_counter++;
1894 found++;
1895 }
1896 }
@@ -2013,7 +2058,7 @@ static uint32_t facets_sort_and_reorder_values_internal(FACET_KEY *k) {
2058
2059 values[used++] = v;
2060
2016 - if(all_values_numeric && !v->empty && v->name) {
2061 + if(all_values_numeric && !v->empty && v->name && v->name_len) {
2062 const char *s = v->name;
2063 while(isdigit(*s)) s++;
2064 if(*s != '\0')
@@ -2053,7 +2098,10 @@ static uint32_t facets_sort_and_reorder_values(FACET_KEY *k) {
2098 uint32_t ret = 0;
2099
2100 size_t entries = k->values.used;
2056 - const char *values[entries];
2101 + struct {
2102 + const char *name;
2103 + uint32_t name_len;
2104 + } values[entries];
2105 FACET_VALUE *v;
2106 uint32_t used = 0;
2107
@@ -2061,12 +2109,14 @@ static uint32_t facets_sort_and_reorder_values(FACET_KEY *k) {
2109 if(used >= entries)
2110 break;
2111
2064 - values[used++] = v->name;
2112 + values[used].name = v->name;
2113 + values[used].name_len = v->name_len;
2114 + used++;
2115
2066 - buffer_flush(tb);
2067 - buffer_strcat(tb, v->name);
2116 + buffer_contents_replace(tb, v->name, v->name_len);
2117 k->transform.cb(k->facets, tb, FACETS_TRANSFORM_FACET_SORT, k->transform.data);
2118 v->name = strdupz(buffer_tostring(tb));
2119 + v->name_len = buffer_strlen(tb);
2120 }
2121 foreach_value_in_key_done(v);
2122
@@ -2078,7 +2128,9 @@ static uint32_t facets_sort_and_reorder_values(FACET_KEY *k) {
2128 break;
2129
2130 freez((void *)v->name);
2081 - v->name = values[used++];
2131 + v->name = values[used].name;
2132 + v->name_len = values[used].name_len;
2133 + used++;
2134 }
2135 foreach_value_in_key_done(v);
2136
@@ -2125,11 +2177,11 @@ static const char *facets_json_key_name_string(FACET_KEY *k, DICTIONARY *used_ha
2177 }
2178
2179 static const char *facets_json_key_value_string(FACET_KEY *k, FACET_VALUE *v, DICTIONARY *used_hashes_registry) {
2128 - if(v->name) {
2180 + if(v->name && v->name_len) {
2181 if(used_hashes_registry && !k->default_selected_for_values && v->selected) {
2182 char hash_str[FACET_STRING_HASH_SIZE];
2183 facets_hash_to_str(v->hash, hash_str);
2132 - dictionary_set(used_hashes_registry, hash_str, (void *)v->name, strlen(v->name) + 1);
2184 + dictionary_set(used_hashes_registry, hash_str, (void *)v->name, v->name_len + 1);
2185 }
2186
2187 return v->name;
@@ -2205,8 +2257,7 @@ void facets_report(FACETS *facets, BUFFER *wb, DICTIONARY *used_hashes_registry)
2257 if(!tb)
2258 tb = buffer_create(0, NULL);
2259
2208 - buffer_flush(tb);
2209 - buffer_strcat(tb, v->name);
2260 + buffer_contents_replace(tb, v->name, v->name_len);
2261 k->transform.cb(facets, tb, FACETS_TRANSFORM_FACET, k->transform.data);
2262 buffer_json_member_add_string(wb, "name", buffer_tostring(tb));
2263 }
libnetdata/facets/facets.h
+2 -1
@@ -43,8 +43,9 @@ typedef enum __attribute__((packed)) {
43
44 typedef struct facet_row_key_value {
45 const char *tmp;
46 - BUFFER *wb;
46 + uint32_t tmp_len;
47 bool empty;
48 + BUFFER *wb;
49 } FACET_ROW_KEY_VALUE;
50
51 typedef struct facet_row {