github.com/thanos-io/thanos@v0.32.5/pkg/store/prometheus_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  	"net/url"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/cespare/xxhash"
    15  
    16  	"github.com/pkg/errors"
    17  	"github.com/prometheus/prometheus/model/labels"
    18  	"github.com/prometheus/prometheus/model/timestamp"
    19  	"github.com/prometheus/prometheus/storage"
    20  	"github.com/prometheus/prometheus/tsdb/chunkenc"
    21  
    22  	"github.com/efficientgo/core/testutil"
    23  
    24  	"github.com/thanos-io/thanos/pkg/component"
    25  	"github.com/thanos-io/thanos/pkg/promclient"
    26  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    27  	"github.com/thanos-io/thanos/pkg/store/storepb"
    28  	"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
    29  	"github.com/thanos-io/thanos/pkg/testutil/custom"
    30  	"github.com/thanos-io/thanos/pkg/testutil/e2eutil"
    31  )
    32  
    33  func TestPrometheusStore_Series_e2e(t *testing.T) {
    34  	testPrometheusStoreSeriesE2e(t, "")
    35  }
    36  
    37  // Regression test for https://github.com/thanos-io/thanos/issues/478.
    38  func TestPrometheusStore_Series_promOnPath_e2e(t *testing.T) {
    39  	testPrometheusStoreSeriesE2e(t, "/prometheus/sub/path")
    40  }
    41  
    42  func testPrometheusStoreSeriesE2e(t *testing.T, prefix string) {
    43  	defer custom.TolerantVerifyLeak(t)
    44  
    45  	p, err := e2eutil.NewPrometheusOnPath(prefix)
    46  	testutil.Ok(t, err)
    47  	defer func() { testutil.Ok(t, p.Stop()) }()
    48  
    49  	baseT := timestamp.FromTime(time.Now()) / 1000 * 1000
    50  
    51  	// region is an external label; by adding it as an internal label too we also trigger
    52  	// the resorting code paths
    53  	a := p.Appender()
    54  	_, err = a.Append(0, labels.FromStrings("a", "b", "region", "local"), baseT+100, 1)
    55  	testutil.Ok(t, err)
    56  	_, err = a.Append(0, labels.FromStrings("a", "b", "region", "local"), baseT+200, 2)
    57  	testutil.Ok(t, err)
    58  	_, err = a.Append(0, labels.FromStrings("a", "b", "region", "local"), baseT+300, 3)
    59  	testutil.Ok(t, err)
    60  	testutil.Ok(t, a.Commit())
    61  
    62  	ctx, cancel := context.WithCancel(context.Background())
    63  	defer cancel()
    64  
    65  	testutil.Ok(t, p.Start())
    66  
    67  	u, err := url.Parse(fmt.Sprintf("http://%s", p.Addr()))
    68  	testutil.Ok(t, err)
    69  
    70  	limitMinT := int64(0)
    71  	proxy, err := NewPrometheusStore(nil, nil, promclient.NewDefaultClient(), u, component.Sidecar,
    72  		func() labels.Labels { return labels.FromStrings("region", "eu-west") },
    73  		func() (int64, int64) { return limitMinT, -1 },
    74  		nil,
    75  	) // MaxTime does not matter.
    76  	testutil.Ok(t, err)
    77  
    78  	// Query all three samples except for the first one. Since we round up queried data
    79  	// to seconds, we can test whether the extra sample gets stripped properly.
    80  	{
    81  		srv := newStoreSeriesServer(ctx)
    82  		testutil.Ok(t, proxy.Series(&storepb.SeriesRequest{
    83  			MinTime: baseT + 101,
    84  			MaxTime: baseT + 300,
    85  			Matchers: []storepb.LabelMatcher{
    86  				{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "b"},
    87  			},
    88  		}, srv))
    89  
    90  		testutil.Equals(t, 1, len(srv.SeriesSet))
    91  
    92  		testutil.Equals(t, []labelpb.ZLabel{
    93  			{Name: "a", Value: "b"},
    94  			{Name: "region", Value: "eu-west"},
    95  		}, srv.SeriesSet[0].Labels)
    96  		testutil.Equals(t, []string(nil), srv.Warnings)
    97  		testutil.Equals(t, 1, len(srv.SeriesSet[0].Chunks))
    98  
    99  		c := srv.SeriesSet[0].Chunks[0]
   100  		testutil.Equals(t, storepb.Chunk_XOR, c.Raw.Type)
   101  
   102  		chk, err := chunkenc.FromData(chunkenc.EncXOR, c.Raw.Data)
   103  		testutil.Ok(t, err)
   104  
   105  		samples := expandChunk(chk.Iterator(nil))
   106  		testutil.Equals(t, []sample{{baseT + 200, 2}, {baseT + 300, 3}}, samples)
   107  
   108  	}
   109  	// Query all samples, but limit mint time to exclude the first one.
   110  	{
   111  		limitMinT = baseT + 101
   112  		srv := newStoreSeriesServer(ctx)
   113  		testutil.Ok(t, proxy.Series(&storepb.SeriesRequest{
   114  			MinTime: 0,
   115  			MaxTime: baseT + 300,
   116  			Matchers: []storepb.LabelMatcher{
   117  				{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "b"},
   118  			},
   119  		}, srv))
   120  		// Revert for next cases.
   121  		limitMinT = 0
   122  
   123  		testutil.Equals(t, 1, len(srv.SeriesSet))
   124  
   125  		testutil.Equals(t, []labelpb.ZLabel{
   126  			{Name: "a", Value: "b"},
   127  			{Name: "region", Value: "eu-west"},
   128  		}, srv.SeriesSet[0].Labels)
   129  
   130  		testutil.Equals(t, 1, len(srv.SeriesSet[0].Chunks))
   131  
   132  		c := srv.SeriesSet[0].Chunks[0]
   133  		testutil.Equals(t, []string(nil), srv.Warnings)
   134  		testutil.Equals(t, storepb.Chunk_XOR, c.Raw.Type)
   135  
   136  		chk, err := chunkenc.FromData(chunkenc.EncXOR, c.Raw.Data)
   137  		testutil.Ok(t, err)
   138  
   139  		samples := expandChunk(chk.Iterator(nil))
   140  		testutil.Equals(t, []sample{{baseT + 200, 2}, {baseT + 300, 3}}, samples)
   141  	}
   142  	// Querying by external labels only.
   143  	{
   144  		srv := newStoreSeriesServer(ctx)
   145  
   146  		err = proxy.Series(&storepb.SeriesRequest{
   147  			MinTime: baseT + 101,
   148  			MaxTime: baseT + 300,
   149  			Matchers: []storepb.LabelMatcher{
   150  				{Type: storepb.LabelMatcher_EQ, Name: "region", Value: "eu-west"},
   151  			},
   152  		}, srv)
   153  		testutil.NotOk(t, err)
   154  		testutil.Equals(t, []string(nil), srv.Warnings)
   155  		testutil.Equals(t, "rpc error: code = InvalidArgument desc = no matchers specified (excluding external labels)", err.Error())
   156  	}
   157  	// Querying with pushdown.
   158  	{
   159  		srv := newStoreSeriesServer(ctx)
   160  		testutil.Ok(t, proxy.Series(&storepb.SeriesRequest{
   161  			MinTime: baseT + 101,
   162  			MaxTime: baseT + 300,
   163  			Matchers: []storepb.LabelMatcher{
   164  				{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "b"},
   165  			},
   166  			QueryHints: &storepb.QueryHints{Func: &storepb.Func{Name: "min_over_time"}, Range: &storepb.Range{Millis: 300}},
   167  		}, srv))
   168  
   169  		testutil.Equals(t, 1, len(srv.SeriesSet))
   170  
   171  		testutil.Equals(t, []labelpb.ZLabel{
   172  			{Name: "a", Value: "b"},
   173  			{Name: "region", Value: "eu-west"},
   174  			{Name: "__thanos_pushed_down", Value: "true"},
   175  		}, srv.SeriesSet[0].Labels)
   176  		testutil.Equals(t, []string(nil), srv.Warnings)
   177  		testutil.Equals(t, 1, len(srv.SeriesSet[0].Chunks))
   178  
   179  		c := srv.SeriesSet[0].Chunks[0]
   180  		testutil.Equals(t, storepb.Chunk_XOR, c.Raw.Type)
   181  
   182  		chk, err := chunkenc.FromData(chunkenc.EncXOR, c.Raw.Data)
   183  		testutil.Ok(t, err)
   184  
   185  		samples := expandChunk(chk.Iterator(nil))
   186  		testutil.Equals(t, []sample{{baseT + 300, 1}}, samples)
   187  
   188  	}
   189  }
   190  
   191  type sample struct {
   192  	t int64
   193  	v float64
   194  }
   195  
   196  func expandChunk(cit chunkenc.Iterator) (res []sample) {
   197  	for cit.Next() != chunkenc.ValNone {
   198  		t, v := cit.At()
   199  		res = append(res, sample{t, v})
   200  	}
   201  	return res
   202  }
   203  
   204  func TestPrometheusStore_SeriesLabels_e2e(t *testing.T) {
   205  	defer custom.TolerantVerifyLeak(t)
   206  
   207  	p, err := e2eutil.NewPrometheus()
   208  	testutil.Ok(t, err)
   209  	defer func() { testutil.Ok(t, p.Stop()) }()
   210  
   211  	baseT := timestamp.FromTime(time.Now()) / 1000 * 1000
   212  
   213  	a := p.Appender()
   214  	_, err = a.Append(0, labels.FromStrings("a", "b", "b", "d"), baseT+100, 1)
   215  	testutil.Ok(t, err)
   216  	_, err = a.Append(0, labels.FromStrings("a", "c", "b", "d", "job", "test"), baseT+200, 2)
   217  	testutil.Ok(t, err)
   218  	_, err = a.Append(0, labels.FromStrings("a", "d", "b", "d", "job", "test"), baseT+300, 3)
   219  	testutil.Ok(t, err)
   220  	_, err = a.Append(0, labels.FromStrings("b", "d", "job", "test"), baseT+400, 4)
   221  	testutil.Ok(t, err)
   222  	testutil.Ok(t, a.Commit())
   223  
   224  	ctx, cancel := context.WithCancel(context.Background())
   225  	defer cancel()
   226  
   227  	testutil.Ok(t, p.Start())
   228  
   229  	u, err := url.Parse(fmt.Sprintf("http://%s", p.Addr()))
   230  	testutil.Ok(t, err)
   231  
   232  	promStore, err := NewPrometheusStore(nil, nil, promclient.NewDefaultClient(), u, component.Sidecar,
   233  		func() labels.Labels { return labels.FromStrings("region", "eu-west") },
   234  		func() (int64, int64) { return math.MinInt64/1000 + 62135596801, math.MaxInt64/1000 - 62135596801 },
   235  		nil,
   236  	)
   237  	testutil.Ok(t, err)
   238  
   239  	for _, tcase := range []struct {
   240  		req         *storepb.SeriesRequest
   241  		expected    []storepb.Series
   242  		expectedErr error
   243  	}{
   244  		{
   245  			req: &storepb.SeriesRequest{
   246  				SkipChunks: true,
   247  				Matchers:   []storepb.LabelMatcher{},
   248  				MinTime:    baseT - 10000000000,
   249  				MaxTime:    baseT + 10000000000,
   250  			},
   251  			expectedErr: errors.New("rpc error: code = InvalidArgument desc = no matchers specified (excluding external labels)"),
   252  		},
   253  		{
   254  			req: &storepb.SeriesRequest{
   255  				SkipChunks: true,
   256  				Matchers: []storepb.LabelMatcher{
   257  					{Type: storepb.LabelMatcher_RE, Name: "wrong-chars-in-label-name(hyphen)", Value: "adsf"},
   258  				},
   259  				MinTime: baseT - 10000000000,
   260  				MaxTime: baseT + 10000000000,
   261  			},
   262  			expectedErr: errors.New("rpc error: code = InvalidArgument desc = expected 2xx response, got 400. Body: {\"status\":\"error\",\"errorType\":\"bad_data\",\"error\":\"invalid parameter \\\"match[]\\\": 1:7: parse error: unexpected character inside braces: '-'\"}"),
   263  		},
   264  		{
   265  			req: &storepb.SeriesRequest{
   266  				SkipChunks: true,
   267  				Matchers: []storepb.LabelMatcher{
   268  					{Type: storepb.LabelMatcher_EQ, Name: "non_existing", Value: "something"},
   269  				},
   270  				MinTime: baseT - 10000000000,
   271  				MaxTime: baseT + 10000000000,
   272  			},
   273  		},
   274  		{
   275  			req: &storepb.SeriesRequest{
   276  				SkipChunks: true,
   277  				Matchers: []storepb.LabelMatcher{
   278  					{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "b"},
   279  				},
   280  				MinTime: baseT,
   281  				MaxTime: baseT + 300,
   282  			},
   283  			expected: []storepb.Series{
   284  				{
   285  					Labels: []labelpb.ZLabel{{Name: "a", Value: "b"}, {Name: "b", Value: "d"}, {Name: "region", Value: "eu-west"}},
   286  				},
   287  			},
   288  		},
   289  		{
   290  			req: &storepb.SeriesRequest{
   291  				SkipChunks: true,
   292  				Matchers: []storepb.LabelMatcher{
   293  					{Type: storepb.LabelMatcher_EQ, Name: "job", Value: "foo"},
   294  				},
   295  				MinTime: baseT,
   296  				MaxTime: baseT + 300,
   297  			},
   298  		},
   299  		{
   300  			req: &storepb.SeriesRequest{
   301  				SkipChunks: true,
   302  				Matchers: []storepb.LabelMatcher{
   303  					{Type: storepb.LabelMatcher_NEQ, Name: "a", Value: "b"},
   304  					{Type: storepb.LabelMatcher_EQ, Name: "job", Value: "test"},
   305  				},
   306  				MinTime: baseT,
   307  				MaxTime: baseT + 300,
   308  			},
   309  			expected: []storepb.Series{
   310  				{
   311  					Labels: []labelpb.ZLabel{{Name: "a", Value: "c"}, {Name: "b", Value: "d"}, {Name: "job", Value: "test"}, {Name: "region", Value: "eu-west"}},
   312  				},
   313  				{
   314  					Labels: []labelpb.ZLabel{{Name: "a", Value: "d"}, {Name: "b", Value: "d"}, {Name: "job", Value: "test"}, {Name: "region", Value: "eu-west"}},
   315  				},
   316  			},
   317  		},
   318  		{
   319  			req: &storepb.SeriesRequest{
   320  				SkipChunks: true,
   321  				Matchers: []storepb.LabelMatcher{
   322  					{Type: storepb.LabelMatcher_EQ, Name: "job", Value: "test"},
   323  				},
   324  				MinTime: baseT,
   325  				MaxTime: baseT + 300,
   326  			},
   327  			expected: []storepb.Series{
   328  				{
   329  					Labels: []labelpb.ZLabel{{Name: "a", Value: "c"}, {Name: "b", Value: "d"}, {Name: "job", Value: "test"}, {Name: "region", Value: "eu-west"}},
   330  				},
   331  				{
   332  					Labels: []labelpb.ZLabel{{Name: "a", Value: "d"}, {Name: "b", Value: "d"}, {Name: "job", Value: "test"}, {Name: "region", Value: "eu-west"}},
   333  				},
   334  			},
   335  		},
   336  		{
   337  			req: &storepb.SeriesRequest{
   338  				SkipChunks: true,
   339  				Matchers: []storepb.LabelMatcher{
   340  					{Type: storepb.LabelMatcher_EQ, Name: "job", Value: "test"},
   341  				},
   342  				MinTime: baseT + 400,
   343  				MaxTime: baseT + 400,
   344  			},
   345  			expected: []storepb.Series{
   346  				{
   347  					Labels: []labelpb.ZLabel{{Name: "b", Value: "d"}, {Name: "job", Value: "test"}, {Name: "region", Value: "eu-west"}},
   348  				},
   349  			},
   350  		},
   351  		{
   352  			req: &storepb.SeriesRequest{
   353  				SkipChunks: true,
   354  				Matchers: []storepb.LabelMatcher{
   355  					{Type: storepb.LabelMatcher_EQ, Name: "job", Value: "test"},
   356  				},
   357  				MinTime: func() int64 { minTime, _ := promStore.timestamps(); return minTime }(),
   358  				MaxTime: func() int64 { _, maxTime := promStore.timestamps(); return maxTime }(),
   359  			},
   360  			expected: []storepb.Series{
   361  				{
   362  					Labels: []labelpb.ZLabel{{Name: "a", Value: "c"}, {Name: "b", Value: "d"}, {Name: "job", Value: "test"}, {Name: "region", Value: "eu-west"}},
   363  				},
   364  				{
   365  					Labels: []labelpb.ZLabel{{Name: "a", Value: "d"}, {Name: "b", Value: "d"}, {Name: "job", Value: "test"}, {Name: "region", Value: "eu-west"}},
   366  				},
   367  				{
   368  					Labels: []labelpb.ZLabel{{Name: "b", Value: "d"}, {Name: "job", Value: "test"}, {Name: "region", Value: "eu-west"}},
   369  				},
   370  			},
   371  		},
   372  	} {
   373  		t.Run("", func(t *testing.T) {
   374  			srv := newStoreSeriesServer(ctx)
   375  			err = promStore.Series(tcase.req, srv)
   376  			if tcase.expectedErr != nil {
   377  				testutil.NotOk(t, err)
   378  				testutil.Equals(t, tcase.expectedErr.Error(), err.Error())
   379  				return
   380  			}
   381  			testutil.Ok(t, err)
   382  			testutil.Equals(t, []string(nil), srv.Warnings)
   383  			testutil.Equals(t, tcase.expected, srv.SeriesSet)
   384  		})
   385  	}
   386  }
   387  
   388  func TestPrometheusStore_Series_MatchExternalLabel(t *testing.T) {
   389  	defer custom.TolerantVerifyLeak(t)
   390  
   391  	p, err := e2eutil.NewPrometheus()
   392  	testutil.Ok(t, err)
   393  	defer func() { testutil.Ok(t, p.Stop()) }()
   394  
   395  	baseT := timestamp.FromTime(time.Now()) / 1000 * 1000
   396  
   397  	a := p.Appender()
   398  	_, err = a.Append(0, labels.FromStrings("a", "b", "region", "eu-west"), baseT+100, 1)
   399  	testutil.Ok(t, err)
   400  	_, err = a.Append(0, labels.FromStrings("a", "b", "region", "eu-west"), baseT+200, 2)
   401  	testutil.Ok(t, err)
   402  	_, err = a.Append(0, labels.FromStrings("a", "b", "region", "eu-west"), baseT+300, 3)
   403  	testutil.Ok(t, err)
   404  	testutil.Ok(t, a.Commit())
   405  
   406  	ctx, cancel := context.WithCancel(context.Background())
   407  	defer cancel()
   408  
   409  	testutil.Ok(t, p.Start())
   410  
   411  	u, err := url.Parse(fmt.Sprintf("http://%s", p.Addr()))
   412  	testutil.Ok(t, err)
   413  
   414  	proxy, err := NewPrometheusStore(nil, nil, promclient.NewDefaultClient(), u, component.Sidecar,
   415  		func() labels.Labels { return labels.FromStrings("region", "eu-west") },
   416  		func() (int64, int64) { return 0, math.MaxInt64 },
   417  		nil)
   418  	testutil.Ok(t, err)
   419  	srv := newStoreSeriesServer(ctx)
   420  
   421  	testutil.Ok(t, proxy.Series(&storepb.SeriesRequest{
   422  		MinTime: baseT + 101,
   423  		MaxTime: baseT + 300,
   424  		Matchers: []storepb.LabelMatcher{
   425  			{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "b"},
   426  			{Type: storepb.LabelMatcher_EQ, Name: "region", Value: "eu-west"},
   427  		},
   428  	}, srv))
   429  	testutil.Equals(t, 1, len(srv.SeriesSet))
   430  
   431  	testutil.Equals(t, []labelpb.ZLabel{
   432  		{Name: "a", Value: "b"},
   433  		{Name: "region", Value: "eu-west"},
   434  	}, srv.SeriesSet[0].Labels)
   435  
   436  	srv = newStoreSeriesServer(ctx)
   437  	// However, it should not match wrong external label.
   438  	testutil.Ok(t, proxy.Series(&storepb.SeriesRequest{
   439  		MinTime: baseT + 101,
   440  		MaxTime: baseT + 300,
   441  		Matchers: []storepb.LabelMatcher{
   442  			{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "b"},
   443  			{Type: storepb.LabelMatcher_EQ, Name: "region", Value: "eu-west2"}, // Non existing label value.
   444  		},
   445  	}, srv))
   446  
   447  	// No series.
   448  	testutil.Equals(t, 0, len(srv.SeriesSet))
   449  }
   450  
   451  func TestPrometheusStore_Series_ChunkHashCalculation_Integration(t *testing.T) {
   452  	defer custom.TolerantVerifyLeak(t)
   453  
   454  	p, err := e2eutil.NewPrometheus()
   455  	testutil.Ok(t, err)
   456  	defer func() { testutil.Ok(t, p.Stop()) }()
   457  
   458  	baseT := timestamp.FromTime(time.Now()) / 1000 * 1000
   459  
   460  	a := p.Appender()
   461  	_, err = a.Append(0, labels.FromStrings("a", "b"), baseT+100, 1)
   462  	testutil.Ok(t, err)
   463  	_, err = a.Append(0, labels.FromStrings("a", "b"), baseT+200, 2)
   464  	testutil.Ok(t, err)
   465  	_, err = a.Append(0, labels.FromStrings("a", "b"), baseT+300, 3)
   466  	testutil.Ok(t, err)
   467  	testutil.Ok(t, a.Commit())
   468  
   469  	ctx, cancel := context.WithCancel(context.Background())
   470  	defer cancel()
   471  
   472  	testutil.Ok(t, p.Start())
   473  
   474  	u, err := url.Parse(fmt.Sprintf("http://%s", p.Addr()))
   475  	testutil.Ok(t, err)
   476  
   477  	proxy, err := NewPrometheusStore(nil, nil, promclient.NewDefaultClient(), u, component.Sidecar,
   478  		func() labels.Labels { return labels.FromStrings("region", "eu-west") },
   479  		func() (int64, int64) { return 0, math.MaxInt64 },
   480  		nil)
   481  	testutil.Ok(t, err)
   482  	srv := newStoreSeriesServer(ctx)
   483  
   484  	testutil.Ok(t, proxy.Series(&storepb.SeriesRequest{
   485  		MinTime: baseT + 101,
   486  		MaxTime: baseT + 300,
   487  		Matchers: []storepb.LabelMatcher{
   488  			{Name: "a", Value: "b"},
   489  			{Type: storepb.LabelMatcher_EQ, Name: "region", Value: "eu-west"},
   490  		},
   491  	}, srv))
   492  	testutil.Equals(t, 1, len(srv.SeriesSet))
   493  
   494  	for _, chunk := range srv.SeriesSet[0].Chunks {
   495  		got := chunk.Raw.Hash
   496  		want := xxhash.Sum64(chunk.Raw.Data)
   497  		testutil.Equals(t, want, got)
   498  	}
   499  }
   500  
   501  func TestPrometheusStore_Info(t *testing.T) {
   502  	defer custom.TolerantVerifyLeak(t)
   503  
   504  	ctx, cancel := context.WithCancel(context.Background())
   505  	defer cancel()
   506  
   507  	proxy, err := NewPrometheusStore(nil, nil, promclient.NewDefaultClient(), nil, component.Sidecar,
   508  		func() labels.Labels { return labels.FromStrings("region", "eu-west") },
   509  		func() (int64, int64) { return 123, 456 },
   510  		nil)
   511  	testutil.Ok(t, err)
   512  
   513  	resp, err := proxy.Info(ctx, &storepb.InfoRequest{})
   514  	testutil.Ok(t, err)
   515  
   516  	testutil.Equals(t, []labelpb.ZLabel{{Name: "region", Value: "eu-west"}}, resp.Labels)
   517  	testutil.Equals(t, storepb.StoreType_SIDECAR, resp.StoreType)
   518  	testutil.Equals(t, int64(123), resp.MinTime)
   519  	testutil.Equals(t, int64(456), resp.MaxTime)
   520  }
   521  
   522  func testSeries_SplitSamplesIntoChunksWithMaxSizeOf120(t *testing.T, appender storage.Appender, newStore func() storepb.StoreServer) {
   523  	baseT := timestamp.FromTime(time.Now().AddDate(0, 0, -2)) / 1000 * 1000
   524  
   525  	offset := int64(2*math.MaxUint16 + 5)
   526  	for i := int64(0); i < offset; i++ {
   527  		_, err := appender.Append(0, labels.FromStrings("a", "b", "region", "eu-west"), baseT+i, 1)
   528  		testutil.Ok(t, err)
   529  	}
   530  
   531  	testutil.Ok(t, appender.Commit())
   532  
   533  	ctx, cancel := context.WithCancel(context.Background())
   534  	defer cancel()
   535  
   536  	client := newStore()
   537  	srv := newStoreSeriesServer(ctx)
   538  
   539  	testutil.Ok(t, client.Series(&storepb.SeriesRequest{
   540  		MinTime: baseT,
   541  		MaxTime: baseT + offset,
   542  		Matchers: []storepb.LabelMatcher{
   543  			{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "b"},
   544  			{Type: storepb.LabelMatcher_EQ, Name: "region", Value: "eu-west"},
   545  		},
   546  	}, srv))
   547  
   548  	testutil.Equals(t, 1, len(srv.SeriesSet))
   549  
   550  	firstSeries := srv.SeriesSet[0]
   551  
   552  	testutil.Equals(t, []labelpb.ZLabel{
   553  		{Name: "a", Value: "b"},
   554  		{Name: "region", Value: "eu-west"},
   555  	}, firstSeries.Labels)
   556  
   557  	testutil.Equals(t, 1093, len(firstSeries.Chunks))
   558  
   559  	chunk, err := chunkenc.FromData(chunkenc.EncXOR, firstSeries.Chunks[0].Raw.Data)
   560  	testutil.Ok(t, err)
   561  	testutil.Equals(t, 120, chunk.NumSamples())
   562  
   563  	chunk, err = chunkenc.FromData(chunkenc.EncXOR, firstSeries.Chunks[1].Raw.Data)
   564  	testutil.Ok(t, err)
   565  	testutil.Equals(t, 120, chunk.NumSamples())
   566  
   567  	chunk, err = chunkenc.FromData(chunkenc.EncXOR, firstSeries.Chunks[len(firstSeries.Chunks)-1].Raw.Data)
   568  	testutil.Ok(t, err)
   569  	testutil.Equals(t, 35, chunk.NumSamples())
   570  }
   571  
   572  // Regression test for https://github.com/thanos-io/thanos/issues/396.
   573  func TestPrometheusStore_Series_SplitSamplesIntoChunksWithMaxSizeOf120(t *testing.T) {
   574  	defer custom.TolerantVerifyLeak(t)
   575  
   576  	p, err := e2eutil.NewPrometheus()
   577  	testutil.Ok(t, err)
   578  	defer func() { testutil.Ok(t, p.Stop()) }()
   579  
   580  	testSeries_SplitSamplesIntoChunksWithMaxSizeOf120(t, p.Appender(), func() storepb.StoreServer {
   581  		testutil.Ok(t, p.Start())
   582  
   583  		u, err := url.Parse(fmt.Sprintf("http://%s", p.Addr()))
   584  		testutil.Ok(t, err)
   585  
   586  		proxy, err := NewPrometheusStore(nil, nil, promclient.NewDefaultClient(), u, component.Sidecar,
   587  			func() labels.Labels { return labels.FromStrings("region", "eu-west") },
   588  			func() (int64, int64) { return 0, math.MaxInt64 },
   589  			nil)
   590  		testutil.Ok(t, err)
   591  
   592  		// We build chunks only for SAMPLES method. Make sure we ask for SAMPLES only.
   593  		proxy.remoteReadAcceptableResponses = []prompb.ReadRequest_ResponseType{prompb.ReadRequest_SAMPLES}
   594  
   595  		return proxy
   596  	})
   597  }