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  }