github.com/thanos-io/thanos@v0.32.5/pkg/store/bucket_e2e_test.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package store
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"math"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  	"sync"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/alecthomas/units"
    18  	"github.com/go-kit/log"
    19  	"github.com/gogo/status"
    20  	"github.com/oklog/ulid"
    21  	"github.com/prometheus/client_golang/prometheus"
    22  	dto "github.com/prometheus/client_model/go"
    23  	"github.com/prometheus/prometheus/model/labels"
    24  	"github.com/prometheus/prometheus/model/relabel"
    25  	"github.com/prometheus/prometheus/model/timestamp"
    26  	"github.com/prometheus/prometheus/storage"
    27  	"google.golang.org/grpc/codes"
    28  
    29  	"github.com/thanos-io/objstore"
    30  	"github.com/thanos-io/objstore/objtesting"
    31  
    32  	"github.com/efficientgo/core/testutil"
    33  
    34  	"github.com/thanos-io/thanos/pkg/block"
    35  	"github.com/thanos-io/thanos/pkg/block/metadata"
    36  	"github.com/thanos-io/thanos/pkg/model"
    37  	storecache "github.com/thanos-io/thanos/pkg/store/cache"
    38  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    39  	"github.com/thanos-io/thanos/pkg/store/storepb"
    40  	"github.com/thanos-io/thanos/pkg/testutil/e2eutil"
    41  )
    42  
    43  var (
    44  	minTime            = time.Unix(0, 0)
    45  	maxTime, _         = time.Parse(time.RFC3339, "9999-12-31T23:59:59Z")
    46  	minTimeDuration    = model.TimeOrDurationValue{Time: &minTime}
    47  	maxTimeDuration    = model.TimeOrDurationValue{Time: &maxTime}
    48  	allowAllFilterConf = &FilterConfig{
    49  		MinTime: minTimeDuration,
    50  		MaxTime: maxTimeDuration,
    51  	}
    52  )
    53  
    54  type swappableCache struct {
    55  	ptr storecache.IndexCache
    56  }
    57  
    58  func (c *swappableCache) SwapWith(ptr2 storecache.IndexCache) {
    59  	c.ptr = ptr2
    60  }
    61  
    62  func (c *swappableCache) StorePostings(blockID ulid.ULID, l labels.Label, v []byte) {
    63  	c.ptr.StorePostings(blockID, l, v)
    64  }
    65  
    66  func (c *swappableCache) FetchMultiPostings(ctx context.Context, blockID ulid.ULID, keys []labels.Label) (map[labels.Label][]byte, []labels.Label) {
    67  	return c.ptr.FetchMultiPostings(ctx, blockID, keys)
    68  }
    69  
    70  func (c *swappableCache) StoreExpandedPostings(blockID ulid.ULID, matchers []*labels.Matcher, v []byte) {
    71  	c.ptr.StoreExpandedPostings(blockID, matchers, v)
    72  }
    73  
    74  func (c *swappableCache) FetchExpandedPostings(ctx context.Context, blockID ulid.ULID, matchers []*labels.Matcher) ([]byte, bool) {
    75  	return c.ptr.FetchExpandedPostings(ctx, blockID, matchers)
    76  }
    77  
    78  func (c *swappableCache) StoreSeries(blockID ulid.ULID, id storage.SeriesRef, v []byte) {
    79  	c.ptr.StoreSeries(blockID, id, v)
    80  }
    81  
    82  func (c *swappableCache) FetchMultiSeries(ctx context.Context, blockID ulid.ULID, ids []storage.SeriesRef) (map[storage.SeriesRef][]byte, []storage.SeriesRef) {
    83  	return c.ptr.FetchMultiSeries(ctx, blockID, ids)
    84  }
    85  
    86  type storeSuite struct {
    87  	store            *BucketStore
    88  	minTime, maxTime int64
    89  	cache            *swappableCache
    90  
    91  	logger log.Logger
    92  }
    93  
    94  func prepareTestBlocks(t testing.TB, now time.Time, count int, dir string, bkt objstore.Bucket,
    95  	series []labels.Labels, extLset labels.Labels) (minTime, maxTime int64) {
    96  	ctx := context.Background()
    97  	logger := log.NewNopLogger()
    98  
    99  	for i := 0; i < count; i++ {
   100  		mint := timestamp.FromTime(now)
   101  		now = now.Add(2 * time.Hour)
   102  		maxt := timestamp.FromTime(now)
   103  
   104  		if minTime == 0 {
   105  			minTime = mint
   106  		}
   107  		maxTime = maxt
   108  
   109  		// Create two blocks per time slot. Only add 10 samples each so only one chunk
   110  		// gets created each. This way we can easily verify we got 10 chunks per series below.
   111  		id1, err := e2eutil.CreateBlock(ctx, dir, series[:4], 10, mint, maxt, extLset, 0, metadata.NoneFunc)
   112  		testutil.Ok(t, err)
   113  		id2, err := e2eutil.CreateBlock(ctx, dir, series[4:], 10, mint, maxt, extLset, 0, metadata.NoneFunc)
   114  		testutil.Ok(t, err)
   115  
   116  		dir1, dir2 := filepath.Join(dir, id1.String()), filepath.Join(dir, id2.String())
   117  
   118  		// Replace labels to the meta of the second block.
   119  		meta, err := metadata.ReadFromDir(dir2)
   120  		testutil.Ok(t, err)
   121  		meta.Thanos.Labels = map[string]string{"ext2": "value2"}
   122  		testutil.Ok(t, meta.WriteToDir(logger, dir2))
   123  
   124  		testutil.Ok(t, block.Upload(ctx, logger, bkt, dir1, metadata.NoneFunc))
   125  		testutil.Ok(t, block.Upload(ctx, logger, bkt, dir2, metadata.NoneFunc))
   126  
   127  		testutil.Ok(t, os.RemoveAll(dir1))
   128  		testutil.Ok(t, os.RemoveAll(dir2))
   129  	}
   130  
   131  	return
   132  }
   133  
   134  func prepareStoreWithTestBlocks(t testing.TB, dir string, bkt objstore.Bucket, manyParts bool, chunksLimiterFactory ChunksLimiterFactory, seriesLimiterFactory SeriesLimiterFactory, bytesLimiterFactory BytesLimiterFactory, relabelConfig []*relabel.Config, filterConf *FilterConfig) *storeSuite {
   135  	series := []labels.Labels{
   136  		labels.FromStrings("a", "1", "b", "1"),
   137  		labels.FromStrings("a", "1", "b", "2"),
   138  		labels.FromStrings("a", "2", "b", "1"),
   139  		labels.FromStrings("a", "2", "b", "2"),
   140  		labels.FromStrings("a", "1", "c", "1"),
   141  		labels.FromStrings("a", "1", "c", "2"),
   142  		labels.FromStrings("a", "2", "c", "1"),
   143  		labels.FromStrings("a", "2", "c", "2"),
   144  	}
   145  	extLset := labels.FromStrings("ext1", "value1")
   146  
   147  	minTime, maxTime := prepareTestBlocks(t, time.Now(), 3, dir, bkt, series, extLset)
   148  
   149  	s := &storeSuite{
   150  		logger:  log.NewLogfmtLogger(os.Stderr),
   151  		cache:   &swappableCache{},
   152  		minTime: minTime,
   153  		maxTime: maxTime,
   154  	}
   155  
   156  	metaFetcher, err := block.NewMetaFetcher(s.logger, 20, objstore.WithNoopInstr(bkt), dir, nil, []block.MetadataFilter{
   157  		block.NewTimePartitionMetaFilter(filterConf.MinTime, filterConf.MaxTime),
   158  		block.NewLabelShardedMetaFilter(relabelConfig),
   159  	})
   160  	testutil.Ok(t, err)
   161  
   162  	reg := prometheus.NewRegistry()
   163  	store, err := NewBucketStore(
   164  		objstore.WithNoopInstr(bkt),
   165  		metaFetcher,
   166  		dir,
   167  		chunksLimiterFactory,
   168  		seriesLimiterFactory,
   169  		bytesLimiterFactory,
   170  		NewGapBasedPartitioner(PartitionerMaxGapSize),
   171  		20,
   172  		true,
   173  		DefaultPostingOffsetInMemorySampling,
   174  		true,
   175  		true,
   176  		time.Minute,
   177  		WithLogger(s.logger),
   178  		WithIndexCache(s.cache),
   179  		WithFilterConfig(filterConf),
   180  		WithRegistry(reg),
   181  	)
   182  	testutil.Ok(t, err)
   183  	defer func() { testutil.Ok(t, store.Close()) }()
   184  
   185  	s.store = store
   186  
   187  	if manyParts {
   188  		s.store.partitioner = naivePartitioner{}
   189  	}
   190  
   191  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   192  	defer cancel()
   193  
   194  	// Check if the blocks are being loaded.
   195  	start := time.Now()
   196  	time.Sleep(time.Second * 1)
   197  	testutil.Ok(t, store.SyncBlocks(ctx))
   198  
   199  	// Get the value of the metric 'thanos_bucket_store_blocks_last_loaded_timestamp_seconds' to capture the timestamp of the last loaded block.
   200  	m := gatherFamily(t, reg, "thanos_bucket_store_blocks_last_loaded_timestamp_seconds")
   201  	lastUploaded := time.Unix(int64(m.Metric[0].Gauge.GetValue()), 0)
   202  
   203  	if lastUploaded.Before(start) {
   204  		t.Fatalf("no blocks are loaded ")
   205  	}
   206  
   207  	return s
   208  }
   209  
   210  func gatherFamily(t testing.TB, reg prometheus.Gatherer, familyName string) *dto.MetricFamily {
   211  
   212  	families, err := reg.Gather()
   213  	testutil.Ok(t, err)
   214  
   215  	for _, f := range families {
   216  		if f.GetName() == familyName {
   217  			return f
   218  		}
   219  	}
   220  
   221  	t.Fatalf("could not find family %s", familyName)
   222  	return nil
   223  }
   224  
   225  func testBucketStore_e2e(t *testing.T, ctx context.Context, s *storeSuite) {
   226  	t.Helper()
   227  
   228  	mint, maxt := s.store.TimeRange()
   229  	testutil.Equals(t, s.minTime, mint)
   230  	testutil.Equals(t, s.maxTime, maxt)
   231  
   232  	vals, err := s.store.LabelValues(ctx, &storepb.LabelValuesRequest{
   233  		Label: "a",
   234  		Start: timestamp.FromTime(minTime),
   235  		End:   timestamp.FromTime(maxTime),
   236  	})
   237  	testutil.Ok(t, err)
   238  	testutil.Equals(t, []string{"1", "2"}, vals.Values)
   239  
   240  	// TODO(bwplotka): Add those test cases to TSDB querier_test.go as well, there are no tests for matching.
   241  	for i, tcase := range []struct {
   242  		req              *storepb.SeriesRequest
   243  		expected         [][]labelpb.ZLabel
   244  		expectedChunkLen int
   245  	}{
   246  		{
   247  			req: &storepb.SeriesRequest{
   248  				Matchers: []storepb.LabelMatcher{
   249  					{Type: storepb.LabelMatcher_RE, Name: "a", Value: "1|2"},
   250  				},
   251  				MinTime: mint,
   252  				MaxTime: maxt,
   253  			},
   254  			expectedChunkLen: 3,
   255  			expected: [][]labelpb.ZLabel{
   256  				{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   257  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   258  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   259  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   260  				{{Name: "a", Value: "2"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   261  				{{Name: "a", Value: "2"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   262  				{{Name: "a", Value: "2"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   263  				{{Name: "a", Value: "2"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   264  			},
   265  		},
   266  		{
   267  			req: &storepb.SeriesRequest{
   268  				Matchers: []storepb.LabelMatcher{
   269  					{Type: storepb.LabelMatcher_RE, Name: "a", Value: "1|2"},
   270  				},
   271  				MinTime:              mint,
   272  				MaxTime:              maxt,
   273  				WithoutReplicaLabels: []string{"ext1", "ext2"},
   274  			},
   275  			expectedChunkLen: 3,
   276  			expected: [][]labelpb.ZLabel{
   277  				{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}},
   278  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}},
   279  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}},
   280  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}},
   281  				{{Name: "a", Value: "2"}, {Name: "b", Value: "1"}},
   282  				{{Name: "a", Value: "2"}, {Name: "b", Value: "2"}},
   283  				{{Name: "a", Value: "2"}, {Name: "c", Value: "1"}},
   284  				{{Name: "a", Value: "2"}, {Name: "c", Value: "2"}},
   285  			},
   286  		},
   287  		{
   288  			req: &storepb.SeriesRequest{
   289  				Matchers: []storepb.LabelMatcher{
   290  					{Type: storepb.LabelMatcher_RE, Name: "a", Value: "1"},
   291  				},
   292  				MinTime: mint,
   293  				MaxTime: maxt,
   294  			},
   295  			expectedChunkLen: 3,
   296  			expected: [][]labelpb.ZLabel{
   297  				{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   298  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   299  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   300  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   301  			},
   302  		},
   303  		{
   304  			req: &storepb.SeriesRequest{
   305  				Matchers: []storepb.LabelMatcher{
   306  					{Type: storepb.LabelMatcher_NRE, Name: "a", Value: "2"},
   307  				},
   308  				MinTime: mint,
   309  				MaxTime: maxt,
   310  			},
   311  			expectedChunkLen: 3,
   312  			expected: [][]labelpb.ZLabel{
   313  				{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   314  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   315  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   316  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   317  			},
   318  		},
   319  		{
   320  			req: &storepb.SeriesRequest{
   321  				Matchers: []storepb.LabelMatcher{
   322  					{Type: storepb.LabelMatcher_NRE, Name: "a", Value: "not_existing"},
   323  				},
   324  				MinTime: mint,
   325  				MaxTime: maxt,
   326  			},
   327  			expectedChunkLen: 3,
   328  			expected: [][]labelpb.ZLabel{
   329  				{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   330  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   331  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   332  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   333  				{{Name: "a", Value: "2"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   334  				{{Name: "a", Value: "2"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   335  				{{Name: "a", Value: "2"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   336  				{{Name: "a", Value: "2"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   337  			},
   338  		},
   339  		{
   340  			req: &storepb.SeriesRequest{
   341  				Matchers: []storepb.LabelMatcher{
   342  					{Type: storepb.LabelMatcher_NRE, Name: "not_existing", Value: "1"},
   343  				},
   344  				MinTime: mint,
   345  				MaxTime: maxt,
   346  			},
   347  			expectedChunkLen: 3,
   348  			expected: [][]labelpb.ZLabel{
   349  				{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   350  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   351  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   352  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   353  				{{Name: "a", Value: "2"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   354  				{{Name: "a", Value: "2"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   355  				{{Name: "a", Value: "2"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   356  				{{Name: "a", Value: "2"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   357  			},
   358  		},
   359  		{
   360  			req: &storepb.SeriesRequest{
   361  				Matchers: []storepb.LabelMatcher{
   362  					{Type: storepb.LabelMatcher_EQ, Name: "b", Value: "2"},
   363  				},
   364  				MinTime: mint,
   365  				MaxTime: maxt,
   366  			},
   367  			expectedChunkLen: 3,
   368  			expected: [][]labelpb.ZLabel{
   369  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   370  				{{Name: "a", Value: "2"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   371  			},
   372  		},
   373  		{
   374  			// Matching by external label should work as well.
   375  			req: &storepb.SeriesRequest{
   376  				Matchers: []storepb.LabelMatcher{
   377  					{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "1"},
   378  					{Type: storepb.LabelMatcher_EQ, Name: "ext2", Value: "value2"},
   379  				},
   380  				MinTime: mint,
   381  				MaxTime: maxt,
   382  			},
   383  			expectedChunkLen: 3,
   384  			expected: [][]labelpb.ZLabel{
   385  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   386  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   387  			},
   388  		},
   389  		{
   390  			req: &storepb.SeriesRequest{
   391  				Matchers: []storepb.LabelMatcher{
   392  					{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "1"},
   393  					{Type: storepb.LabelMatcher_EQ, Name: "ext2", Value: "wrong-value"},
   394  				},
   395  				MinTime: mint,
   396  				MaxTime: maxt,
   397  			},
   398  		},
   399  		{
   400  			req: &storepb.SeriesRequest{
   401  				Matchers: []storepb.LabelMatcher{
   402  					{Type: storepb.LabelMatcher_NEQ, Name: "a", Value: "2"},
   403  				},
   404  				MinTime: mint,
   405  				MaxTime: maxt,
   406  			},
   407  			expectedChunkLen: 3,
   408  			expected: [][]labelpb.ZLabel{
   409  				{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   410  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   411  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   412  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   413  			},
   414  		},
   415  		{
   416  			req: &storepb.SeriesRequest{
   417  				Matchers: []storepb.LabelMatcher{
   418  					{Type: storepb.LabelMatcher_NEQ, Name: "a", Value: "not_existing"},
   419  				},
   420  				MinTime: mint,
   421  				MaxTime: maxt,
   422  			},
   423  			expectedChunkLen: 3,
   424  			expected: [][]labelpb.ZLabel{
   425  				{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   426  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   427  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   428  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   429  				{{Name: "a", Value: "2"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   430  				{{Name: "a", Value: "2"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   431  				{{Name: "a", Value: "2"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   432  				{{Name: "a", Value: "2"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   433  			},
   434  		},
   435  		// Regression https://github.com/thanos-io/thanos/issues/833.
   436  		// Problem: Matcher that was selecting NO series, was ignored instead of passed as emptyPosting to Intersect.
   437  		{
   438  			req: &storepb.SeriesRequest{
   439  				Matchers: []storepb.LabelMatcher{
   440  					{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "1"},
   441  					{Type: storepb.LabelMatcher_RE, Name: "non_existing", Value: "something"},
   442  				},
   443  				MinTime: mint,
   444  				MaxTime: maxt,
   445  			},
   446  		},
   447  		// Test skip-chunk option.
   448  		{
   449  			req: &storepb.SeriesRequest{
   450  				Matchers: []storepb.LabelMatcher{
   451  					{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "1"},
   452  				},
   453  				MinTime:    mint,
   454  				MaxTime:    maxt,
   455  				SkipChunks: true,
   456  			},
   457  			expectedChunkLen: 0,
   458  			expected: [][]labelpb.ZLabel{
   459  				{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   460  				{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   461  				{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   462  				{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   463  			},
   464  		},
   465  	} {
   466  		if ok := t.Run(fmt.Sprint(i), func(t *testing.T) {
   467  			srv := newStoreSeriesServer(ctx)
   468  
   469  			testutil.Ok(t, s.store.Series(tcase.req, srv))
   470  			testutil.Equals(t, len(tcase.expected), len(srv.SeriesSet))
   471  
   472  			for i, s := range srv.SeriesSet {
   473  				testutil.Equals(t, tcase.expected[i], s.Labels)
   474  				testutil.Equals(t, tcase.expectedChunkLen, len(s.Chunks))
   475  			}
   476  		}); !ok {
   477  			return
   478  		}
   479  	}
   480  }
   481  
   482  func TestBucketStore_e2e(t *testing.T) {
   483  	objtesting.ForeachStore(t, func(t *testing.T, bkt objstore.Bucket) {
   484  		ctx, cancel := context.WithCancel(context.Background())
   485  		defer cancel()
   486  
   487  		dir := t.TempDir()
   488  
   489  		s := prepareStoreWithTestBlocks(t, dir, bkt, false, NewChunksLimiterFactory(0), NewSeriesLimiterFactory(0), NewBytesLimiterFactory(0), emptyRelabelConfig, allowAllFilterConf)
   490  
   491  		if ok := t.Run("no index cache", func(t *testing.T) {
   492  			s.cache.SwapWith(noopCache{})
   493  			testBucketStore_e2e(t, ctx, s)
   494  		}); !ok {
   495  			return
   496  		}
   497  
   498  		if ok := t.Run("with large, sufficient index cache", func(t *testing.T) {
   499  			indexCache, err := storecache.NewInMemoryIndexCacheWithConfig(s.logger, nil, nil, storecache.InMemoryIndexCacheConfig{
   500  				MaxItemSize: 1e5,
   501  				MaxSize:     2e5,
   502  			})
   503  			testutil.Ok(t, err)
   504  			s.cache.SwapWith(indexCache)
   505  			testBucketStore_e2e(t, ctx, s)
   506  		}); !ok {
   507  			return
   508  		}
   509  
   510  		t.Run("with small index cache", func(t *testing.T) {
   511  			indexCache2, err := storecache.NewInMemoryIndexCacheWithConfig(s.logger, nil, nil, storecache.InMemoryIndexCacheConfig{
   512  				MaxItemSize: 50,
   513  				MaxSize:     100,
   514  			})
   515  			testutil.Ok(t, err)
   516  			s.cache.SwapWith(indexCache2)
   517  			testBucketStore_e2e(t, ctx, s)
   518  		})
   519  	})
   520  }
   521  
   522  type naivePartitioner struct{}
   523  
   524  func (g naivePartitioner) Partition(length int, rng func(int) (uint64, uint64)) (parts []Part) {
   525  	for i := 0; i < length; i++ {
   526  		s, e := rng(i)
   527  		parts = append(parts, Part{Start: s, End: e, ElemRng: [2]int{i, i + 1}})
   528  	}
   529  	return parts
   530  }
   531  
   532  // Naive partitioner splits the array equally (it does not combine anything).
   533  // This tests if our, sometimes concurrent, fetches for different parts works.
   534  // Regression test against: https://github.com/thanos-io/thanos/issues/829.
   535  func TestBucketStore_ManyParts_e2e(t *testing.T) {
   536  	objtesting.ForeachStore(t, func(t *testing.T, bkt objstore.Bucket) {
   537  		ctx, cancel := context.WithCancel(context.Background())
   538  		defer cancel()
   539  
   540  		dir := t.TempDir()
   541  
   542  		s := prepareStoreWithTestBlocks(t, dir, bkt, true, NewChunksLimiterFactory(0), NewSeriesLimiterFactory(0), NewBytesLimiterFactory(0), emptyRelabelConfig, allowAllFilterConf)
   543  
   544  		indexCache, err := storecache.NewInMemoryIndexCacheWithConfig(s.logger, nil, nil, storecache.InMemoryIndexCacheConfig{
   545  			MaxItemSize: 1e5,
   546  			MaxSize:     2e5,
   547  		})
   548  		testutil.Ok(t, err)
   549  		s.cache.SwapWith(indexCache)
   550  
   551  		testBucketStore_e2e(t, ctx, s)
   552  	})
   553  }
   554  
   555  func TestBucketStore_TimePartitioning_e2e(t *testing.T) {
   556  	ctx, cancel := context.WithCancel(context.Background())
   557  	defer cancel()
   558  	bkt := objstore.NewInMemBucket()
   559  
   560  	dir := t.TempDir()
   561  
   562  	hourAfter := time.Now().Add(1 * time.Hour)
   563  	filterMaxTime := model.TimeOrDurationValue{Time: &hourAfter}
   564  
   565  	// The query will fetch 2 series from 2 blocks, so we do expect to hit a total of 4 chunks.
   566  	expectedChunks := uint64(2 * 2)
   567  
   568  	s := prepareStoreWithTestBlocks(t, dir, bkt, false, NewChunksLimiterFactory(expectedChunks), NewSeriesLimiterFactory(0), NewBytesLimiterFactory(0), emptyRelabelConfig, &FilterConfig{
   569  		MinTime: minTimeDuration,
   570  		MaxTime: filterMaxTime,
   571  	})
   572  	testutil.Ok(t, s.store.SyncBlocks(ctx))
   573  
   574  	mint, maxt := s.store.TimeRange()
   575  	testutil.Equals(t, s.minTime, mint)
   576  	testutil.Equals(t, filterMaxTime.PrometheusTimestamp(), maxt)
   577  
   578  	req := &storepb.SeriesRequest{
   579  		Matchers: []storepb.LabelMatcher{
   580  			{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "1"},
   581  		},
   582  		MinTime: mint,
   583  		MaxTime: timestamp.FromTime(time.Now().AddDate(0, 0, 1)),
   584  	}
   585  
   586  	expectedLabels := [][]labelpb.ZLabel{
   587  		{{Name: "a", Value: "1"}, {Name: "b", Value: "1"}, {Name: "ext1", Value: "value1"}},
   588  		{{Name: "a", Value: "1"}, {Name: "b", Value: "2"}, {Name: "ext1", Value: "value1"}},
   589  		{{Name: "a", Value: "1"}, {Name: "c", Value: "1"}, {Name: "ext2", Value: "value2"}},
   590  		{{Name: "a", Value: "1"}, {Name: "c", Value: "2"}, {Name: "ext2", Value: "value2"}},
   591  	}
   592  
   593  	s.cache.SwapWith(noopCache{})
   594  	srv := newStoreSeriesServer(ctx)
   595  
   596  	testutil.Ok(t, s.store.Series(req, srv))
   597  	testutil.Equals(t, len(expectedLabels), len(srv.SeriesSet))
   598  
   599  	for i, s := range srv.SeriesSet {
   600  		testutil.Equals(t, expectedLabels[i], s.Labels)
   601  
   602  		// prepareTestBlocks makes 3 chunks containing 2 hour data,
   603  		// we should only get 1, as we are filtering by time.
   604  		testutil.Equals(t, 1, len(s.Chunks))
   605  	}
   606  }
   607  
   608  func TestBucketStore_Series_ChunksLimiter_e2e(t *testing.T) {
   609  	// The query will fetch 2 series from 6 blocks, so we do expect to hit a total of 12 chunks.
   610  	expectedChunks := uint64(2 * 6)
   611  
   612  	cases := map[string]struct {
   613  		maxChunksLimit uint64
   614  		maxSeriesLimit uint64
   615  		maxBytesLimit  int64
   616  		expectedErr    string
   617  		code           codes.Code
   618  	}{
   619  		"should succeed if the max chunks limit is not exceeded": {
   620  			maxChunksLimit: expectedChunks,
   621  		},
   622  		"should fail if the max chunks limit is exceeded - ResourceExhausted": {
   623  			maxChunksLimit: expectedChunks - 1,
   624  			expectedErr:    "exceeded chunks limit",
   625  			code:           codes.ResourceExhausted,
   626  		},
   627  		"should fail if the max series limit is exceeded - ResourceExhausted": {
   628  			maxChunksLimit: expectedChunks,
   629  			expectedErr:    "exceeded series limit",
   630  			maxSeriesLimit: 1,
   631  			code:           codes.ResourceExhausted,
   632  		},
   633  		"should fail if the max bytes limit is exceeded - ResourceExhausted": {
   634  			maxChunksLimit: expectedChunks,
   635  			expectedErr:    "exceeded bytes limit",
   636  			maxSeriesLimit: 2,
   637  			maxBytesLimit:  1,
   638  			code:           codes.ResourceExhausted,
   639  		},
   640  	}
   641  
   642  	for testName, testData := range cases {
   643  		t.Run(testName, func(t *testing.T) {
   644  			ctx, cancel := context.WithCancel(context.Background())
   645  			defer cancel()
   646  			bkt := objstore.NewInMemBucket()
   647  
   648  			dir := t.TempDir()
   649  
   650  			s := prepareStoreWithTestBlocks(t, dir, bkt, false, NewChunksLimiterFactory(testData.maxChunksLimit), NewSeriesLimiterFactory(testData.maxSeriesLimit), NewBytesLimiterFactory(units.Base2Bytes(testData.maxBytesLimit)), emptyRelabelConfig, allowAllFilterConf)
   651  			testutil.Ok(t, s.store.SyncBlocks(ctx))
   652  
   653  			req := &storepb.SeriesRequest{
   654  				Matchers: []storepb.LabelMatcher{
   655  					{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "1"},
   656  				},
   657  				MinTime: minTimeDuration.PrometheusTimestamp(),
   658  				MaxTime: maxTimeDuration.PrometheusTimestamp(),
   659  			}
   660  
   661  			s.cache.SwapWith(noopCache{})
   662  			srv := newStoreSeriesServer(ctx)
   663  			err := s.store.Series(req, srv)
   664  
   665  			if testData.expectedErr == "" {
   666  				testutil.Ok(t, err)
   667  			} else {
   668  				testutil.NotOk(t, err)
   669  				testutil.Assert(t, strings.Contains(err.Error(), testData.expectedErr))
   670  				status, ok := status.FromError(err)
   671  				testutil.Equals(t, true, ok)
   672  				testutil.Equals(t, testData.code, status.Code())
   673  			}
   674  		})
   675  	}
   676  }
   677  
   678  func TestBucketStore_LabelNames_e2e(t *testing.T) {
   679  	objtesting.ForeachStore(t, func(t *testing.T, bkt objstore.Bucket) {
   680  		ctx, cancel := context.WithCancel(context.Background())
   681  		defer cancel()
   682  
   683  		dir := t.TempDir()
   684  
   685  		s := prepareStoreWithTestBlocks(t, dir, bkt, false, NewChunksLimiterFactory(0), NewSeriesLimiterFactory(0), NewBytesLimiterFactory(0), emptyRelabelConfig, allowAllFilterConf)
   686  		s.cache.SwapWith(noopCache{})
   687  
   688  		mint, maxt := s.store.TimeRange()
   689  		testutil.Equals(t, s.minTime, mint)
   690  		testutil.Equals(t, s.maxTime, maxt)
   691  
   692  		for name, tc := range map[string]struct {
   693  			req      *storepb.LabelNamesRequest
   694  			expected []string
   695  		}{
   696  			"basic labelNames": {
   697  				req: &storepb.LabelNamesRequest{
   698  					Start: timestamp.FromTime(minTime),
   699  					End:   timestamp.FromTime(maxTime),
   700  				},
   701  				expected: []string{"a", "b", "c", "ext1", "ext2"}, // ext2 is added by the prepareStoreWithTestBlocks function.
   702  			},
   703  			"outside the time range": {
   704  				req: &storepb.LabelNamesRequest{
   705  					Start: timestamp.FromTime(time.Now().Add(-24 * time.Hour)),
   706  					End:   timestamp.FromTime(time.Now().Add(-23 * time.Hour)),
   707  				},
   708  				expected: nil,
   709  			},
   710  			"matcher matching everything": {
   711  				req: &storepb.LabelNamesRequest{
   712  					Start: timestamp.FromTime(minTime),
   713  					End:   timestamp.FromTime(maxTime),
   714  					Matchers: []storepb.LabelMatcher{
   715  						{
   716  							Type:  storepb.LabelMatcher_EQ,
   717  							Name:  "a",
   718  							Value: "1",
   719  						},
   720  					},
   721  				},
   722  				expected: []string{"a", "b", "c", "ext1", "ext2"},
   723  			},
   724  			"b=1 matcher": {
   725  				req: &storepb.LabelNamesRequest{
   726  					Start: timestamp.FromTime(minTime),
   727  					End:   timestamp.FromTime(maxTime),
   728  					Matchers: []storepb.LabelMatcher{
   729  						{
   730  							Type:  storepb.LabelMatcher_EQ,
   731  							Name:  "b",
   732  							Value: "1",
   733  						},
   734  					},
   735  				},
   736  				expected: []string{"a", "b", "ext1"},
   737  			},
   738  
   739  			"b='' matcher": {
   740  				req: &storepb.LabelNamesRequest{
   741  					Start: timestamp.FromTime(minTime),
   742  					End:   timestamp.FromTime(maxTime),
   743  					Matchers: []storepb.LabelMatcher{
   744  						{
   745  							Type:  storepb.LabelMatcher_EQ,
   746  							Name:  "b",
   747  							Value: "",
   748  						},
   749  					},
   750  				},
   751  				expected: []string{"a", "c", "ext2"},
   752  			},
   753  			"outside the time range, with matcher": {
   754  				req: &storepb.LabelNamesRequest{
   755  					Start: timestamp.FromTime(time.Now().Add(-24 * time.Hour)),
   756  					End:   timestamp.FromTime(time.Now().Add(-23 * time.Hour)),
   757  					Matchers: []storepb.LabelMatcher{
   758  						{
   759  							Type:  storepb.LabelMatcher_EQ,
   760  							Name:  "a",
   761  							Value: "1",
   762  						},
   763  					},
   764  				},
   765  				expected: nil,
   766  			},
   767  		} {
   768  			t.Run(name, func(t *testing.T) {
   769  				vals, err := s.store.LabelNames(ctx, tc.req)
   770  				for _, b := range s.store.blocks {
   771  					waitTimeout(t, &b.pendingReaders, 5*time.Second)
   772  				}
   773  
   774  				testutil.Ok(t, err)
   775  
   776  				testutil.Equals(t, tc.expected, vals.Names)
   777  			})
   778  		}
   779  	})
   780  }
   781  
   782  func TestBucketStore_LabelNames_SeriesLimiter_e2e(t *testing.T) {
   783  	cases := map[string]struct {
   784  		maxSeriesLimit uint64
   785  		expectedErr    string
   786  		code           codes.Code
   787  	}{
   788  		"should succeed if the max series limit is not exceeded": {
   789  			maxSeriesLimit: math.MaxUint64,
   790  		},
   791  		"should fail if the max series limit is exceeded - ResourceExhausted": {
   792  			expectedErr:    "exceeded series limit",
   793  			maxSeriesLimit: 1,
   794  			code:           codes.ResourceExhausted,
   795  		},
   796  	}
   797  
   798  	for testName, testData := range cases {
   799  		t.Run(testName, func(t *testing.T) {
   800  			ctx, cancel := context.WithCancel(context.Background())
   801  			defer cancel()
   802  
   803  			bkt := objstore.NewInMemBucket()
   804  			dir := t.TempDir()
   805  			s := prepareStoreWithTestBlocks(t, dir, bkt, false, NewChunksLimiterFactory(0), NewSeriesLimiterFactory(testData.maxSeriesLimit), NewBytesLimiterFactory(0), emptyRelabelConfig, allowAllFilterConf)
   806  			testutil.Ok(t, s.store.SyncBlocks(ctx))
   807  			req := &storepb.LabelNamesRequest{
   808  				Matchers: []storepb.LabelMatcher{
   809  					{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "1"},
   810  				},
   811  				Start: minTimeDuration.PrometheusTimestamp(),
   812  				End:   maxTimeDuration.PrometheusTimestamp(),
   813  			}
   814  
   815  			s.cache.SwapWith(noopCache{})
   816  
   817  			_, err := s.store.LabelNames(context.Background(), req)
   818  
   819  			if testData.expectedErr == "" {
   820  				testutil.Ok(t, err)
   821  			} else {
   822  				testutil.NotOk(t, err)
   823  				testutil.Assert(t, strings.Contains(err.Error(), testData.expectedErr))
   824  
   825  				status, ok := status.FromError(err)
   826  				testutil.Equals(t, true, ok)
   827  				testutil.Equals(t, testData.code, status.Code())
   828  			}
   829  		})
   830  	}
   831  }
   832  
   833  func TestBucketStore_LabelValues_e2e(t *testing.T) {
   834  	objtesting.ForeachStore(t, func(t *testing.T, bkt objstore.Bucket) {
   835  		ctx, cancel := context.WithCancel(context.Background())
   836  		defer cancel()
   837  
   838  		dir := t.TempDir()
   839  
   840  		s := prepareStoreWithTestBlocks(t, dir, bkt, false, NewChunksLimiterFactory(0), NewSeriesLimiterFactory(0), NewBytesLimiterFactory(0), emptyRelabelConfig, allowAllFilterConf)
   841  		s.cache.SwapWith(noopCache{})
   842  
   843  		mint, maxt := s.store.TimeRange()
   844  		testutil.Equals(t, s.minTime, mint)
   845  		testutil.Equals(t, s.maxTime, maxt)
   846  
   847  		for name, tc := range map[string]struct {
   848  			req      *storepb.LabelValuesRequest
   849  			expected []string
   850  		}{
   851  			"label a": {
   852  				req: &storepb.LabelValuesRequest{
   853  					Label: "a",
   854  					Start: timestamp.FromTime(minTime),
   855  					End:   timestamp.FromTime(maxTime),
   856  				},
   857  				expected: []string{"1", "2"},
   858  			},
   859  			"label a, outside time range": {
   860  				req: &storepb.LabelValuesRequest{
   861  					Label: "a",
   862  					Start: timestamp.FromTime(time.Now().Add(-24 * time.Hour)),
   863  					End:   timestamp.FromTime(time.Now().Add(-23 * time.Hour)),
   864  				},
   865  				expected: nil,
   866  			},
   867  			"label a, a=1": {
   868  				req: &storepb.LabelValuesRequest{
   869  					Label: "a",
   870  					Start: timestamp.FromTime(minTime),
   871  					End:   timestamp.FromTime(maxTime),
   872  					Matchers: []storepb.LabelMatcher{
   873  						{
   874  							Type:  storepb.LabelMatcher_EQ,
   875  							Name:  "a",
   876  							Value: "1",
   877  						},
   878  					},
   879  				},
   880  				expected: []string{"1"},
   881  			},
   882  			"label a, a=2, c=2": {
   883  				req: &storepb.LabelValuesRequest{
   884  					Label: "a",
   885  					Start: timestamp.FromTime(minTime),
   886  					End:   timestamp.FromTime(maxTime),
   887  					Matchers: []storepb.LabelMatcher{
   888  						{
   889  							Type:  storepb.LabelMatcher_EQ,
   890  							Name:  "a",
   891  							Value: "2",
   892  						},
   893  						{
   894  							Type:  storepb.LabelMatcher_EQ,
   895  							Name:  "c",
   896  							Value: "2",
   897  						},
   898  					},
   899  				},
   900  				expected: []string{"2"},
   901  			},
   902  			"label ext1": {
   903  				req: &storepb.LabelValuesRequest{
   904  					Label: "ext1",
   905  					Start: timestamp.FromTime(minTime),
   906  					End:   timestamp.FromTime(maxTime),
   907  				},
   908  				expected: []string{"value1"},
   909  			},
   910  			"label ext1, c=1": {
   911  				req: &storepb.LabelValuesRequest{
   912  					Label: "ext1",
   913  					Start: timestamp.FromTime(minTime),
   914  					End:   timestamp.FromTime(maxTime),
   915  					Matchers: []storepb.LabelMatcher{
   916  						{
   917  							Type:  storepb.LabelMatcher_EQ,
   918  							Name:  "c",
   919  							Value: "1",
   920  						},
   921  					},
   922  				},
   923  				expected: nil, // ext1 is replaced with ext2 for series with c
   924  			},
   925  		} {
   926  			t.Run(name, func(t *testing.T) {
   927  				vals, err := s.store.LabelValues(ctx, tc.req)
   928  				for _, b := range s.store.blocks {
   929  					waitTimeout(t, &b.pendingReaders, 5*time.Second)
   930  				}
   931  
   932  				testutil.Ok(t, err)
   933  
   934  				testutil.Equals(t, tc.expected, emptyToNil(vals.Values))
   935  			})
   936  		}
   937  	})
   938  }
   939  
   940  func TestBucketStore_LabelValues_SeriesLimiter_e2e(t *testing.T) {
   941  	cases := map[string]struct {
   942  		maxSeriesLimit uint64
   943  		expectedErr    string
   944  		code           codes.Code
   945  	}{
   946  		"should succeed if the max chunks limit is not exceeded": {
   947  			maxSeriesLimit: math.MaxUint64,
   948  		},
   949  		"should fail if the max series limit is exceeded - ResourceExhausted": {
   950  			expectedErr:    "exceeded series limit",
   951  			maxSeriesLimit: 1,
   952  			code:           codes.ResourceExhausted,
   953  		},
   954  	}
   955  
   956  	for testName, testData := range cases {
   957  		t.Run(testName, func(t *testing.T) {
   958  			ctx, cancel := context.WithCancel(context.Background())
   959  			defer cancel()
   960  			bkt := objstore.NewInMemBucket()
   961  
   962  			dir := t.TempDir()
   963  
   964  			s := prepareStoreWithTestBlocks(t, dir, bkt, false, NewChunksLimiterFactory(0), NewSeriesLimiterFactory(testData.maxSeriesLimit), NewBytesLimiterFactory(0), emptyRelabelConfig, allowAllFilterConf)
   965  			testutil.Ok(t, s.store.SyncBlocks(ctx))
   966  
   967  			req := &storepb.LabelValuesRequest{
   968  				Label: "a",
   969  				Start: minTimeDuration.PrometheusTimestamp(),
   970  				End:   maxTimeDuration.PrometheusTimestamp(),
   971  				Matchers: []storepb.LabelMatcher{
   972  					{
   973  						Type:  storepb.LabelMatcher_EQ,
   974  						Name:  "a",
   975  						Value: "1",
   976  					},
   977  				},
   978  			}
   979  
   980  			s.cache.SwapWith(noopCache{})
   981  
   982  			_, err := s.store.LabelValues(context.Background(), req)
   983  
   984  			if testData.expectedErr == "" {
   985  				testutil.Ok(t, err)
   986  			} else {
   987  				testutil.NotOk(t, err)
   988  				testutil.Assert(t, strings.Contains(err.Error(), testData.expectedErr))
   989  
   990  				status, ok := status.FromError(err)
   991  				testutil.Equals(t, true, ok)
   992  				testutil.Equals(t, testData.code, status.Code())
   993  			}
   994  		})
   995  	}
   996  }
   997  
   998  func emptyToNil(values []string) []string {
   999  	if len(values) == 0 {
  1000  		return nil
  1001  	}
  1002  	return values
  1003  }
  1004  
  1005  func waitTimeout(t *testing.T, wg *sync.WaitGroup, timeout time.Duration) {
  1006  	c := make(chan struct{})
  1007  	go func() {
  1008  		defer close(c)
  1009  		wg.Wait()
  1010  	}()
  1011  	select {
  1012  	case <-c:
  1013  		return
  1014  	case <-time.After(timeout):
  1015  		t.Fatalf("timeout waiting wg for %v", timeout)
  1016  	}
  1017  }