github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/ingester/index/index_test.go (about)

     1  package index
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/prometheus/common/model"
    10  	"github.com/prometheus/prometheus/pkg/labels"
    11  	"github.com/prometheus/prometheus/promql/parser"
    12  	"github.com/stretchr/testify/assert"
    13  
    14  	"github.com/cortexproject/cortex/pkg/cortexpb"
    15  )
    16  
    17  func TestIndex(t *testing.T) {
    18  	index := New()
    19  
    20  	for _, entry := range []struct {
    21  		m  model.Metric
    22  		fp model.Fingerprint
    23  	}{
    24  		{model.Metric{"foo": "bar", "flip": "flop"}, 3},
    25  		{model.Metric{"foo": "bar", "flip": "flap"}, 2},
    26  		{model.Metric{"foo": "baz", "flip": "flop"}, 1},
    27  		{model.Metric{"foo": "baz", "flip": "flap"}, 0},
    28  	} {
    29  		index.Add(cortexpb.FromMetricsToLabelAdapters(entry.m), entry.fp)
    30  	}
    31  
    32  	for _, tc := range []struct {
    33  		matchers []*labels.Matcher
    34  		fps      []model.Fingerprint
    35  	}{
    36  		{nil, nil},
    37  		{mustParseMatcher(`{fizz="buzz"}`), []model.Fingerprint{}},
    38  
    39  		{mustParseMatcher(`{foo="bar"}`), []model.Fingerprint{2, 3}},
    40  		{mustParseMatcher(`{foo="baz"}`), []model.Fingerprint{0, 1}},
    41  		{mustParseMatcher(`{flip="flop"}`), []model.Fingerprint{1, 3}},
    42  		{mustParseMatcher(`{flip="flap"}`), []model.Fingerprint{0, 2}},
    43  
    44  		{mustParseMatcher(`{foo="bar", flip="flop"}`), []model.Fingerprint{3}},
    45  		{mustParseMatcher(`{foo="bar", flip="flap"}`), []model.Fingerprint{2}},
    46  		{mustParseMatcher(`{foo="baz", flip="flop"}`), []model.Fingerprint{1}},
    47  		{mustParseMatcher(`{foo="baz", flip="flap"}`), []model.Fingerprint{0}},
    48  
    49  		{mustParseMatcher(`{fizz=~"b.*"}`), []model.Fingerprint{}},
    50  
    51  		{mustParseMatcher(`{foo=~"bar.*"}`), []model.Fingerprint{2, 3}},
    52  		{mustParseMatcher(`{foo=~"ba.*"}`), []model.Fingerprint{0, 1, 2, 3}},
    53  		{mustParseMatcher(`{flip=~"flop|flap"}`), []model.Fingerprint{0, 1, 2, 3}},
    54  		{mustParseMatcher(`{flip=~"flaps"}`), []model.Fingerprint{}},
    55  
    56  		{mustParseMatcher(`{foo=~"bar|bax", flip="flop"}`), []model.Fingerprint{3}},
    57  		{mustParseMatcher(`{foo=~"bar|baz", flip="flap"}`), []model.Fingerprint{0, 2}},
    58  		{mustParseMatcher(`{foo=~"baz.+", flip="flop"}`), []model.Fingerprint{}},
    59  		{mustParseMatcher(`{foo=~"baz", flip="flap"}`), []model.Fingerprint{0}},
    60  	} {
    61  		assert.Equal(t, tc.fps, index.Lookup(tc.matchers))
    62  	}
    63  
    64  	assert.Equal(t, []string{"flip", "foo"}, index.LabelNames())
    65  	assert.Equal(t, []string{"bar", "baz"}, index.LabelValues("foo"))
    66  	assert.Equal(t, []string{"flap", "flop"}, index.LabelValues("flip"))
    67  }
    68  
    69  func BenchmarkSetRegexLookup(b *testing.B) {
    70  	// Prepare the benchmark.
    71  	seriesLabels := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
    72  	seriesPerLabel := 100000
    73  
    74  	idx := New()
    75  	for _, l := range seriesLabels {
    76  		for i := 0; i < seriesPerLabel; i++ {
    77  			lbls := labels.FromStrings("foo", l, "bar", strconv.Itoa(i))
    78  			idx.Add(cortexpb.FromLabelsToLabelAdapters(lbls), model.Fingerprint(lbls.Hash()))
    79  		}
    80  	}
    81  
    82  	selectionLabels := []string{}
    83  	for i := 0; i < 100; i++ {
    84  		selectionLabels = append(selectionLabels, strconv.Itoa(i))
    85  	}
    86  
    87  	tests := []struct {
    88  		name    string
    89  		matcher string
    90  	}{
    91  		{
    92  			name:    "select all",
    93  			matcher: fmt.Sprintf(`{bar=~"%s"}`, strings.Join(selectionLabels, "|")),
    94  		},
    95  		{
    96  			name:    "select two",
    97  			matcher: fmt.Sprintf(`{bar=~"%s"}`, strings.Join(selectionLabels[:2], "|")),
    98  		},
    99  		{
   100  			name:    "select half",
   101  			matcher: fmt.Sprintf(`{bar=~"%s"}`, strings.Join(selectionLabels[:len(selectionLabels)/2], "|")),
   102  		},
   103  		{
   104  			name:    "select none",
   105  			matcher: `{bar=~"bleep|bloop"}`,
   106  		},
   107  		{
   108  			name:    "equality matcher",
   109  			matcher: `{bar="1"}`,
   110  		},
   111  		{
   112  			name:    "regex (non-set) matcher",
   113  			matcher: `{bar=~"1.*"}`,
   114  		},
   115  	}
   116  
   117  	b.ResetTimer()
   118  
   119  	for _, tc := range tests {
   120  		b.Run(fmt.Sprintf("%s:%s", tc.name, tc.matcher), func(b *testing.B) {
   121  			matcher := mustParseMatcher(tc.matcher)
   122  			for n := 0; n < b.N; n++ {
   123  				idx.Lookup(matcher)
   124  			}
   125  		})
   126  	}
   127  
   128  }
   129  
   130  func mustParseMatcher(s string) []*labels.Matcher {
   131  	ms, err := parser.ParseMetricSelector(s)
   132  	if err != nil {
   133  		panic(err)
   134  	}
   135  	return ms
   136  }
   137  
   138  func TestIndex_Delete(t *testing.T) {
   139  	index := New()
   140  
   141  	testData := []struct {
   142  		m  model.Metric
   143  		fp model.Fingerprint
   144  	}{
   145  		{model.Metric{"common": "label", "foo": "bar", "flip": "flop"}, 0},
   146  		{model.Metric{"common": "label", "foo": "bar", "flip": "flap"}, 1},
   147  		{model.Metric{"common": "label", "foo": "baz", "flip": "flop"}, 2},
   148  		{model.Metric{"common": "label", "foo": "baz", "flip": "flap"}, 3},
   149  	}
   150  	for _, entry := range testData {
   151  		index.Add(cortexpb.FromMetricsToLabelAdapters(entry.m), entry.fp)
   152  	}
   153  
   154  	for _, tc := range []struct {
   155  		name           string
   156  		labelsToDelete labels.Labels
   157  		fpToDelete     model.Fingerprint
   158  		expectedFPs    []model.Fingerprint
   159  	}{
   160  		{
   161  			name:           "existing labels and fp",
   162  			labelsToDelete: metricToLabels(testData[0].m),
   163  			fpToDelete:     testData[0].fp,
   164  			expectedFPs:    []model.Fingerprint{1, 2, 3},
   165  		},
   166  		{
   167  			name:           "non-existing labels",
   168  			labelsToDelete: metricToLabels(model.Metric{"app": "fizz"}),
   169  			fpToDelete:     testData[1].fp,
   170  			expectedFPs:    []model.Fingerprint{1, 2, 3},
   171  		},
   172  		{
   173  			name:           "non-existing fp",
   174  			labelsToDelete: metricToLabels(testData[1].m),
   175  			fpToDelete:     99,
   176  			expectedFPs:    []model.Fingerprint{1, 2, 3},
   177  		},
   178  	} {
   179  		t.Run(tc.name, func(t *testing.T) {
   180  			index.Delete(tc.labelsToDelete, tc.fpToDelete)
   181  			assert.Equal(t, tc.expectedFPs, index.Lookup(mustParseMatcher(`{common="label"}`)))
   182  		})
   183  	}
   184  
   185  	assert.Equal(t, []string{"common", "flip", "foo"}, index.LabelNames())
   186  	assert.Equal(t, []string{"label"}, index.LabelValues("common"))
   187  	assert.Equal(t, []string{"bar", "baz"}, index.LabelValues("foo"))
   188  	assert.Equal(t, []string{"flap", "flop"}, index.LabelValues("flip"))
   189  }
   190  
   191  func metricToLabels(m model.Metric) labels.Labels {
   192  	ls := make(labels.Labels, 0, len(m))
   193  	for k, v := range m {
   194  		ls = append(ls, labels.Label{
   195  			Name:  string(k),
   196  			Value: string(v),
   197  		})
   198  	}
   199  
   200  	return ls
   201  }