252
// ----------------------------------------------------------------------------
253
// rrdlabels_add()
254
255
-static void labels_add_already_sanitized(RRDLABELS *labels, const char *key, const char *value, RRDLABEL_SRC ls)
255
+static bool labels_add_already_sanitized(RRDLABELS *labels, const char *key, const char *value, RRDLABEL_SRC ls)
256
{
257
- if (unlikely(!labels || !key)) return;
257
+ if (unlikely(!labels || !key)) return false;
258
259
RRDLABEL *new_label = add_label_name_value(key, value);
260
271
int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
272
RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
273
274
+ bool changed;
275
if(*PValue) {
276
new_ls |= RRDLABEL_FLAG_OLD;
277
*((RRDLABEL_SRC *)PValue) = new_ls;
278
279
delete_label(new_label);
280
+ changed = false;
281
}
282
else {
283
new_ls |= RRDLABEL_FLAG_NEW;
291
RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
292
delete_label(old_label_with_same_key);
293
}
294
+ changed = true;
295
}
296
297
labels->version++;
300
// RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
301
302
spinlock_unlock(&labels->spinlock);
303
+
304
+ return changed;
305
}
306
307
void rrdlabels_add(RRDLABELS *labels, const char *name, const char *value, RRDLABEL_SRC ls)
308
+{
309
+ (void)rrdlabels_add_changed(labels, name, value, ls);
310
+}
311
+
312
+bool rrdlabels_add_changed(RRDLABELS *labels, const char *name, const char *value, RRDLABEL_SRC ls)
313
{
314
if(!labels) {
315
netdata_log_error("%s(): called with NULL dictionary.", __FUNCTION__ );
306
- return;
316
+ return false;
317
}
318
319
char n[RRDLABELS_MAX_NAME_LENGTH + 1], v[RRDLABELS_MAX_VALUE_LENGTH + 1];
322
323
if(!*n) {
324
netdata_log_error("%s: cannot add name '%s' (value '%s') which is sanitized as empty string", __FUNCTION__, name, value);
315
- return;
325
+ return false;
326
}
327
318
- labels_add_already_sanitized(labels, n, v, ls);
328
+ return labels_add_already_sanitized(labels, n, v, ls);
329
}
330
331
bool rrdlabels_exist(RRDLABELS *labels, const char *key)
560
spinlock_unlock(&labels->spinlock);
561
}
562
553
-static void rrdlabels_remove_all_unmarked_unsafe(RRDLABELS *labels)
563
+static size_t rrdlabels_remove_all_unmarked_unsafe(RRDLABELS *labels)
564
{
565
Pvoid_t *PValue;
566
Word_t Index = 0;
567
bool first_then_next = true;
568
+ size_t removed = 0;
569
570
while ((PValue = JudyLFirstThenNext(labels->JudyL, &Index, &first_then_next))) {
571
if (!((*((RRDLABEL_SRC *)PValue)) & (RRDLABEL_FLAG_INTERNAL))) {
577
RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
578
579
delete_label((RRDLABEL *)Index);
580
+ removed++;
581
if (labels->JudyL != (Pvoid_t) NULL) {
582
Index = 0;
583
first_then_next = true;
584
}
585
}
586
}
587
+
588
+ return removed;
589
}
590
591
void rrdlabels_remove_all_unmarked(RRDLABELS *labels)
592
{
593
spinlock_lock(&labels->spinlock);
580
- rrdlabels_remove_all_unmarked_unsafe(labels);
594
+ (void)rrdlabels_remove_all_unmarked_unsafe(labels);
595
spinlock_unlock(&labels->spinlock);
596
}
597
613
spinlock_unlock(&labels->spinlock);
614
}
615
616
+bool rrdlabels_remove_all_unmarked_and_changed(RRDLABELS *labels)
617
+{
618
+ if(!labels) return false;
619
+
620
+ spinlock_lock(&labels->spinlock);
621
+
622
+ size_t added = 0;
623
+ Pvoid_t *PValue;
624
+ Word_t Index = 0;
625
+ bool first_then_next = true;
626
+ while ((PValue = JudyLFirstThenNext(labels->JudyL, &Index, &first_then_next))) {
627
+ if ((*((RRDLABEL_SRC *)PValue)) & RRDLABEL_FLAG_NEW)
628
+ added++;
629
+ }
630
+
631
+ size_t removed = rrdlabels_remove_all_unmarked_unsafe(labels);
632
+
633
+ spinlock_unlock(&labels->spinlock);
634
+
635
+ return (added > 0) || (removed > 0);
636
+}
637
+
638
// ----------------------------------------------------------------------------
639
// rrdlabels_walkthrough_read()
640
699
// rrdlabels_migrate_to_these()
700
// migrate an existing label list to a new list
701
666
-void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src) {
702
+bool rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src) {
703
if (!dst || !src || (dst == src))
668
- return;
704
+ return false;
705
706
spinlock_lock(&dst->spinlock);
707
spinlock_lock(&src->spinlock);
710
711
RRDLABEL *label;
712
Pvoid_t *PValue;
713
+ size_t added = 0;
714
715
RRDLABEL_SRC ls;
716
lfe_start_nolock(src, label, ls)
727
dup_label(label);
728
int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
729
RRDLABELS_MEMORY_DELTA(&dictionary_stats_category_rrdlabels, judy_mem, 0);
730
+ added++;
731
}
732
else
733
flag = RRDLABEL_FLAG_OLD;
736
}
737
lfe_done_nolock();
738
701
- rrdlabels_remove_all_unmarked_unsafe(dst);
739
+ size_t removed = rrdlabels_remove_all_unmarked_unsafe(dst);
740
dst->version = src->version;
741
742
spinlock_unlock(&src->spinlock);
743
spinlock_unlock(&dst->spinlock);
744
+
745
+ return (added > 0) || (removed > 0);
746
}
747
748
//
1051
}
1052
1053
void rrdset_update_rrdlabels(RRDSET *st, RRDLABELS *new_rrdlabels) {
1054
+ bool labels_changed = false;
1055
+
1056
if(!st->rrdlabels)
1057
st->rrdlabels = rrdlabels_create();
1058
1059
if (new_rrdlabels)
1018
- rrdlabels_migrate_to_these(st->rrdlabels, new_rrdlabels);
1060
+ labels_changed = rrdlabels_migrate_to_these(st->rrdlabels, new_rrdlabels);
1061
1062
rrdset_flag_set(st, RRDSET_FLAG_METADATA_UPDATE);
1063
rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
1064
rrdset_metadata_updated(st);
1065
+
1066
+ if(labels_changed) {
1067
+ rrdset_flag_set(st, RRDSET_FLAG_PENDING_LABEL_RECHECK);
1068
+ rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION);
1069
+ }
1070
}
1071
1072
// ----------------------------------------------------------------------------
1580
return errors;
1581
}
1582
1583
+#define UT_EXPECT(_cond, _msg) do { \
1584
+ if (!(_cond)) { \
1585
+ fprintf(stderr, " FAIL: %s\n", (_msg)); \
1586
+ errors++; \
1587
+ } \
1588
+} while (0)
1589
+
1590
+static int rrdlabels_unittest_change_detection(void) {
1591
+ fprintf(stderr, "\n%s() tests\n", __FUNCTION__);
1592
+ int errors = 0;
1593
+
1594
+ // ---- rrdlabels_add_changed: new vs same vs value-change ----
1595
+ RRDLABELS *l = rrdlabels_create();
1596
+ UT_EXPECT(rrdlabels_add_changed(l, "k1", "v1", RRDLABEL_SRC_CONFIG) == true,
1597
+ "add of new label should return true");
1598
+ UT_EXPECT(rrdlabels_add_changed(l, "k1", "v1", RRDLABEL_SRC_CONFIG) == false,
1599
+ "re-add of identical key+value should return false");
1600
+ UT_EXPECT(rrdlabels_add_changed(l, "k1", "v2", RRDLABEL_SRC_CONFIG) == true,
1601
+ "value change for an existing key should return true");
1602
+ UT_EXPECT(rrdlabels_add_changed(l, "k2", "v2", RRDLABEL_SRC_CONFIG) == true,
1603
+ "add of a different new key should return true");
1604
+ rrdlabels_destroy(l);
1605
+
1606
+ // ---- rrdlabels_migrate_to_these: empty/identical/diff/DONT_DELETE ----
1607
+ RRDLABELS *dst = rrdlabels_create();
1608
+ RRDLABELS *src = rrdlabels_create();
1609
+
1610
+ // empty dst, empty src => no add, no remove => false
1611
+ UT_EXPECT(rrdlabels_migrate_to_these(dst, src) == false,
1612
+ "migrate of two empty label sets should return false");
1613
+
1614
+ // populate src; dst empty => added > 0 => true
1615
+ rrdlabels_add(src, "k1", "v1", RRDLABEL_SRC_CONFIG);
1616
+ rrdlabels_add(src, "k2", "v2", RRDLABEL_SRC_CONFIG);
1617
+ UT_EXPECT(rrdlabels_migrate_to_these(dst, src) == true,
1618
+ "migrate to empty dst should return true when src has labels");
1619
+
1620
+ // identical content now (dst was populated from src) => false
1621
+ UT_EXPECT(rrdlabels_migrate_to_these(dst, src) == false,
1622
+ "migrate with identical key/value sets should return false");
1623
+
1624
+ // src adds one => true
1625
+ rrdlabels_add(src, "k3", "v3", RRDLABEL_SRC_CONFIG);
1626
+ UT_EXPECT(rrdlabels_migrate_to_these(dst, src) == true,
1627
+ "migrate that adds a label should return true");
1628
+
1629
+ // src drops one => true (removed > 0)
1630
+ {
1631
+ RRDLABELS *trimmed = rrdlabels_create();
1632
+ rrdlabels_add(trimmed, "k1", "v1", RRDLABEL_SRC_CONFIG);
1633
+ rrdlabels_add(trimmed, "k2", "v2", RRDLABEL_SRC_CONFIG);
1634
+ UT_EXPECT(rrdlabels_migrate_to_these(dst, trimmed) == true,
1635
+ "migrate that removes a label should return true");
1636
+ rrdlabels_destroy(trimmed);
1637
+ }
1638
+ rrdlabels_destroy(dst);
1639
+ rrdlabels_destroy(src);
1640
+
1641
+ // DONT_DELETE: a label that is not in src remains in dst; nothing added or
1642
+ // removed, so the function returns false even though dst != src.
1643
+ dst = rrdlabels_create();
1644
+ src = rrdlabels_create();
1645
+ rrdlabels_add(dst, "k1", "v1", RRDLABEL_SRC_CONFIG | RRDLABEL_FLAG_DONT_DELETE);
1646
+ UT_EXPECT(rrdlabels_migrate_to_these(dst, src) == false,
1647
+ "migrate where the only diff is a DONT_DELETE label should return false");
1648
+ UT_EXPECT(rrdlabels_entries(dst) == 1,
1649
+ "DONT_DELETE label should be preserved after migrate");
1650
+ rrdlabels_destroy(dst);
1651
+ rrdlabels_destroy(src);
1652
+
1653
+ // ---- rrdlabels_remove_all_unmarked_and_changed: CLABEL-commit semantics ----
1654
+ // (1) unmark + re-add identical set => no change => false
1655
+ l = rrdlabels_create();
1656
+ rrdlabels_add(l, "k1", "v1", RRDLABEL_SRC_CONFIG);
1657
+ rrdlabels_add(l, "k2", "v2", RRDLABEL_SRC_CONFIG);
1658
+ rrdlabels_unmark_all(l);
1659
+ rrdlabels_add(l, "k1", "v1", RRDLABEL_SRC_CONFIG);
1660
+ rrdlabels_add(l, "k2", "v2", RRDLABEL_SRC_CONFIG);
1661
+ UT_EXPECT(rrdlabels_remove_all_unmarked_and_changed(l) == false,
1662
+ "remove_all_unmarked_and_changed: identical re-commit should return false");
1663
+ rrdlabels_destroy(l);
1664
+
1665
+ // (2) unmark + add a new label => added > 0 => true
1666
+ l = rrdlabels_create();
1667
+ rrdlabels_add(l, "k1", "v1", RRDLABEL_SRC_CONFIG);
1668
+ rrdlabels_unmark_all(l);
1669
+ rrdlabels_add(l, "k1", "v1", RRDLABEL_SRC_CONFIG);
1670
+ rrdlabels_add(l, "k2", "v2", RRDLABEL_SRC_CONFIG);
1671
+ UT_EXPECT(rrdlabels_remove_all_unmarked_and_changed(l) == true,
1672
+ "remove_all_unmarked_and_changed: adding a label should return true");
1673
+ rrdlabels_destroy(l);
1674
+
1675
+ // (3) unmark + commit a subset => removed > 0 => true
1676
+ l = rrdlabels_create();
1677
+ rrdlabels_add(l, "k1", "v1", RRDLABEL_SRC_CONFIG);
1678
+ rrdlabels_add(l, "k2", "v2", RRDLABEL_SRC_CONFIG);
1679
+ rrdlabels_unmark_all(l);
1680
+ rrdlabels_add(l, "k1", "v1", RRDLABEL_SRC_CONFIG);
1681
+ UT_EXPECT(rrdlabels_remove_all_unmarked_and_changed(l) == true,
1682
+ "remove_all_unmarked_and_changed: dropping a label should return true");
1683
+ rrdlabels_destroy(l);
1684
+
1685
+ // (4) unmark + change a value => true (the changed entry is NEW)
1686
+ l = rrdlabels_create();
1687
+ rrdlabels_add(l, "k1", "v1", RRDLABEL_SRC_CONFIG);
1688
+ rrdlabels_unmark_all(l);
1689
+ rrdlabels_add(l, "k1", "v2", RRDLABEL_SRC_CONFIG);
1690
+ UT_EXPECT(rrdlabels_remove_all_unmarked_and_changed(l) == true,
1691
+ "remove_all_unmarked_and_changed: value change should return true");
1692
+ rrdlabels_destroy(l);
1693
+
1694
+ return errors;
1695
+}
1696
+
1697
+#undef UT_EXPECT
1698
+
1699
struct pattern_array *trim_and_add_key_to_values(struct pattern_array *pa, const char *key, STRING *input);
1700
static int rrdlabels_unittest_check_pattern_list(RRDLABELS *labels, const char *pattern, bool expected) {
1701
fprintf(stderr, "rrdlabels_match_simple_pattern(labels, \"%s\") ... ", pattern);
1879
errors += rrdlabels_unittest_double_check();
1880
errors += rrdlabels_unittest_migrate_check();
1881
errors += rrdlabels_unittest_mark_source_as_old();
1882
+ errors += rrdlabels_unittest_change_detection();
1883
errors += rrdlabels_unittest_pattern_check();
1884
1885
fprintf(stderr, "%d errors found\n", errors);