github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/queryrange/queryrangebase/test_utils.go (about) 1 package queryrangebase 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/pkg/errors" 9 "github.com/prometheus/common/model" 10 "github.com/prometheus/prometheus/model/labels" 11 "github.com/prometheus/prometheus/storage" 12 13 "github.com/grafana/loki/pkg/querier/astmapper" 14 "github.com/grafana/loki/pkg/querier/series" 15 ) 16 17 // genLabels will create a slice of labels where each label has an equal chance to occupy a value from [0,labelBuckets]. It returns a slice of length labelBuckets^len(labelSet) 18 func genLabels( 19 labelSet []string, 20 labelBuckets int, 21 ) (result []labels.Labels) { 22 if len(labelSet) == 0 { 23 return result 24 } 25 26 l := labelSet[0] 27 rest := genLabels(labelSet[1:], labelBuckets) 28 29 for i := 0; i < labelBuckets; i++ { 30 x := labels.Label{ 31 Name: l, 32 Value: fmt.Sprintf("%d", i), 33 } 34 if len(rest) == 0 { 35 set := labels.Labels{x} 36 result = append(result, set) 37 continue 38 } 39 for _, others := range rest { 40 set := append(others, x) 41 result = append(result, set) 42 } 43 } 44 return result 45 46 } 47 48 // NewMockShardedQueryable creates a shard-aware in memory queryable. 49 func NewMockShardedQueryable( 50 nSamples int, 51 labelSet []string, 52 labelBuckets int, 53 delayPerSeries time.Duration, 54 ) *MockShardedQueryable { 55 samples := make([]model.SamplePair, 0, nSamples) 56 for i := 0; i < nSamples; i++ { 57 samples = append(samples, model.SamplePair{ 58 Timestamp: model.Time(i * 1000), 59 Value: model.SampleValue(i), 60 }) 61 } 62 sets := genLabels(labelSet, labelBuckets) 63 xs := make([]storage.Series, 0, len(sets)) 64 for _, ls := range sets { 65 xs = append(xs, series.NewConcreteSeries(ls, samples)) 66 } 67 68 return &MockShardedQueryable{ 69 series: xs, 70 delayPerSeries: delayPerSeries, 71 } 72 } 73 74 // MockShardedQueryable is exported to be reused in the querysharding benchmarking 75 type MockShardedQueryable struct { 76 series []storage.Series 77 delayPerSeries time.Duration 78 } 79 80 // Querier impls storage.Queryable 81 func (q *MockShardedQueryable) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) { 82 return q, nil 83 } 84 85 // Select implements storage.Querier interface. 86 // The bool passed is ignored because the series is always sorted. 87 func (q *MockShardedQueryable) Select(_ bool, _ *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet { 88 tStart := time.Now() 89 90 shard, _, err := astmapper.ShardFromMatchers(matchers) 91 if err != nil { 92 return storage.ErrSeriesSet(err) 93 } 94 95 var ( 96 start int 97 end int 98 ) 99 100 if shard == nil { 101 start = 0 102 end = len(q.series) 103 } else { 104 // return the series range associated with this shard 105 seriesPerShard := len(q.series) / shard.Of 106 start = shard.Shard * seriesPerShard 107 end = start + seriesPerShard 108 109 // if we're clipping an odd # of series, add the final series to the last shard 110 if end == len(q.series)-1 && len(q.series)%2 == 1 { 111 end = len(q.series) 112 } 113 } 114 115 var name string 116 for _, m := range matchers { 117 if m.Type == labels.MatchEqual && m.Name == "__name__" { 118 name = m.Value 119 } 120 } 121 122 results := make([]storage.Series, 0, end-start) 123 for i := start; i < end; i++ { 124 results = append(results, &ShardLabelSeries{ 125 shard: shard, 126 name: name, 127 Series: q.series[i], 128 }) 129 } 130 131 // loosely enforce the assumption that an operation on 1/nth of the data 132 // takes 1/nth of the time. 133 duration := q.delayPerSeries * time.Duration(len(q.series)) 134 if shard != nil { 135 duration = duration / time.Duration(shard.Of) 136 } 137 138 remaining := time.Until(tStart.Add(duration)) 139 if remaining > 0 { 140 time.Sleep(remaining) 141 } 142 143 // sorted 144 return series.NewConcreteSeriesSet(results) 145 } 146 147 // ShardLabelSeries allows extending a Series with new labels. This is helpful for adding cortex shard labels 148 type ShardLabelSeries struct { 149 shard *astmapper.ShardAnnotation 150 name string 151 storage.Series 152 } 153 154 // Labels impls storage.Series 155 func (s *ShardLabelSeries) Labels() labels.Labels { 156 ls := s.Series.Labels() 157 158 if s.name != "" { 159 ls = append(ls, labels.Label{ 160 Name: "__name__", 161 Value: s.name, 162 }) 163 } 164 165 if s.shard != nil { 166 ls = append(ls, s.shard.Label()) 167 } 168 169 return ls 170 } 171 172 // LabelValues impls storage.Querier 173 func (q *MockShardedQueryable) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 174 return nil, nil, errors.Errorf("unimplemented") 175 } 176 177 // LabelNames returns all the unique label names present in the block in sorted order. 178 func (q *MockShardedQueryable) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 179 return nil, nil, errors.Errorf("unimplemented") 180 } 181 182 // Close releases the resources of the Querier. 183 func (q *MockShardedQueryable) Close() error { 184 return nil 185 }