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

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sort"
     7  	"strconv"
     8  	"testing"
     9  
    10  	"github.com/prometheus/common/model"
    11  	"github.com/prometheus/prometheus/pkg/labels"
    12  )
    13  
    14  func TestQueryRequest(t *testing.T) {
    15  	from, to := model.Time(int64(0)), model.Time(int64(10))
    16  	matchers := []*labels.Matcher{}
    17  	matcher1, err := labels.NewMatcher(labels.MatchEqual, "foo", "1")
    18  	if err != nil {
    19  		t.Fatal(err)
    20  	}
    21  	matchers = append(matchers, matcher1)
    22  
    23  	matcher2, err := labels.NewMatcher(labels.MatchNotEqual, "bar", "2")
    24  	if err != nil {
    25  		t.Fatal(err)
    26  	}
    27  	matchers = append(matchers, matcher2)
    28  
    29  	matcher3, err := labels.NewMatcher(labels.MatchRegexp, "baz", "3")
    30  	if err != nil {
    31  		t.Fatal(err)
    32  	}
    33  	matchers = append(matchers, matcher3)
    34  
    35  	matcher4, err := labels.NewMatcher(labels.MatchNotRegexp, "bop", "4")
    36  	if err != nil {
    37  		t.Fatal(err)
    38  	}
    39  	matchers = append(matchers, matcher4)
    40  
    41  	req, err := ToQueryRequest(from, to, matchers)
    42  	if err != nil {
    43  		t.Fatal(err)
    44  	}
    45  
    46  	haveFrom, haveTo, haveMatchers, err := FromQueryRequest(req)
    47  	if err != nil {
    48  		t.Fatal(err)
    49  	}
    50  
    51  	if !reflect.DeepEqual(haveFrom, from) {
    52  		t.Fatalf("Bad from FromQueryRequest(ToQueryRequest) round trip")
    53  	}
    54  	if !reflect.DeepEqual(haveTo, to) {
    55  		t.Fatalf("Bad to FromQueryRequest(ToQueryRequest) round trip")
    56  	}
    57  	if !reflect.DeepEqual(haveMatchers, matchers) {
    58  		t.Fatalf("Bad have FromQueryRequest(ToQueryRequest) round trip - %v != %v", haveMatchers, matchers)
    59  	}
    60  }
    61  
    62  func buildTestMatrix(numSeries int, samplesPerSeries int, offset int) model.Matrix {
    63  	m := make(model.Matrix, 0, numSeries)
    64  	for i := 0; i < numSeries; i++ {
    65  		ss := model.SampleStream{
    66  			Metric: model.Metric{
    67  				model.MetricNameLabel: model.LabelValue(fmt.Sprintf("testmetric_%d", i)),
    68  				model.JobLabel:        "testjob",
    69  			},
    70  			Values: make([]model.SamplePair, 0, samplesPerSeries),
    71  		}
    72  		for j := 0; j < samplesPerSeries; j++ {
    73  			ss.Values = append(ss.Values, model.SamplePair{
    74  				Timestamp: model.Time(i + j + offset),
    75  				Value:     model.SampleValue(i + j + offset),
    76  			})
    77  		}
    78  		m = append(m, &ss)
    79  	}
    80  	sort.Sort(m)
    81  	return m
    82  }
    83  
    84  func TestQueryResponse(t *testing.T) {
    85  	want := buildTestMatrix(10, 10, 10)
    86  	have := FromQueryResponse(ToQueryResponse(want))
    87  	if !reflect.DeepEqual(have, want) {
    88  		t.Fatalf("Bad FromQueryResponse(ToQueryResponse) round trip")
    89  	}
    90  }
    91  
    92  // This test shows label sets with same fingerprints, and also shows how to easily create new collisions
    93  // (by adding "_" or "A" label with specific values, see below).
    94  func TestFingerprintCollisions(t *testing.T) {
    95  	// "8yn0iYCKYHlIj4-BwPqk" and "GReLUrM4wMqfg9yzV3KQ" have same FNV-1a hash.
    96  	// If we use it as a single label name (for labels that have same value), we get colliding labels.
    97  	c1 := labels.FromStrings("8yn0iYCKYHlIj4-BwPqk", "hello")
    98  	c2 := labels.FromStrings("GReLUrM4wMqfg9yzV3KQ", "hello")
    99  	verifyCollision(t, true, c1, c2)
   100  
   101  	// Adding _="ypfajYg2lsv" or _="KiqbryhzUpn" respectively to most metrics will produce collision.
   102  	// It's because "_\xffypfajYg2lsv" and "_\xffKiqbryhzUpn" have same FNV-1a hash, and "_" label is sorted before
   103  	// most other labels (except labels starting with upper-case letter)
   104  
   105  	const _label1 = "ypfajYg2lsv"
   106  	const _label2 = "KiqbryhzUpn"
   107  
   108  	metric := labels.NewBuilder(labels.FromStrings("__name__", "logs"))
   109  	c1 = metric.Set("_", _label1).Labels()
   110  	c2 = metric.Set("_", _label2).Labels()
   111  	verifyCollision(t, true, c1, c2)
   112  
   113  	metric = labels.NewBuilder(labels.FromStrings("__name__", "up", "instance", "hello"))
   114  	c1 = metric.Set("_", _label1).Labels()
   115  	c2 = metric.Set("_", _label2).Labels()
   116  	verifyCollision(t, true, c1, c2)
   117  
   118  	// here it breaks, because "Z" label is sorted before "_" label.
   119  	metric = labels.NewBuilder(labels.FromStrings("__name__", "up", "Z", "hello"))
   120  	c1 = metric.Set("_", _label1).Labels()
   121  	c2 = metric.Set("_", _label2).Labels()
   122  	verifyCollision(t, false, c1, c2)
   123  
   124  	// A="K6sjsNNczPl" and A="cswpLMIZpwt" label has similar property.
   125  	// (Again, because "A\xffK6sjsNNczPl" and "A\xffcswpLMIZpwt" have same FNV-1a hash)
   126  	// This time, "A" is the smallest possible label name, and is always sorted first.
   127  
   128  	const Alabel1 = "K6sjsNNczPl"
   129  	const Alabel2 = "cswpLMIZpwt"
   130  
   131  	metric = labels.NewBuilder(labels.FromStrings("__name__", "up", "Z", "hello"))
   132  	c1 = metric.Set("A", Alabel1).Labels()
   133  	c2 = metric.Set("A", Alabel2).Labels()
   134  	verifyCollision(t, true, c1, c2)
   135  
   136  	// Adding the same suffix to the "A" label also works.
   137  	metric = labels.NewBuilder(labels.FromStrings("__name__", "up", "Z", "hello"))
   138  	c1 = metric.Set("A", Alabel1+"suffix").Labels()
   139  	c2 = metric.Set("A", Alabel2+"suffix").Labels()
   140  	verifyCollision(t, true, c1, c2)
   141  }
   142  
   143  func verifyCollision(t *testing.T, collision bool, ls1 labels.Labels, ls2 labels.Labels) {
   144  	if collision && Fingerprint(ls1) != Fingerprint(ls2) {
   145  		t.Errorf("expected same fingerprints for %v (%016x) and %v (%016x)", ls1.String(), Fingerprint(ls1), ls2.String(), Fingerprint(ls2))
   146  	} else if !collision && Fingerprint(ls1) == Fingerprint(ls2) {
   147  		t.Errorf("expected different fingerprints for %v (%016x) and %v (%016x)", ls1.String(), Fingerprint(ls1), ls2.String(), Fingerprint(ls2))
   148  	}
   149  }
   150  
   151  // The main usecase for `LabelsToKeyString` is to generate hashKeys
   152  // for maps. We are benchmarking that here.
   153  func BenchmarkSeriesMap(b *testing.B) {
   154  	benchmarkSeriesMap(100000, b)
   155  }
   156  
   157  func benchmarkSeriesMap(numSeries int, b *testing.B) {
   158  	series := makeSeries(numSeries)
   159  	sm := make(map[string]int, numSeries)
   160  
   161  	b.ReportAllocs()
   162  	b.ResetTimer()
   163  	for n := 0; n < b.N; n++ {
   164  		for i, s := range series {
   165  			sm[LabelsToKeyString(s)] = i
   166  		}
   167  
   168  		for _, s := range series {
   169  			_, ok := sm[LabelsToKeyString(s)]
   170  			if !ok {
   171  				b.Fatal("element missing")
   172  			}
   173  		}
   174  
   175  		if len(sm) != numSeries {
   176  			b.Fatal("the number of series expected:", numSeries, "got:", len(sm))
   177  		}
   178  	}
   179  }
   180  
   181  func makeSeries(n int) []labels.Labels {
   182  	series := make([]labels.Labels, 0, n)
   183  	for i := 0; i < n; i++ {
   184  		series = append(series, labels.FromMap(map[string]string{
   185  			"label0": "value0",
   186  			"label1": "value1",
   187  			"label2": "value2",
   188  			"label3": "value3",
   189  			"label4": "value4",
   190  			"label5": "value5",
   191  			"label6": "value6",
   192  			"label7": "value7",
   193  			"label8": "value8",
   194  			"label9": strconv.Itoa(i),
   195  		}))
   196  	}
   197  
   198  	return series
   199  }