github.com/grafana/pyroscope@v1.18.0/pkg/block/metadata/metadata_labels_test.go (about)

     1  package metadata
     2  
     3  import (
     4  	"slices"
     5  	"testing"
     6  
     7  	"github.com/prometheus/prometheus/model/labels"
     8  	"github.com/stretchr/testify/assert"
     9  
    10  	metastorev1 "github.com/grafana/pyroscope/api/gen/proto/go/metastore/v1"
    11  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
    12  	"github.com/grafana/pyroscope/pkg/model"
    13  )
    14  
    15  func TestLabelBuilder_Put(t *testing.T) {
    16  	strings := NewStringTable()
    17  	b := NewLabelBuilder(strings)
    18  
    19  	// a=b, a=b; a=b, a=b;
    20  	b.Put([]int32{2, 1, 2, 1, 2, 2, 1, 2, 1, 2}, []string{"", "a", "b"})
    21  	b.Put([]int32{2, 1, 2, 1, 2, 2, 1, 2, 1, 2}, []string{"", "a", "b"})
    22  
    23  	// c=d, c=d; c=d, c=d;
    24  	b.Put([]int32{2, 1, 2, 1, 2, 2, 1, 2, 1, 2}, []string{"", "c", "d"})
    25  	b.Put([]int32{2, 1, 2, 1, 2}, []string{"", "c", "d"})
    26  
    27  	assert.Equal(t, []int32{
    28  		2, 1, 2, 1, 2,
    29  		2, 3, 4, 3, 4,
    30  	}, b.Build())
    31  }
    32  
    33  func labelStrings(v []int32, s *StringTable) []string {
    34  	var ls []string
    35  	pairs := LabelPairs(v)
    36  	for pairs.Next() {
    37  		p := pairs.At()
    38  		var l string
    39  		for len(p) > 0 {
    40  			l += s.Lookup(p[0]) + "=" + s.Lookup(p[1]) + ";"
    41  			p = p[2:]
    42  		}
    43  		ls = append(ls, l)
    44  	}
    45  	return ls
    46  }
    47  
    48  func TestLabelMatcher_Matches(t *testing.T) {
    49  	strings := NewStringTable()
    50  	setA := NewLabelBuilder(strings).
    51  		WithLabelSet("service_name", "service_a", "__profile_type__", "cpu:a").
    52  		WithLabelSet("service_name", "service_a", "__profile_type__", "cpu:b").
    53  		WithLabelSet("service_name", "service_a", "__profile_type__", "memory").
    54  		Build()
    55  	assert.Equal(t, []string{
    56  		"service_name=service_a;__profile_type__=cpu:a;",
    57  		"service_name=service_a;__profile_type__=cpu:b;",
    58  		"service_name=service_a;__profile_type__=memory;",
    59  	}, labelStrings(setA, strings))
    60  
    61  	setB := NewLabelBuilder(strings).
    62  		WithLabelSet("service_name", "service_b", "__profile_type__", "cpu:a").
    63  		WithLabelSet("service_name", "service_b", "__profile_type__", "cpu:b").
    64  		Build()
    65  	assert.Equal(t, []string{
    66  		"service_name=service_b;__profile_type__=cpu:a;",
    67  		"service_name=service_b;__profile_type__=cpu:b;",
    68  	}, labelStrings(setB, strings))
    69  
    70  	keepLabels := []string{"service_name", "__profile_type__", "none"}
    71  	m := NewLabelMatcher(strings.Strings, []*labels.Matcher{
    72  		labels.MustNewMatcher(labels.MatchEqual, "__profile_type__", "cpu:a")},
    73  		keepLabels...)
    74  	assert.True(t, m.IsValid())
    75  
    76  	expected := []bool{true, false, false, true, false}
    77  	matches := make([]bool, 0, len(expected))
    78  
    79  	pairs := LabelPairs(setA)
    80  	for pairs.Next() {
    81  		matches = append(matches, m.MatchesPairs(pairs.At()))
    82  	}
    83  
    84  	pairs = LabelPairs(setB)
    85  	for pairs.Next() {
    86  		matches = append(matches, m.MatchesPairs(pairs.At()))
    87  	}
    88  	assert.Equal(t, expected, matches)
    89  
    90  	t.Run("LabelCollector", func(t *testing.T) {
    91  		c := NewLabelsCollector(keepLabels...)
    92  		c.CollectMatches(m)
    93  
    94  		collected := slices.Collect(c.Unique())
    95  		slices.SortFunc(collected, model.CompareLabels)
    96  
    97  		// The label order matches the input.
    98  		assert.Equal(t, []*typesv1.Labels{
    99  			{
   100  				Labels: []*typesv1.LabelPair{
   101  					{Name: "service_name", Value: "service_a"},
   102  					{Name: "__profile_type__", Value: "cpu:a"},
   103  					{Name: "none", Value: ""},
   104  				},
   105  			},
   106  			{
   107  				Labels: []*typesv1.LabelPair{
   108  					{Name: "service_name", Value: "service_b"},
   109  					{Name: "__profile_type__", Value: "cpu:a"},
   110  					{Name: "none", Value: ""},
   111  				},
   112  			},
   113  		}, collected)
   114  	})
   115  }
   116  
   117  func TestLabelMatcher_Collect(t *testing.T) {
   118  	strings := NewStringTable()
   119  	setA := NewLabelBuilder(strings).
   120  		WithLabelSet("service_name", "service_a", "__profile_type__", "cpu:a").
   121  		WithLabelSet("service_name", "service_a", "__profile_type__", "cpu:b").
   122  		WithLabelSet("service_name", "service_a", "__profile_type__", "memory").
   123  		Build()
   124  
   125  	setB := NewLabelBuilder(strings).
   126  		WithLabelSet("service_name", "service_b", "__profile_type__", "cpu:a").
   127  		WithLabelSet("service_name", "service_b", "__profile_type__", "cpu:b").
   128  		Build()
   129  
   130  	m := NewLabelMatcher(strings.Strings, []*labels.Matcher{
   131  		labels.MustNewMatcher(labels.MatchEqual, "service_name", "service_a"),
   132  		labels.MustNewMatcher(labels.MatchRegexp, "__profile_type__", "cpu.*")},
   133  		"service_name",
   134  		"none")
   135  	assert.True(t, m.IsValid())
   136  
   137  	matches, ok := m.CollectMatches(nil, setA)
   138  	assert.True(t, ok)
   139  	assert.Equal(t, []string{
   140  		"service_name=service_a;",
   141  		"service_name=service_a;",
   142  	}, labelStrings(matches, strings))
   143  
   144  	matches = matches[:0]
   145  	matches, ok = m.CollectMatches(matches, setB)
   146  	assert.False(t, ok)
   147  	assert.Len(t, matches, 0)
   148  }
   149  
   150  func Benchmark_LabelMatcher_Matches(b *testing.B) {
   151  	strings := NewStringTable()
   152  
   153  	ls := NewLabelBuilder(strings).
   154  		WithLabelSet("service_name", "service_a", "__profile_type__", "cpu").
   155  		Build()
   156  
   157  	m := NewLabelMatcher(strings.Strings,
   158  		[]*labels.Matcher{labels.MustNewMatcher(labels.MatchEqual, "service_name", "service_a")},
   159  		"service_name", "__profile_type__")
   160  
   161  	assert.True(b, m.IsValid())
   162  	b.ReportAllocs()
   163  
   164  	for i := 0; i < b.N; i++ {
   165  		pairs := LabelPairs(ls)
   166  		for pairs.Next() {
   167  			m.MatchesPairs(pairs.At())
   168  		}
   169  	}
   170  }
   171  
   172  func TestFindDatasets(t *testing.T) {
   173  	strings := NewStringTable()
   174  	setA := NewLabelBuilder(strings).
   175  		WithLabelSet("service_name", "service_a", "__profile_type__", "cpu:a").
   176  		WithLabelSet("service_name", "service_a", "__profile_type__", "cpu:b").
   177  		WithLabelSet("service_name", "service_a", "__profile_type__", "memory").
   178  		Build()
   179  
   180  	setB := NewLabelBuilder(strings).
   181  		WithLabelSet("service_name", "service_b", "__profile_type__", "cpu:a").
   182  		WithLabelSet("service_name", "service_b", "__profile_type__", "cpu:b").
   183  		Build()
   184  
   185  	md := &metastorev1.BlockMeta{
   186  		Datasets: []*metastorev1.Dataset{
   187  			{Name: 3, Labels: setA},
   188  			{Name: 4, Labels: setB},
   189  		},
   190  		StringTable: strings.Strings,
   191  	}
   192  
   193  	for _, test := range []struct {
   194  		matchers []*labels.Matcher
   195  		expected []int32
   196  	}{
   197  		{
   198  			expected: []int32{3, 4},
   199  		},
   200  		{
   201  			matchers: []*labels.Matcher{labels.MustNewMatcher(labels.MatchEqual, "foo", "bar")},
   202  		},
   203  		{
   204  			matchers: []*labels.Matcher{labels.MustNewMatcher(labels.MatchEqual, "service_name", "service_b")},
   205  			expected: []int32{4},
   206  		},
   207  		{
   208  			matchers: []*labels.Matcher{labels.MustNewMatcher(labels.MatchNotEqual, "service_name", "service_b")},
   209  			expected: []int32{3},
   210  		},
   211  		{
   212  			matchers: []*labels.Matcher{labels.MustNewMatcher(labels.MatchRegexp, "service_name", ".*")},
   213  			expected: []int32{3, 4},
   214  		},
   215  		{
   216  			matchers: []*labels.Matcher{
   217  				labels.MustNewMatcher(labels.MatchEqual, "__profile_type__", "memory"),
   218  			},
   219  			expected: []int32{3},
   220  		},
   221  		{
   222  			matchers: []*labels.Matcher{
   223  				labels.MustNewMatcher(labels.MatchEqual, "__profile_type__", "memory"),
   224  				labels.MustNewMatcher(labels.MatchEqual, "service_name", "service_a"),
   225  			},
   226  			expected: []int32{3},
   227  		},
   228  		{
   229  			matchers: []*labels.Matcher{
   230  				labels.MustNewMatcher(labels.MatchEqual, "__profile_type__", "memory"),
   231  				labels.MustNewMatcher(labels.MatchEqual, "service_name", "service_b"),
   232  			},
   233  		},
   234  	} {
   235  		var actual []int32
   236  		FindDatasets(md, test.matchers...)(func(v *metastorev1.Dataset) bool {
   237  			actual = append(actual, v.Name)
   238  			return true
   239  		})
   240  		assert.Equal(t, test.expected, actual)
   241  	}
   242  }
   243  
   244  func Test_LabelMatcher_Skip(t *testing.T) {
   245  	strings := []string{"", "foo", "bar", "baz", "qux"}
   246  
   247  	type testCase struct {
   248  		valid   bool
   249  		matches []*labels.Matcher
   250  	}
   251  
   252  	for _, test := range []testCase{
   253  		{true, []*labels.Matcher{}},
   254  		{true, []*labels.Matcher{
   255  			labels.MustNewMatcher(labels.MatchEqual, "foo", "bar"),
   256  		}},
   257  		{true, []*labels.Matcher{
   258  			labels.MustNewMatcher(labels.MatchRegexp, "foo", "b.*"),
   259  		}},
   260  		{true, []*labels.Matcher{
   261  			labels.MustNewMatcher(labels.MatchRegexp, "fee", ""),
   262  		}},
   263  		{true, []*labels.Matcher{
   264  			labels.MustNewMatcher(labels.MatchNotEqual, "foo", ""),
   265  		}},
   266  		{true, []*labels.Matcher{
   267  			labels.MustNewMatcher(labels.MatchNotRegexp, "far", ""),
   268  		}},
   269  		{false, []*labels.Matcher{
   270  			labels.MustNewMatcher(labels.MatchEqual, "foo", "bar"),
   271  			labels.MustNewMatcher(labels.MatchEqual, "har", "bor"),
   272  		}},
   273  	} {
   274  		assert.Equal(t, test.valid, NewLabelMatcher(strings, test.matches).IsValid())
   275  	}
   276  }