github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/series/index/schema_test.go (about) 1 package index 2 3 import ( 4 "bytes" 5 "fmt" 6 "testing" 7 8 jsoniter "github.com/json-iterator/go" 9 "github.com/prometheus/common/model" 10 "github.com/prometheus/prometheus/model/labels" 11 "github.com/prometheus/prometheus/promql/parser" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 "github.com/grafana/loki/pkg/querier/astmapper" 16 "github.com/grafana/loki/pkg/storage/config" 17 ) 18 19 func TestDailyBuckets(t *testing.T) { 20 const ( 21 userID = "0" 22 metricName = model.LabelValue("name") 23 tableName = "table" 24 ) 25 cfg := config.PeriodConfig{ 26 IndexTables: config.PeriodicTableConfig{Prefix: tableName}, 27 } 28 29 type args struct { 30 from model.Time 31 through model.Time 32 } 33 tests := []struct { 34 name string 35 args args 36 want []Bucket 37 }{ 38 { 39 "0 day window", 40 args{ 41 from: model.TimeFromUnix(0), 42 through: model.TimeFromUnix(0), 43 }, 44 []Bucket{{ 45 from: 0, 46 through: 0, 47 tableName: "table", 48 hashKey: "0:d0", 49 bucketSize: uint32(millisecondsInDay), 50 }}, 51 }, 52 { 53 "6 hour window", 54 args{ 55 from: model.TimeFromUnix(0), 56 through: model.TimeFromUnix(6 * 3600), 57 }, 58 []Bucket{{ 59 from: 0, 60 through: (6 * 3600) * 1000, // ms 61 tableName: "table", 62 hashKey: "0:d0", 63 bucketSize: uint32(millisecondsInDay), 64 }}, 65 }, 66 { 67 "1 day window", 68 args{ 69 from: model.TimeFromUnix(0), 70 through: model.TimeFromUnix(24 * 3600), 71 }, 72 []Bucket{{ 73 from: 0, 74 through: (24 * 3600) * 1000, // ms 75 tableName: "table", 76 hashKey: "0:d0", 77 bucketSize: uint32(millisecondsInDay), 78 }, { 79 from: 0, 80 through: 0, 81 tableName: "table", 82 hashKey: "0:d1", 83 bucketSize: uint32(millisecondsInDay), 84 }}, 85 }, 86 { 87 "window spanning 3 days with non-zero start", 88 args{ 89 from: model.TimeFromUnix(6 * 3600), 90 through: model.TimeFromUnix((2 * 24 * 3600) + (12 * 3600)), 91 }, 92 []Bucket{{ 93 from: (6 * 3600) * 1000, // ms 94 through: (24 * 3600) * 1000, // ms 95 tableName: "table", 96 hashKey: "0:d0", 97 bucketSize: uint32(millisecondsInDay), 98 }, { 99 from: 0, 100 through: (24 * 3600) * 1000, // ms 101 tableName: "table", 102 hashKey: "0:d1", 103 bucketSize: uint32(millisecondsInDay), 104 }, { 105 from: 0, 106 through: (12 * 3600) * 1000, // ms 107 tableName: "table", 108 hashKey: "0:d2", 109 bucketSize: uint32(millisecondsInDay), 110 }}, 111 }, 112 } 113 for _, tt := range tests { 114 t.Run(tt.name, func(t *testing.T) { 115 got := dailyBuckets(cfg)(tt.args.from, tt.args.through, userID) 116 assert.Equal(t, tt.want, got) 117 }) 118 } 119 } 120 121 type ByHashRangeKey []Entry 122 123 func (a ByHashRangeKey) Len() int { return len(a) } 124 func (a ByHashRangeKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 125 func (a ByHashRangeKey) Less(i, j int) bool { 126 if a[i].HashValue != a[j].HashValue { 127 return a[i].HashValue < a[j].HashValue 128 } 129 return bytes.Compare(a[i].RangeValue, a[j].RangeValue) < 0 130 } 131 132 const table = "table" 133 134 func mustMakeSchema(schemaName string) SeriesStoreSchema { 135 s, err := CreateSchema(config.PeriodConfig{ 136 Schema: schemaName, 137 IndexTables: config.PeriodicTableConfig{Prefix: table}, 138 }) 139 if err != nil { 140 panic(err) 141 } 142 return s 143 } 144 145 // range value types 146 const ( 147 _ = iota 148 MetricNameRangeValue 149 ChunkTimeRangeValue 150 SeriesRangeValue 151 ) 152 153 func BenchmarkEncodeLabelsJson(b *testing.B) { 154 decoded := &labels.Labels{} 155 lbs := labels.FromMap(map[string]string{ 156 "foo": "bar", 157 "fuzz": "buzz", 158 "cluster": "test", 159 "test": "test1", 160 "instance": "cortex-01", 161 "bar": "foo", 162 "version": "0.1", 163 }) 164 json := jsoniter.ConfigFastest 165 var data []byte 166 var err error 167 for n := 0; n < b.N; n++ { 168 data, err = json.Marshal(lbs) 169 if err != nil { 170 panic(err) 171 } 172 err = json.Unmarshal(data, decoded) 173 if err != nil { 174 panic(err) 175 } 176 } 177 b.Log("data size", len(data)) 178 b.Log("decode", decoded) 179 } 180 181 func BenchmarkEncodeLabelsString(b *testing.B) { 182 var decoded labels.Labels 183 lbs := labels.FromMap(map[string]string{ 184 "foo": "bar", 185 "fuzz": "buzz", 186 "cluster": "test", 187 "test": "test1", 188 "instance": "cortex-01", 189 "bar": "foo", 190 "version": "0.1", 191 }) 192 var data []byte 193 var err error 194 for n := 0; n < b.N; n++ { 195 data = []byte(lbs.String()) 196 decoded, err = parser.ParseMetric(string(data)) 197 if err != nil { 198 panic(err) 199 } 200 } 201 b.Log("data size", len(data)) 202 b.Log("decode", decoded) 203 } 204 205 func TestV10IndexQueries(t *testing.T) { 206 fromShards := func(n int) (res []Query) { 207 for i := 0; i < n; i++ { 208 res = append(res, Query{ 209 TableName: "tbl", 210 HashValue: fmt.Sprintf("%02d:%s:%s:%s", i, "hash", "metric", "label"), 211 RangeValueStart: []byte(fmt.Sprint(i)), 212 ValueEqual: []byte(fmt.Sprint(i)), 213 }) 214 } 215 return res 216 } 217 218 testExprs := []struct { 219 name string 220 queries []Query 221 shard *astmapper.ShardAnnotation 222 expected []Query 223 }{ 224 { 225 name: "passthrough when no shard specified", 226 queries: fromShards(2), 227 shard: nil, 228 expected: fromShards(2), 229 }, 230 { 231 name: "out of bounds shard returns 0 matches", 232 queries: fromShards(2), 233 shard: &astmapper.ShardAnnotation{ 234 Shard: 3, 235 }, 236 expected: nil, 237 }, 238 { 239 name: "return correct shard", 240 queries: fromShards(3), 241 shard: &astmapper.ShardAnnotation{ 242 Shard: 1, 243 }, 244 expected: []Query{fromShards(2)[1]}, 245 }, 246 } 247 248 for _, c := range testExprs { 249 t.Run(c.name, func(t *testing.T) { 250 s := v10Entries{} 251 filtered := s.FilterReadQueries(c.queries, c.shard) 252 require.Equal(t, c.expected, filtered) 253 }) 254 } 255 }