github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/series/series_store_test.go (about) 1 package series_test 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "sort" 8 "testing" 9 "time" 10 11 "github.com/go-kit/log" 12 "github.com/grafana/dskit/flagext" 13 "github.com/prometheus/client_golang/prometheus" 14 "github.com/prometheus/common/model" 15 "github.com/prometheus/prometheus/model/labels" 16 "github.com/prometheus/prometheus/promql/parser" 17 "github.com/stretchr/testify/require" 18 "github.com/weaveworks/common/test" 19 "github.com/weaveworks/common/user" 20 21 "github.com/grafana/loki/pkg/ingester/client" 22 "github.com/grafana/loki/pkg/logqlmodel/stats" 23 "github.com/grafana/loki/pkg/storage" 24 "github.com/grafana/loki/pkg/storage/chunk" 25 "github.com/grafana/loki/pkg/storage/chunk/cache" 26 "github.com/grafana/loki/pkg/storage/chunk/client/testutils" 27 "github.com/grafana/loki/pkg/storage/config" 28 "github.com/grafana/loki/pkg/storage/stores/series/index" 29 "github.com/grafana/loki/pkg/validation" 30 ) 31 32 type configFactory func() config.ChunkStoreConfig 33 34 const userID = "1" 35 36 var ( 37 ctx = user.InjectOrgID(context.Background(), userID) 38 schemas = []string{"v9", "v10", "v11", "v12"} 39 stores = []struct { 40 name string 41 configFn configFactory 42 }{ 43 { 44 name: "store", 45 configFn: func() config.ChunkStoreConfig { 46 var storeCfg config.ChunkStoreConfig 47 flagext.DefaultValues(&storeCfg) 48 return storeCfg 49 }, 50 }, 51 { 52 name: "cached_store", 53 configFn: func() config.ChunkStoreConfig { 54 var storeCfg config.ChunkStoreConfig 55 flagext.DefaultValues(&storeCfg) 56 storeCfg.WriteDedupeCacheConfig.Cache = cache.NewFifoCache("test", cache.FifoCacheConfig{ 57 MaxSizeItems: 500, 58 }, prometheus.NewRegistry(), log.NewNopLogger(), stats.ChunkCache) 59 return storeCfg 60 }, 61 }, 62 } 63 cm = storage.NewClientMetrics() 64 ) 65 66 // newTestStore creates a new Store for testing. 67 func newTestChunkStore(t require.TestingT, schemaName string) (storage.Store, config.SchemaConfig) { 68 var storeCfg config.ChunkStoreConfig 69 flagext.DefaultValues(&storeCfg) 70 return newTestChunkStoreConfig(t, schemaName, storeCfg) 71 } 72 73 func newTestChunkStoreConfig(t require.TestingT, schemaName string, storeCfg config.ChunkStoreConfig) (storage.Store, config.SchemaConfig) { 74 schemaCfg := testutils.SchemaConfig("", schemaName, 0) 75 schemaCfg.Configs[0].IndexType = "inmemory" 76 schemaCfg.Configs[0].ObjectType = "inmemory" 77 78 return newTestChunkStoreConfigWithMockStorage(t, schemaCfg, storeCfg), schemaCfg 79 } 80 81 func newTestChunkStoreConfigWithMockStorage(t require.TestingT, schemaCfg config.SchemaConfig, storeCfg config.ChunkStoreConfig) storage.Store { 82 testutils.ResetMockStorage() 83 var tbmConfig index.TableManagerConfig 84 err := schemaCfg.Validate() 85 require.NoError(t, err) 86 flagext.DefaultValues(&tbmConfig) 87 88 limits, err := validation.NewOverrides(validation.Limits{ 89 MaxQueryLength: model.Duration(30 * 24 * time.Hour), 90 }, nil) 91 require.NoError(t, err) 92 93 store, err := storage.NewStore(storage.Config{MaxChunkBatchSize: 1}, storeCfg, schemaCfg, limits, cm, prometheus.NewRegistry(), log.NewNopLogger()) 94 require.NoError(t, err) 95 tm, err := index.NewTableManager(tbmConfig, schemaCfg, 12*time.Hour, testutils.NewMockStorage(), nil, nil, nil) 96 require.NoError(t, err) 97 _ = tm.SyncTables(context.Background()) 98 return store 99 } 100 101 func TestChunkStore_LabelValuesForMetricName(t *testing.T) { 102 now := model.Now() 103 104 fooMetric1 := labels.Labels{ 105 {Name: labels.MetricName, Value: "foo"}, 106 {Name: "bar", Value: "baz"}, 107 {Name: "flip", Value: "flop"}, 108 {Name: "toms", Value: "code"}, 109 } 110 fooMetric2 := labels.Labels{ 111 {Name: labels.MetricName, Value: "foo"}, 112 {Name: "bar", Value: "beep"}, 113 {Name: "toms", Value: "code"}, 114 } 115 fooMetric3 := labels.Labels{ 116 {Name: labels.MetricName, Value: "foo"}, 117 {Name: "bar", Value: "bop"}, 118 {Name: "flip", Value: "flap"}, 119 } 120 121 // barMetric1 is a subset of barMetric2 to test over-matching bug. 122 barMetric1 := labels.Labels{ 123 {Name: labels.MetricName, Value: "bar"}, 124 {Name: "bar", Value: "baz"}, 125 } 126 barMetric2 := labels.Labels{ 127 {Name: labels.MetricName, Value: "bar"}, 128 {Name: "bar", Value: "baz"}, 129 {Name: "toms", Value: "code"}, 130 } 131 132 fooChunk1 := dummyChunkFor(now, fooMetric1) 133 fooChunk2 := dummyChunkFor(now, fooMetric2) 134 fooChunk3 := dummyChunkFor(now, fooMetric3) 135 136 barChunk1 := dummyChunkFor(now, barMetric1) 137 barChunk2 := dummyChunkFor(now, barMetric2) 138 139 for _, tc := range []struct { 140 metricName, labelName string 141 expect []string 142 }{ 143 { 144 `foo`, `bar`, 145 []string{"baz", "beep", "bop"}, 146 }, 147 { 148 `bar`, `toms`, 149 []string{"code"}, 150 }, 151 { 152 `bar`, `bar`, 153 []string{"baz"}, 154 }, 155 { 156 `foo`, `foo`, 157 nil, 158 }, 159 { 160 `foo`, `flip`, 161 []string{"flap", "flop"}, 162 }, 163 } { 164 for _, schema := range schemas { 165 for _, storeCase := range stores { 166 t.Run(fmt.Sprintf("%s / %s / %s / %s", tc.metricName, tc.labelName, schema, storeCase.name), func(t *testing.T) { 167 t.Log("========= Running labelValues with metricName", tc.metricName, "with labelName", tc.labelName, "with schema", schema) 168 storeCfg := storeCase.configFn() 169 store, _ := newTestChunkStoreConfig(t, schema, storeCfg) 170 defer store.Stop() 171 172 if err := store.Put(ctx, []chunk.Chunk{ 173 fooChunk1, 174 fooChunk2, 175 fooChunk3, 176 barChunk1, 177 barChunk2, 178 }); err != nil { 179 t.Fatal(err) 180 } 181 182 // Query with ordinary time-range 183 labelValues1, err := store.LabelValuesForMetricName(ctx, userID, now.Add(-time.Hour), now, tc.metricName, tc.labelName) 184 require.NoError(t, err) 185 186 if !reflect.DeepEqual(tc.expect, labelValues1) { 187 t.Fatalf("%s/%s: wrong label values - %s", tc.metricName, tc.labelName, test.Diff(tc.expect, labelValues1)) 188 } 189 190 // Pushing end of time-range into future should yield exact same resultset 191 labelValues2, err := store.LabelValuesForMetricName(ctx, userID, now.Add(-time.Hour), now.Add(time.Hour*24*10), tc.metricName, tc.labelName) 192 require.NoError(t, err) 193 194 if !reflect.DeepEqual(tc.expect, labelValues2) { 195 t.Fatalf("%s/%s: wrong label values - %s", tc.metricName, tc.labelName, test.Diff(tc.expect, labelValues2)) 196 } 197 198 // Query with both begin & end of time-range in future should yield empty resultset 199 labelValues3, err := store.LabelValuesForMetricName(ctx, userID, now.Add(time.Hour), now.Add(time.Hour*2), tc.metricName, tc.labelName) 200 require.NoError(t, err) 201 if len(labelValues3) != 0 { 202 t.Fatalf("%s/%s: future query should yield empty resultset ... actually got %v label values: %#v", 203 tc.metricName, tc.labelName, len(labelValues3), labelValues3) 204 } 205 }) 206 } 207 } 208 } 209 } 210 211 func TestChunkStore_LabelNamesForMetricName(t *testing.T) { 212 now := model.Now() 213 214 fooMetric1 := labels.Labels{ 215 {Name: labels.MetricName, Value: "foo"}, 216 {Name: "bar", Value: "baz"}, 217 {Name: "flip", Value: "flop"}, 218 {Name: "toms", Value: "code"}, 219 } 220 fooMetric2 := labels.Labels{ 221 {Name: labels.MetricName, Value: "foo"}, 222 {Name: "bar", Value: "beep"}, 223 {Name: "toms", Value: "code"}, 224 } 225 fooMetric3 := labels.Labels{ 226 {Name: labels.MetricName, Value: "foo"}, 227 {Name: "bar", Value: "bop"}, 228 {Name: "flip", Value: "flap"}, 229 } 230 231 // barMetric1 is a subset of barMetric2 to test over-matching bug. 232 barMetric1 := labels.Labels{ 233 {Name: labels.MetricName, Value: "bar"}, 234 {Name: "bar", Value: "baz"}, 235 } 236 barMetric2 := labels.Labels{ 237 {Name: labels.MetricName, Value: "bar"}, 238 {Name: "bar", Value: "baz"}, 239 {Name: "toms", Value: "code"}, 240 } 241 242 fooChunk1 := dummyChunkFor(now, fooMetric1) 243 fooChunk2 := dummyChunkFor(now, fooMetric2) 244 fooChunk3 := dummyChunkFor(now, fooMetric3) 245 fooChunk4 := dummyChunkFor(now.Add(-time.Hour), fooMetric1) // same series but different chunk 246 247 barChunk1 := dummyChunkFor(now, barMetric1) 248 barChunk2 := dummyChunkFor(now, barMetric2) 249 250 for _, tc := range []struct { 251 metricName string 252 expect []string 253 }{ 254 { 255 `foo`, 256 []string{"bar", "flip", "toms"}, 257 }, 258 { 259 `bar`, 260 []string{"bar", "toms"}, 261 }, 262 } { 263 for _, schema := range schemas { 264 for _, storeCase := range stores { 265 t.Run(fmt.Sprintf("%s / %s / %s ", tc.metricName, schema, storeCase.name), func(t *testing.T) { 266 t.Log("========= Running labelNames with metricName", tc.metricName, "with schema", schema) 267 storeCfg := storeCase.configFn() 268 store, _ := newTestChunkStoreConfig(t, schema, storeCfg) 269 defer store.Stop() 270 271 if err := store.Put(ctx, []chunk.Chunk{ 272 fooChunk1, 273 fooChunk2, 274 fooChunk3, 275 fooChunk4, 276 barChunk1, 277 barChunk2, 278 }); err != nil { 279 t.Fatal(err) 280 } 281 282 // Query with ordinary time-range 283 labelNames1, err := store.LabelNamesForMetricName(ctx, userID, now.Add(-time.Hour), now, tc.metricName) 284 require.NoError(t, err) 285 286 if !reflect.DeepEqual(tc.expect, labelNames1) { 287 t.Fatalf("%s: wrong label name - %s", tc.metricName, test.Diff(tc.expect, labelNames1)) 288 } 289 290 // Pushing end of time-range into future should yield exact same resultset 291 labelNames2, err := store.LabelNamesForMetricName(ctx, userID, now.Add(-time.Hour), now.Add(time.Hour*24*10), tc.metricName) 292 require.NoError(t, err) 293 294 if !reflect.DeepEqual(tc.expect, labelNames2) { 295 t.Fatalf("%s: wrong label name - %s", tc.metricName, test.Diff(tc.expect, labelNames2)) 296 } 297 298 // Query with both begin & end of time-range in future should yield empty resultset 299 labelNames3, err := store.LabelNamesForMetricName(ctx, userID, now.Add(time.Hour), now.Add(time.Hour*2), tc.metricName) 300 require.NoError(t, err) 301 if len(labelNames3) != 0 { 302 t.Fatalf("%s: future query should yield empty resultset ... actually got %v label names: %#v", 303 tc.metricName, len(labelNames3), labelNames3) 304 } 305 }) 306 } 307 } 308 } 309 } 310 311 // TestChunkStore_getMetricNameChunks tests if chunks are fetched correctly when we have the metric name 312 func TestChunkStore_getMetricNameChunks(t *testing.T) { 313 now := model.Now() 314 chunk1 := dummyChunkFor(now, labels.Labels{ 315 {Name: labels.MetricName, Value: "foo"}, 316 {Name: "bar", Value: "baz"}, 317 {Name: "flip", Value: "flop"}, 318 {Name: "toms", Value: "code"}, 319 }) 320 chunk2 := dummyChunkFor(now, labels.Labels{ 321 {Name: labels.MetricName, Value: "foo"}, 322 {Name: "bar", Value: "beep"}, 323 {Name: "toms", Value: "code"}, 324 }) 325 326 testCases := []struct { 327 query string 328 expect []chunk.Chunk 329 }{ 330 { 331 `foo`, 332 []chunk.Chunk{chunk1, chunk2}, 333 }, 334 { 335 `foo{flip=""}`, 336 []chunk.Chunk{chunk2}, 337 }, 338 { 339 `foo{bar="baz"}`, 340 []chunk.Chunk{chunk1}, 341 }, 342 { 343 `foo{bar="beep"}`, 344 []chunk.Chunk{chunk2}, 345 }, 346 { 347 `foo{toms="code"}`, 348 []chunk.Chunk{chunk1, chunk2}, 349 }, 350 { 351 `foo{bar!="baz"}`, 352 []chunk.Chunk{chunk2}, 353 }, 354 { 355 `foo{bar=~"beep|baz"}`, 356 []chunk.Chunk{chunk1, chunk2}, 357 }, 358 { 359 `foo{bar=~"beeping|baz"}`, 360 []chunk.Chunk{chunk1}, 361 }, 362 { 363 `foo{toms="code", bar=~"beep|baz"}`, 364 []chunk.Chunk{chunk1, chunk2}, 365 }, 366 { 367 `foo{toms="code", bar="baz"}`, 368 []chunk.Chunk{chunk1}, 369 }, 370 } 371 for _, schema := range schemas { 372 for _, storeCase := range stores { 373 storeCfg := storeCase.configFn() 374 375 store, schemaCfg := newTestChunkStoreConfig(t, schema, storeCfg) 376 defer store.Stop() 377 378 if err := store.Put(ctx, []chunk.Chunk{chunk1, chunk2}); err != nil { 379 t.Fatal(err) 380 } 381 382 for _, tc := range testCases { 383 t.Run(fmt.Sprintf("%s / %s / %s", tc.query, schema, storeCase.name), func(t *testing.T) { 384 t.Log("========= Running query", tc.query, "with schema", schema) 385 matchers, err := parser.ParseMetricSelector(tc.query) 386 if err != nil { 387 t.Fatal(err) 388 } 389 390 chunks, fetchers, err := store.GetChunkRefs(ctx, userID, now.Add(-time.Hour), now, matchers...) 391 require.NoError(t, err) 392 fetchedChunk := []chunk.Chunk{} 393 for _, f := range fetchers { 394 for _, cs := range chunks { 395 keys := make([]string, 0, len(cs)) 396 sort.Slice(chunks, func(i, j int) bool { 397 return schemaCfg.ExternalKey(cs[i].ChunkRef) < schemaCfg.ExternalKey(cs[j].ChunkRef) 398 }) 399 400 for _, c := range cs { 401 keys = append(keys, schemaCfg.ExternalKey(c.ChunkRef)) 402 } 403 cks, err := f.FetchChunks(ctx, cs, keys) 404 if err != nil { 405 t.Fatal(err) 406 } 407 outer: 408 for _, c := range cks { 409 for _, matcher := range matchers { 410 if !matcher.Matches(c.Metric.Get(matcher.Name)) { 411 continue outer 412 } 413 } 414 fetchedChunk = append(fetchedChunk, c) 415 } 416 417 } 418 } 419 420 for i, c := range fetchedChunk { 421 require.Equal(t, tc.expect[i].ChunkRef, c.ChunkRef) 422 require.Equal(t, tc.expect[i].Metric, c.Metric) 423 require.Equal(t, tc.expect[i].Encoding, c.Encoding) 424 ee, err := tc.expect[i].Encoded() 425 require.NoError(t, err) 426 fe, err := c.Encoded() 427 require.NoError(t, err) 428 require.Equal(t, ee, fe) 429 } 430 }) 431 } 432 } 433 } 434 } 435 436 func Test_GetSeries(t *testing.T) { 437 now := model.Now() 438 ch1lbs := labels.Labels{ 439 {Name: labels.MetricName, Value: "foo"}, 440 {Name: "bar", Value: "baz"}, 441 {Name: "flip", Value: "flop"}, 442 {Name: "toms", Value: "code"}, 443 } 444 chunk1 := dummyChunkFor(now, ch1lbs) 445 ch2lbs := labels.Labels{ 446 {Name: labels.MetricName, Value: "foo"}, 447 {Name: "bar", Value: "beep"}, 448 {Name: "toms", Value: "code"}, 449 } 450 chunk2 := dummyChunkFor(now, ch2lbs) 451 452 testCases := []struct { 453 query string 454 expect []labels.Labels 455 }{ 456 { 457 `foo`, 458 []labels.Labels{ 459 labels.NewBuilder(ch1lbs).Del(labels.MetricName).Labels(), 460 labels.NewBuilder(ch2lbs).Del(labels.MetricName).Labels(), 461 }, 462 }, 463 { 464 `foo{flip=""}`, 465 []labels.Labels{labels.NewBuilder(ch2lbs).Del(labels.MetricName).Labels()}, 466 }, 467 { 468 `foo{bar="baz"}`, 469 []labels.Labels{labels.NewBuilder(ch1lbs).Del(labels.MetricName).Labels()}, 470 }, 471 { 472 `foo{bar="beep"}`, 473 []labels.Labels{labels.NewBuilder(ch2lbs).Del(labels.MetricName).Labels()}, 474 }, 475 { 476 `foo{toms="code"}`, 477 []labels.Labels{ 478 labels.NewBuilder(ch1lbs).Del(labels.MetricName).Labels(), 479 labels.NewBuilder(ch2lbs).Del(labels.MetricName).Labels(), 480 }, 481 }, 482 { 483 `foo{bar!="baz"}`, 484 []labels.Labels{labels.NewBuilder(ch2lbs).Del(labels.MetricName).Labels()}, 485 }, 486 { 487 `foo{bar=~"beep|baz"}`, 488 []labels.Labels{ 489 labels.NewBuilder(ch1lbs).Del(labels.MetricName).Labels(), 490 labels.NewBuilder(ch2lbs).Del(labels.MetricName).Labels(), 491 }, 492 }, 493 { 494 `foo{bar=~"beeping|baz"}`, 495 []labels.Labels{labels.NewBuilder(ch1lbs).Del(labels.MetricName).Labels()}, 496 }, 497 { 498 `foo{toms="code", bar=~"beep|baz"}`, 499 []labels.Labels{ 500 labels.NewBuilder(ch1lbs).Del(labels.MetricName).Labels(), 501 labels.NewBuilder(ch2lbs).Del(labels.MetricName).Labels(), 502 }, 503 }, 504 { 505 `foo{toms="code", bar="baz"}`, 506 []labels.Labels{labels.NewBuilder(ch1lbs).Del(labels.MetricName).Labels()}, 507 }, 508 } 509 for _, schema := range schemas { 510 for _, storeCase := range stores { 511 storeCfg := storeCase.configFn() 512 513 store, _ := newTestChunkStoreConfig(t, schema, storeCfg) 514 defer store.Stop() 515 516 if err := store.Put(ctx, []chunk.Chunk{chunk1, chunk2}); err != nil { 517 t.Fatal(err) 518 } 519 520 for _, tc := range testCases { 521 t.Run(fmt.Sprintf("%s / %s / %s", tc.query, schema, storeCase.name), func(t *testing.T) { 522 t.Log("========= Running query", tc.query, "with schema", schema) 523 matchers, err := parser.ParseMetricSelector(tc.query) 524 if err != nil { 525 t.Fatal(err) 526 } 527 528 res, err := store.GetSeries(ctx, userID, now.Add(-time.Hour), now, matchers...) 529 require.NoError(t, err) 530 require.Equal(t, tc.expect, res) 531 }) 532 } 533 } 534 } 535 } 536 537 func Test_GetSeriesShard(t *testing.T) { 538 now := model.Now() 539 ch1lbs := labels.Labels{ 540 {Name: labels.MetricName, Value: "foo"}, 541 {Name: "bar", Value: "baz"}, 542 {Name: "flip", Value: "flop"}, 543 {Name: "toms", Value: "code"}, 544 } 545 chunk1 := dummyChunkFor(now, ch1lbs) 546 ch2lbs := labels.Labels{ 547 {Name: labels.MetricName, Value: "foo"}, 548 {Name: "bar", Value: "beep"}, 549 {Name: "toms", Value: "code"}, 550 } 551 chunk2 := dummyChunkFor(now, ch2lbs) 552 553 testCases := []struct { 554 query string 555 expect []labels.Labels 556 }{ 557 { 558 `foo{__cortex_shard__="6_of_16"}`, 559 []labels.Labels{labels.NewBuilder(ch2lbs).Del(labels.MetricName).Labels()}, 560 }, 561 { 562 `foo{__cortex_shard__="8_of_16"}`, 563 []labels.Labels{labels.NewBuilder(ch1lbs).Del(labels.MetricName).Labels()}, 564 }, 565 } 566 for _, storeCase := range stores { 567 storeCfg := storeCase.configFn() 568 569 store, _ := newTestChunkStoreConfig(t, "v12", storeCfg) 570 defer store.Stop() 571 572 if err := store.Put(ctx, []chunk.Chunk{chunk1, chunk2}); err != nil { 573 t.Fatal(err) 574 } 575 576 for _, tc := range testCases { 577 t.Run(fmt.Sprintf("%s / %s / %s", tc.query, "v12", storeCase.name), func(t *testing.T) { 578 t.Log("========= Running query", tc.query, "with schema", "v12") 579 matchers, err := parser.ParseMetricSelector(tc.query) 580 if err != nil { 581 t.Fatal(err) 582 } 583 584 res, err := store.GetSeries(ctx, userID, now.Add(-time.Hour), now, matchers...) 585 require.NoError(t, err) 586 require.Equal(t, tc.expect, res) 587 }) 588 } 589 } 590 } 591 592 // nolint 593 func mustNewLabelMatcher(matchType labels.MatchType, name string, value string) *labels.Matcher { 594 return labels.MustNewMatcher(matchType, name, value) 595 } 596 597 func BenchmarkIndexCaching(b *testing.B) { 598 ctx := context.Background() 599 storeMaker := stores[1] 600 storeCfg := storeMaker.configFn() 601 602 store, _ := newTestChunkStoreConfig(b, "v9", storeCfg) 603 defer store.Stop() 604 605 fooChunk1 := dummyChunkFor(model.Time(0).Add(15*time.Second), BenchmarkLabels) 606 607 b.ResetTimer() 608 609 for i := 0; i < b.N; i++ { 610 err := store.Put(ctx, []chunk.Chunk{fooChunk1}) 611 require.NoError(b, err) 612 } 613 } 614 615 func TestChunkStoreError(t *testing.T) { 616 ctx := context.Background() 617 for _, tc := range []struct { 618 query string 619 from, through model.Time 620 err string 621 }{ 622 { 623 query: "foo", 624 from: model.Time(0).Add(31 * 24 * time.Hour), 625 through: model.Time(0), 626 err: "invalid query, through < from (0 < 2678400)", 627 }, 628 { 629 query: "foo", 630 from: model.Time(0), 631 through: model.Time(0).Add(31 * 24 * time.Hour), 632 err: "the query time range exceeds the limit (query length: 744h0m0s, limit: 720h0m0s)", 633 }, 634 { 635 query: "{foo=\"bar\"}", 636 from: model.Time(0), 637 through: model.Time(0).Add(1 * time.Hour), 638 err: "query must contain metric name", 639 }, 640 { 641 query: "{__name__=~\"bar\"}", 642 from: model.Time(0), 643 through: model.Time(0).Add(1 * time.Hour), 644 err: "query must contain metric name", 645 }, 646 } { 647 for _, schema := range schemas { 648 t.Run(fmt.Sprintf("%s / %s", tc.query, schema), func(t *testing.T) { 649 store, _ := newTestChunkStore(t, schema) 650 defer store.Stop() 651 652 matchers, err := parser.ParseMetricSelector(tc.query) 653 require.NoError(t, err) 654 655 // Query with ordinary time-range 656 _, _, err = store.GetChunkRefs(ctx, userID, tc.from, tc.through, matchers...) 657 require.EqualError(t, err, tc.err) 658 }) 659 } 660 } 661 } 662 663 func TestSeriesStore_LabelValuesForMetricName(t *testing.T) { 664 now := model.Now() 665 666 fooMetric1 := labels.Labels{ 667 {Name: labels.MetricName, Value: "foo"}, 668 {Name: "bar", Value: "baz"}, 669 {Name: "flip", Value: "flop"}, 670 {Name: "toms", Value: "code"}, 671 {Name: "env", Value: "dev"}, 672 {Name: "class", Value: "not-secret"}, 673 } 674 fooMetric2 := labels.Labels{ 675 {Name: labels.MetricName, Value: "foo"}, 676 {Name: "bar", Value: "beep"}, 677 {Name: "toms", Value: "code"}, 678 {Name: "env", Value: "prod"}, 679 {Name: "class", Value: "secret"}, 680 } 681 682 fooChunk1 := dummyChunkFor(now, fooMetric1) 683 fooChunk2 := dummyChunkFor(now, fooMetric2) 684 685 for _, tc := range []struct { 686 metricName, labelName string 687 expect []string 688 matchers []*labels.Matcher 689 }{ 690 { 691 `foo`, `class`, 692 []string{"not-secret"}, 693 []*labels.Matcher{labels.MustNewMatcher(labels.MatchEqual, "env", "dev")}, 694 }, 695 { 696 `foo`, `bar`, 697 []string{"baz"}, 698 []*labels.Matcher{ 699 labels.MustNewMatcher(labels.MatchNotEqual, "env", "prod"), 700 labels.MustNewMatcher(labels.MatchEqual, "toms", "code"), 701 }, 702 }, 703 } { 704 for _, schema := range schemas { 705 for _, storeCase := range stores { 706 t.Run(fmt.Sprintf("%s / %s / %s / %s", tc.metricName, tc.labelName, schema, storeCase.name), func(t *testing.T) { 707 t.Log("========= Running labelValues with metricName", tc.metricName, "with labelName", tc.labelName, "with schema", schema) 708 storeCfg := storeCase.configFn() 709 store, _ := newTestChunkStoreConfig(t, schema, storeCfg) 710 defer store.Stop() 711 712 if err := store.Put(ctx, []chunk.Chunk{ 713 fooChunk1, 714 fooChunk2, 715 }); err != nil { 716 t.Fatal(err) 717 } 718 719 // Query with ordinary time-range 720 labelValues1, err := store.LabelValuesForMetricName(ctx, userID, now.Add(-time.Hour), now, tc.metricName, tc.labelName, tc.matchers...) 721 require.NoError(t, err) 722 require.ElementsMatch(t, tc.expect, labelValues1) 723 724 // Pushing end of time-range into future should yield exact same resultset 725 labelValues2, err := store.LabelValuesForMetricName(ctx, userID, now.Add(-time.Hour), now.Add(time.Hour*24*10), tc.metricName, tc.labelName, tc.matchers...) 726 require.NoError(t, err) 727 require.ElementsMatch(t, tc.expect, labelValues2) 728 }) 729 } 730 } 731 } 732 } 733 734 func dummyChunkForEncoding(now model.Time, metric labels.Labels, samples int) chunk.Chunk { 735 c, _ := chunk.NewForEncoding(chunk.Bigchunk) 736 chunkStart := now.Add(-time.Hour) 737 738 for i := 0; i < samples; i++ { 739 t := time.Duration(i) * 15 * time.Second 740 nc, err := c.Add(model.SamplePair{Timestamp: chunkStart.Add(t), Value: model.SampleValue(i)}) 741 if err != nil { 742 panic(err) 743 } 744 if nc != nil { 745 panic("returned chunk was not nil") 746 } 747 } 748 749 chunk := chunk.NewChunk( 750 userID, 751 client.Fingerprint(metric), 752 metric, 753 c, 754 chunkStart, 755 now, 756 ) 757 // Force checksum calculation. 758 err := chunk.Encode() 759 if err != nil { 760 panic(err) 761 } 762 return chunk 763 } 764 765 func dummyChunkFor(now model.Time, metric labels.Labels) chunk.Chunk { 766 return dummyChunkForEncoding(now, metric, 1) 767 } 768 769 // BenchmarkLabels is a real example from Kubernetes' embedded cAdvisor metrics, lightly obfuscated 770 var BenchmarkLabels = labels.Labels{ 771 {Name: model.MetricNameLabel, Value: "container_cpu_usage_seconds_total"}, 772 {Name: "beta_kubernetes_io_arch", Value: "amd64"}, 773 {Name: "beta_kubernetes_io_instance_type", Value: "c3.somesize"}, 774 {Name: "beta_kubernetes_io_os", Value: "linux"}, 775 {Name: "container_name", Value: "some-name"}, 776 {Name: "cpu", Value: "cpu01"}, 777 {Name: "failure_domain_beta_kubernetes_io_region", Value: "somewhere-1"}, 778 {Name: "failure_domain_beta_kubernetes_io_zone", Value: "somewhere-1b"}, 779 {Name: "id", Value: "/kubepods/burstable/pod6e91c467-e4c5-11e7-ace3-0a97ed59c75e/a3c8498918bd6866349fed5a6f8c643b77c91836427fb6327913276ebc6bde28"}, 780 {Name: "image", Value: "registry/organisation/name@sha256:dca3d877a80008b45d71d7edc4fd2e44c0c8c8e7102ba5cbabec63a374d1d506"}, 781 {Name: "instance", Value: "ip-111-11-1-11.ec2.internal"}, 782 {Name: "job", Value: "kubernetes-cadvisor"}, 783 {Name: "kubernetes_io_hostname", Value: "ip-111-11-1-11"}, 784 {Name: "monitor", Value: "prod"}, 785 {Name: "name", Value: "k8s_some-name_some-other-name-5j8s8_kube-system_6e91c467-e4c5-11e7-ace3-0a97ed59c75e_0"}, 786 {Name: "namespace", Value: "kube-system"}, 787 {Name: "pod_name", Value: "some-other-name-5j8s8"}, 788 }