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 {