From 9c668d594c244b16144853365e896a0dd75f6604 Mon Sep 17 00:00:00 2001 From: bwplotka Date: Tue, 20 Dec 2022 14:06:22 +0100 Subject: [PATCH] Fixed tests. Signed-off-by: bwplotka --- docs/img/globalsort-nonoptimized.png | Bin 14336 -> 0 bytes docs/img/globalsort-optimized.png | Bin 18482 -> 0 bytes .../20221129-avoid-global-sort.md | 58 +++--- pkg/api/query/v1_test.go | 25 ++- .../test-storeset-pre-v0.8.0/storeset.go | 2 +- pkg/query/querier_test.go | 4 +- pkg/query/query_test.go | 7 +- pkg/store/proxy.go | 2 +- pkg/store/proxy_test.go | 184 +++++++++--------- .../storepb/testutil/client.go} | 2 +- 10 files changed, 159 insertions(+), 125 deletions(-) delete mode 100644 docs/img/globalsort-nonoptimized.png delete mode 100644 docs/img/globalsort-optimized.png rename pkg/{testutil/teststore/cient.go => store/storepb/testutil/client.go} (97%) diff --git a/docs/img/globalsort-nonoptimized.png b/docs/img/globalsort-nonoptimized.png deleted file mode 100644 index d91a878e661139878ae060ca623bf5c7d49fc92c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14336 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 L{an^LB{Ts57WlQ# diff --git a/docs/proposals-accepted/20221129-avoid-global-sort.md b/docs/proposals-accepted/20221129-avoid-global-sort.md index 84796ff1ffa..a620f42da83 100644 --- a/docs/proposals-accepted/20221129-avoid-global-sort.md +++ b/docs/proposals-accepted/20221129-avoid-global-sort.md @@ -1,18 +1,18 @@ ## Avoid Global Sort on Querier Select * **Owners:** - * @bwplotka, @fpetkovski + * @bwplotka, @fpetkovski * **Related Tickets:** - * https://github.com/thanos-io/thanos/issues/5719 - * https://github.com/thanos-io/thanos/commit/043c5bfcc2464d3ae7af82a1428f6e0d6510f020 - * https://github.com/thanos-io/thanos/pull/5796 also alternatives (https://github.com/thanos-io/thanos/pull/5692) + * https://github.com/thanos-io/thanos/issues/5719 + * https://github.com/thanos-io/thanos/commit/043c5bfcc2464d3ae7af82a1428f6e0d6510f020 + * https://github.com/thanos-io/thanos/pull/5796 also alternatives (https://github.com/thanos-io/thanos/pull/5692) > TL;DR: We propose solution that allows saving query and query_range latency on common setups when deduplication on and data replication. Initial benchmarks indicate ~20% latency improvement for data replicated 2 times. -> -> To make it work we propose adding field to Store API Series call "WithoutReplicaLabels []string", guarded by "SupportsWithoutReplicaLabels" field propagated via Info API. It allows telling store implementations to remove given labels (if they are replica labels) from result, preserving sorting by labels after the removal. -> -> NOTE: This change will break unlikely setups that deduplicate on non-replica label (misconfiguration or wrong setup). +> +> To make it work we propose adding field to Store API Series call "WithoutReplicaLabels []string", guarded by "SupportsWithoutReplicaLabels" field propagated via Info API. It allows telling store implementations to remove given labels (if they are replica labels) from result, preserving sorting by labels after the removal. +> +> NOTE: This change will break unlikely setups that deduplicate on non-replica label (misconfiguration or wrong setup). ## Glossary @@ -20,7 +20,7 @@ ## Why -Currently, we spent a lof of storage selection CPU time on resorting resulting time series needed for deduplication (exactly in [`sortDedupLabels`](https://github.com/thanos-io/thanos/blob/main/pkg/query/querier.go#L400)). However, given distributed effort and current sorting guarantees of StoreAPI there is potential to reduce sorting effort or/and distribute it to leafs or multiple threads. +Currently, we spent a lof of storage selection CPU time on resorting resulting time series needed for deduplication (exactly in [`sortDedupLabels`](https://github.com/thanos-io/thanos/blob/main/pkg/query/querier.go#L400)). However, given distributed effort and current sorting guarantees of StoreAPI there is potential to reduce sorting effort or/and distribute it to leafs or multiple threads. ### Pitfalls of the current solution @@ -31,11 +31,11 @@ Current flow can be represented as follows: 1. Querier PromQL Engine selects data. At this point we know if users asked for deduplicated data or not and [what replica labels to use](https://thanos.io/tip/components/query.md/#deduplication-replica-labels). 2. Querier selection asks internal, in-process Store API which is represented by Proxy code component. It asks relevant store API for data, using StoreAPI.Series. 3. Responses are pulled and k-way merged by the time series. StoreAPI guarantees the responses are sorted by series and the external labels (including replica) are included in the time series. - * There was a [bug in receiver](https://github.com/thanos-io/thanos/commit/043c5bfcc2464d3ae7af82a1428f6e0d6510f020#diff-b3f73a54121d88de203946e84955da7027e3cfce7f0cd82580bf215ac57c02f4) that caused series to be not sorted when returned. Fixed in v0.29.0. +* There was a [bug in receiver](https://github.com/thanos-io/thanos/commit/043c5bfcc2464d3ae7af82a1428f6e0d6510f020#diff-b3f73a54121d88de203946e84955da7027e3cfce7f0cd82580bf215ac57c02f4) that caused series to be not sorted when returned. Fixed in v0.29.0. 4. Querier selection waits until all responses are buffered and then it deduplicates the data, given the requested replica labels. Before it's done it globally sort data with moving replica label at the end of the time series in `sortDedupLabels`. 5. Data is deduplicated using `dedup` package. -The pittfall is in the fact that global sort can be in many cases completely avoided, even when deduplication is enabled. Many storeAPIs can drop certain replica labels without need to resort and others can k-way merge different data sets without certain replica labels without extra effort. +The pittfall is in the fact that global sort can be in many cases completely avoided, even when deduplication is enabled. Many storeAPIs can drop certain replica labels without need to resort and others can k-way merge different data sets without certain replica labels without extra effort. ## Goals @@ -44,12 +44,12 @@ Goals and use cases for the solution as proposed in [How](#how): * Avoid expensive global sort of all series before passing them to PromQL engine in Querier. * Allow StoreAPI implementation to announce if it supports sorting feature or not. The rationale is that we want to make it possible to create simpler StoreAPI servers, if operator wants to trade-off it with latency. * Clear the behaviour in tricky cases when there is an overlap of replica labels between what's in TSDB vs what's attached as external labels. -* Ensure this change can be rolled out in compatible way. +* Ensure this change can be rolled out in compatible way. ## Non-Goals * Allow consuming series in streamed way in PromQL engine. - * While this pitfall (global sort) blocks the above idea, it's currently still more beneficial to pull all series upfront (eager approach) as soon as possible. This is due to current PromQL architecture which requires info upfront for query planners and execution. We don't plan to change it yet, thus no need to push explicitly for that. + * While this pitfall (global sort) blocks the above idea, it's currently still more beneficial to pull all series upfront (eager approach) as soon as possible. This is due to current PromQL architecture which requires info upfront for query planners and execution. We don't plan to change it yet, thus no need to push explicitly for that. ## How @@ -59,12 +59,12 @@ To understand proposal, let's go through important, yet perhaps not trivial, fac * For StoreAPI or generally data that belongs to one replica, if you exclude certain replica label during sort, it does not impact sorting order for returned series. This means, any feature that desired different sort for replicated series is generally noop for sidecars, rules, single tenant receiver or within single block (or one stream of blocks). * You can't stream sorting of unsorted data. Furthermore, it's not possible to detect that data is unsorted, unless we fetch and buffer all series. -* In v0.29 and below, you can deduplicate on any labels, including non replicas. This is assumed semantically wrong, yet someone might depend on it. +* In v0.29 and below, you can deduplicate on any labels, including non replicas. This is assumed semantically wrong, yet someone might depend on it. * Thanos never handled overlap of chunks within one set of store API response. ### Solution -To avoid global sort, we propose removing required replica labels and sort on store API level. +To avoid global sort, we propose removing required replica labels and sort on store API level. For the first step (which is required for compatibility purposes anyway), we propose a logic in proxy Store API implementation that when deduplication is requested with given replica labels will: @@ -79,7 +79,7 @@ As the second step we propose adding `without_replica_labels` field to `SeriesRe ```protobuf message SeriesRequest { // ... - + // without_replica_labels are replica labels which have to be excluded from series set results. // The sorting requirement has to be preserved, so series should be sorted without those labels. // If the requested label is NOT a replica label (labels that identify replication group) it should be not affected by @@ -107,10 +107,9 @@ message StoreInfo { } ``` -Thanks of that implementations can optionally support this feature. We can make all Thanos StoreAPI support it, which will allow faster -deduplication queries on all types of setups. +Thanks of that implementations can optionally support this feature. We can make all Thanos StoreAPI support it, which will allow faster deduplication queries on all types of setups. -In the initial tests we see ~2x improvements on my test data (8M series block, requests for ~200k series) with querier and store gateway. +In the initial tests we see 60% improvements on my test data (8M series block, requests for ~200k series) with querier and store gateway. Without this change: @@ -120,10 +119,9 @@ After implementing this proposal: ![2](../img/globalsort-optimized.png) - ## Alternatives -1. Version StoreAPI. +1. Version StoreAPI. As a best practice gRPC services should be versioned. This should allow easier iterations for everybody implementing or using it. However, having multiple versions (vs extra feature enablement field) might make client side more complex, so we propose to postpone it. @@ -133,7 +131,7 @@ Extra slice in all Series might feel redundant, given all series are always grou 3. Instead of removing some replica labels, just sort without them and leave at the end. -For debugging purposes we could keep the replica labels we want to dedup on at the end of label set. +For debugging purposes we could keep the replica labels we want to dedup on at the end of label set. This might however be less clean way of providing better debuggability, which is not yet required. @@ -161,7 +159,7 @@ Cons: * Extra code and protobuf complexity * Semantics of replica labels are hard to maintain when partial deduplication is configured (we only dedup by part of replica labels, not by all of them). This dynamic policy makes it hard to have clean response with separation of replica labels (i.e. should included replica labels be in "labels" or "replica labels")? -This might be not needed for now. We can add more awareness of replication later on. +This might be not needed for now. We can add more awareness of replication later on. ## Action Plan @@ -169,3 +167,17 @@ The tasks to do in order to migrate to the new idea. * [ ] Merging the PR with the proposal (also includes implementation) * [ ] Add support for `without_replica_label` to other store API servers. +* [ ] Move to deduplicate over chunks from series See [TODO](/pkg/query/querier.go#L405) + +```go +// TODO(bwplotka): Move to deduplication on chunk level inside promSeriesSet, similar to what we have in dedup.NewDedupChunkMerger(). +// This however require big refactor, caring about correct AggrChunk to iterator conversion, pushdown logic and counter reset apply. +// For now we apply simple logic that splits potential overlapping chunks into separate replica series, so we can split the work. +set := &promSeriesSet{ + mint: q.mint, + maxt: q.maxt, + set: dedup.NewOverlapSplit(newStoreSeriesSet(resp.seriesSet)), + aggrs: aggrs, + warns: warns, +} +``` diff --git a/pkg/api/query/v1_test.go b/pkg/api/query/v1_test.go index 55706a1bc92..4c07b3ce116 100644 --- a/pkg/api/query/v1_test.go +++ b/pkg/api/query/v1_test.go @@ -56,6 +56,7 @@ import ( "github.com/thanos-io/thanos/pkg/store" "github.com/thanos-io/thanos/pkg/store/labelpb" "github.com/thanos-io/thanos/pkg/store/storepb" + storetestutil "github.com/thanos-io/thanos/pkg/store/storepb/testutil" "github.com/thanos-io/thanos/pkg/testutil/custom" "github.com/thanos-io/thanos/pkg/testutil/e2eutil" "github.com/thanos-io/thanos/pkg/testutil/testpromcompatibility" @@ -192,7 +193,7 @@ func TestQueryEndpoints(t *testing.T) { baseAPI: &baseAPI.BaseAPI{ Now: func() time.Time { return now }, }, - queryableCreate: query.NewQueryableCreator(nil, nil, store.NewTSDBStore(nil, db, component.Query, nil), 2, timeout), + queryableCreate: query.NewQueryableCreator(nil, nil, newProxyStoreWithTSDBStore(db), 2, timeout), queryEngine: qe, lookbackDeltaCreate: func(m int64) time.Duration { return time.Duration(0) }, gate: gate.New(nil, 4), @@ -642,6 +643,24 @@ func TestQueryEndpoints(t *testing.T) { } } +func newProxyStoreWithTSDBStore(db store.TSDBReader) *store.ProxyStore { + c := &storetestutil.TestClient{ + Name: "1", + StoreClient: storepb.ServerAsClient(store.NewTSDBStore(nil, db, component.Query, nil), 0), + MinTime: math.MinInt64, MaxTime: math.MaxInt64, + } + + return store.NewProxyStore( + nil, + nil, + func() []store.Client { return []store.Client{c} }, + component.Query, + nil, + 0, + store.EagerRetrieval, + ) +} + func TestMetadataEndpoints(t *testing.T) { var old = []labels.Labels{ { @@ -733,7 +752,7 @@ func TestMetadataEndpoints(t *testing.T) { baseAPI: &baseAPI.BaseAPI{ Now: func() time.Time { return now }, }, - queryableCreate: query.NewQueryableCreator(nil, nil, store.NewTSDBStore(nil, db, component.Query, nil), 2, timeout), + queryableCreate: query.NewQueryableCreator(nil, nil, newProxyStoreWithTSDBStore(db), 2, timeout), queryEngine: qe, lookbackDeltaCreate: func(m int64) time.Duration { return time.Duration(0) }, gate: gate.New(nil, 4), @@ -746,7 +765,7 @@ func TestMetadataEndpoints(t *testing.T) { baseAPI: &baseAPI.BaseAPI{ Now: func() time.Time { return now }, }, - queryableCreate: query.NewQueryableCreator(nil, nil, store.NewTSDBStore(nil, db, component.Query, nil), 2, timeout), + queryableCreate: query.NewQueryableCreator(nil, nil, newProxyStoreWithTSDBStore(db), 2, timeout), queryEngine: qe, lookbackDeltaCreate: func(m int64) time.Duration { return time.Duration(0) }, gate: gate.New(nil, 4), diff --git a/pkg/query/internal/test-storeset-pre-v0.8.0/storeset.go b/pkg/query/internal/test-storeset-pre-v0.8.0/storeset.go index 8b02a7ca9e4..0e78a4ccd9e 100644 --- a/pkg/query/internal/test-storeset-pre-v0.8.0/storeset.go +++ b/pkg/query/internal/test-storeset-pre-v0.8.0/storeset.go @@ -208,7 +208,7 @@ func (s *storeRef) SupportsSharding() bool { return false } -func (s *storeRef) SendsSortedSeries() bool { +func (s *storeRef) SupportsWithoutReplicaLabels() bool { return false } diff --git a/pkg/query/querier_test.go b/pkg/query/querier_test.go index 5bfdc559b5e..256b944121c 100644 --- a/pkg/query/querier_test.go +++ b/pkg/query/querier_test.go @@ -33,7 +33,7 @@ import ( "github.com/thanos-io/thanos/pkg/store" "github.com/thanos-io/thanos/pkg/store/labelpb" "github.com/thanos-io/thanos/pkg/store/storepb" - "github.com/thanos-io/thanos/pkg/testutil/teststore" + storetestutil "github.com/thanos-io/thanos/pkg/store/storepb/testutil" ) type sample struct { @@ -837,7 +837,7 @@ func newProxyStore(storeAPIs ...storepb.StoreServer) *store.ProxyStore { if srv, ok := s.(*testStoreServer); ok { withoutReplicaLabelsEnabled = len(srv.respsWithoutReplicaLabels) > 0 } - cls[i] = &teststore.TestClient{ + cls[i] = &storetestutil.TestClient{ Name: fmt.Sprintf("%v", i), StoreClient: storepb.ServerAsClient(s, 0), MinTime: math.MinInt64, MaxTime: math.MaxInt64, diff --git a/pkg/query/query_test.go b/pkg/query/query_test.go index 5ff53935243..35949377e81 100644 --- a/pkg/query/query_test.go +++ b/pkg/query/query_test.go @@ -11,14 +11,13 @@ import ( "testing" "time" + "github.com/efficientgo/core/testutil" "github.com/go-kit/log" "github.com/prometheus/prometheus/storage" - "github.com/thanos-io/thanos/pkg/testutil/teststore" - - "github.com/efficientgo/core/testutil" "github.com/thanos-io/thanos/pkg/component" "github.com/thanos-io/thanos/pkg/store" "github.com/thanos-io/thanos/pkg/store/storepb" + storetestutil "github.com/thanos-io/thanos/pkg/store/storepb/testutil" "github.com/thanos-io/thanos/pkg/testutil/custom" ) @@ -50,7 +49,7 @@ func TestQuerier_Proxy(t *testing.T) { testutil.Ok(t, err) // TODO(bwplotka): Parse external labels. - clients = append(clients, &teststore.TestClient{ + clients = append(clients, &storetestutil.TestClient{ Name: fmt.Sprintf("store number %v", i), StoreClient: storepb.ServerAsClient(selectedStore(store.NewTSDBStore(logger, st.storage.DB, component.Debug, nil), m, st.mint, st.maxt), 0), MinTime: st.mint, diff --git a/pkg/store/proxy.go b/pkg/store/proxy.go index 77b7fc19dd4..8b83d39a7d6 100644 --- a/pkg/store/proxy.go +++ b/pkg/store/proxy.go @@ -388,7 +388,7 @@ func labelSetsMatch(matchers []*labels.Matcher, lset ...labels.Labels) bool { for _, ls := range lset { notMatched := false for _, m := range matchers { - if lv := ls.Get(m.Name); !m.Matches(lv) { + if lv := ls.Get(m.Name); ls.Has(m.Name) && !m.Matches(lv) { notMatched = true break } diff --git a/pkg/store/proxy_test.go b/pkg/store/proxy_test.go index 638db5451a6..120e93cc179 100644 --- a/pkg/store/proxy_test.go +++ b/pkg/store/proxy_test.go @@ -24,7 +24,6 @@ import ( "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/tsdb" "github.com/prometheus/prometheus/tsdb/chunkenc" - "github.com/thanos-io/thanos/pkg/testutil/teststore" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -104,7 +103,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "no storeAPI available for 301-302 time range", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -124,7 +123,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "storeAPI available for time range; no series for ext=2 external label matcher", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -145,7 +144,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "storeAPI available for time range; available series for ext=1 external label matcher", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -171,7 +170,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "storeAPI available for time range; available series for any external label matcher", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{4, 3}}, []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -196,7 +195,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "storeAPI available for time range; available series for any external label matcher, but selector blocks", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -216,7 +215,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "no validation if storeAPI follow matching contract", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -242,7 +241,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "complex scenario with storeAPIs warnings", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}, []sample{{4, 3}}), @@ -254,7 +253,7 @@ func TestProxyStore_Series(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), @@ -263,7 +262,7 @@ func TestProxyStore_Series(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -272,7 +271,7 @@ func TestProxyStore_Series(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "c"), []sample{{100, 1}, {300, 3}, {400, 4}}), @@ -281,7 +280,7 @@ func TestProxyStore_Series(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "outside"), []sample{{1, 1}}), @@ -316,7 +315,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "storeAPI available for time range; available two duplicated series for ext=1 external label matcher from 2 storeAPIs", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -326,7 +325,7 @@ func TestProxyStore_Series(t *testing.T) { MaxTime: 300, ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{1, 4}, {2, 5}, {3, 6}}), @@ -352,7 +351,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "storeAPI available for time range; available a few duplicated series for ext=1 external label matcher, mixed storeAPIs", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "1", "w", "1"), []sample{{5, 5}, {7, 7}}), @@ -366,7 +365,7 @@ func TestProxyStore_Series(t *testing.T) { MaxTime: 300, ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "1", "w", "1"), []sample{{2, 1}}), @@ -402,7 +401,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "same external labels are validated during upload and on querier storeset, proxy does not care", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), @@ -412,7 +411,7 @@ func TestProxyStore_Series(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 11}, {2, 22}, {3, 33}}), @@ -438,7 +437,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "partial response enabled", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -449,7 +448,7 @@ func TestProxyStore_Series(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespError: errors.New("error!"), }, @@ -474,7 +473,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "partial response disabled", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -485,7 +484,7 @@ func TestProxyStore_Series(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespError: errors.New("error!"), }, @@ -506,7 +505,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "storeAPI available for time range; available series for ext=1 external label matcher; allowed by store debug matcher", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -535,7 +534,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "storeAPI available for time range; available series for ext=1 external label matcher; blocked by store debug matcher.", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -558,7 +557,7 @@ func TestProxyStore_Series(t *testing.T) { { title: "sharded series response", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -598,7 +597,7 @@ func TestProxyStore_Series(t *testing.T) { for _, replicaLabelSupport := range []bool{false, true} { t.Run(fmt.Sprintf("replica_support=%v", replicaLabelSupport), func(t *testing.T) { for _, s := range tc.storeAPIs { - cl := s.(*teststore.TestClient) + cl := s.(*storetestutil.TestClient) cl.WithoutReplicaLabelsEnabled = replicaLabelSupport } for _, strategy := range []RetrievalStrategy{EagerRetrieval, LazyRetrieval} { @@ -657,7 +656,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response disabled; 1st errors out after some delay; 2nd store is fast", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -672,7 +671,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -712,7 +711,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response disabled; 1st store is slow, 2nd store is fast;", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -724,7 +723,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -748,7 +747,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response disabled; 1st store is fast, 2nd store is slow;", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -759,7 +758,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -784,7 +783,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response disabled; 1st store is slow on 2nd series, 2nd store is fast;", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -799,7 +798,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -823,7 +822,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response disabled; 1st store is fast to respond, 2nd store is slow on 2nd series;", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -836,7 +835,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -862,7 +861,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response enabled; 1st store is slow to respond, 2nd store is fast;", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -874,7 +873,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -902,7 +901,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response enabled; 1st store is fast, 2nd store is slow;", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -913,7 +912,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -942,7 +941,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response enabled; 1st store is fast, 2-3 is slow, 4th is fast;", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -953,7 +952,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -965,7 +964,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -977,7 +976,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -1009,7 +1008,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response enabled; 1st store is slow on 2nd series, 2nd store is fast", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -1024,7 +1023,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -1056,7 +1055,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response disabled; all stores respond 3s", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), @@ -1088,7 +1087,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { { title: "partial response enabled; all stores respond 3s", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("a", "b"), []sample{{1, 1}, {2, 2}, {3, 3}}), @@ -1101,7 +1100,7 @@ func TestProxyStore_SeriesSlowStores(t *testing.T) { MinTime: 1, MaxTime: 300, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("b", "c"), []sample{{1, 1}, {2, 2}, {3, 3}}), @@ -1187,7 +1186,7 @@ func TestProxyStore_Series_RequestParamsProxied(t *testing.T) { }, } cls := []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: m, ExtLset: []labels.Labels{labels.FromStrings("ext", "1")}, MinTime: 1, @@ -1227,14 +1226,14 @@ func TestProxyStore_Series_RegressionFillResponseChannel(t *testing.T) { var cls []Client for i := 0; i < 10; i++ { - cls = append(cls, &teststore.TestClient{ + cls = append(cls, &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespError: errors.New("test error"), }, MinTime: 1, MaxTime: 300, }) - cls = append(cls, &teststore.TestClient{ + cls = append(cls, &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storepb.NewWarnSeriesResponse(errors.New("warning")), @@ -1288,13 +1287,13 @@ func TestProxyStore_LabelValues(t *testing.T) { }, } cls := []Client{ - &teststore.TestClient{StoreClient: m1}, - &teststore.TestClient{StoreClient: &mockedStoreAPI{ + &storetestutil.TestClient{StoreClient: m1}, + &storetestutil.TestClient{StoreClient: &mockedStoreAPI{ RespLabelValues: &storepb.LabelValuesResponse{ Values: []string{"3", "4"}, }, }}, - &teststore.TestClient{StoreClient: &mockedStoreAPI{ + &storetestutil.TestClient{StoreClient: &mockedStoreAPI{ RespLabelValues: &storepb.LabelValuesResponse{ Values: []string{"5", "6"}, }}, @@ -1356,14 +1355,14 @@ func TestProxyStore_LabelNames(t *testing.T) { { title: "label_names partial response disabled", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespLabelNames: &storepb.LabelNamesResponse{ Names: []string{"a", "b"}, }, }, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespLabelNames: &storepb.LabelNamesResponse{ Names: []string{"a", "c", "d"}, @@ -1382,17 +1381,18 @@ func TestProxyStore_LabelNames(t *testing.T) { { title: "label_names partial response disabled, but returns error", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespLabelNames: &storepb.LabelNamesResponse{ Names: []string{"a", "b"}, }, }, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespError: errors.New("error!"), }, + Name: "test", }, }, req: &storepb.LabelNamesRequest{ @@ -1405,14 +1405,14 @@ func TestProxyStore_LabelNames(t *testing.T) { { title: "label_names partial response enabled", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespLabelNames: &storepb.LabelNamesResponse{ Names: []string{"a", "b"}, }, }, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespError: errors.New("error!"), }, @@ -1429,7 +1429,7 @@ func TestProxyStore_LabelNames(t *testing.T) { { title: "stores filtered by time range", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespLabelNames: &storepb.LabelNamesResponse{ Names: []string{"a", "b"}, @@ -1438,7 +1438,7 @@ func TestProxyStore_LabelNames(t *testing.T) { MinTime: timestamp.FromTime(time.Now().Add(-4 * time.Hour)), MaxTime: timestamp.FromTime(time.Now().Add(-3 * time.Hour)), }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespLabelNames: &storepb.LabelNamesResponse{ Names: []string{"c", "d"}, @@ -1459,12 +1459,13 @@ func TestProxyStore_LabelNames(t *testing.T) { { title: "store matchers blocks", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespLabelNames: &storepb.LabelNamesResponse{ Names: []string{"a", "b"}, }, }, + Name: "testaddr", }, }, req: &storepb.LabelNamesRequest{ @@ -1479,12 +1480,13 @@ func TestProxyStore_LabelNames(t *testing.T) { { title: "store matchers allows", storeAPIs: []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespLabelNames: &storepb.LabelNamesResponse{ Names: []string{"a", "b"}, }, }, + Name: "testaddr", }, }, req: &storepb.LabelNamesRequest{ @@ -1573,7 +1575,7 @@ func TestStoreMatches(t *testing.T) { expectedReason string }{ { - s: &teststore.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, + s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, ms: []*labels.Matcher{ labels.MustNewMatcher(labels.MatchEqual, "b", "1"), }, @@ -1582,7 +1584,7 @@ func TestStoreMatches(t *testing.T) { expectedReason: "does not have data within this time period: [0,-1]. Store time ranges: [0,0]", }, { - s: &teststore.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, + s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, ms: []*labels.Matcher{ labels.MustNewMatcher(labels.MatchEqual, "b", "1"), }, @@ -1590,33 +1592,33 @@ func TestStoreMatches(t *testing.T) { expectedMatch: true, }, { - s: &teststore.TestClient{MinTime: 100, MaxTime: 200}, + s: &storetestutil.TestClient{MinTime: 100, MaxTime: 200}, mint: 201, maxt: 300, expectedMatch: false, expectedReason: "does not have data within this time period: [201,300]. Store time ranges: [100,200]", }, { - s: &teststore.TestClient{MinTime: 100, MaxTime: 200}, + s: &storetestutil.TestClient{MinTime: 100, MaxTime: 200}, mint: 200, maxt: 300, expectedMatch: true, }, { - s: &teststore.TestClient{MinTime: 100, MaxTime: 200}, + s: &storetestutil.TestClient{MinTime: 100, MaxTime: 200}, mint: 50, maxt: 99, expectedMatch: false, expectedReason: "does not have data within this time period: [50,99]. Store time ranges: [100,200]", }, { - s: &teststore.TestClient{MinTime: 100, MaxTime: 200}, + s: &storetestutil.TestClient{MinTime: 100, MaxTime: 200}, mint: 50, maxt: 101, expectedMatch: true, }, { - s: &teststore.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, + s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, ms: []*labels.Matcher{ labels.MustNewMatcher(labels.MatchEqual, "a", "b"), }, @@ -1624,7 +1626,7 @@ func TestStoreMatches(t *testing.T) { expectedMatch: true, }, { - s: &teststore.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, + s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, ms: []*labels.Matcher{ labels.MustNewMatcher(labels.MatchEqual, "a", "c"), }, @@ -1633,7 +1635,7 @@ func TestStoreMatches(t *testing.T) { expectedReason: "external labels [{a=\"b\"}] does not match request label matchers: [a=\"c\"]", }, { - s: &teststore.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, + s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, ms: []*labels.Matcher{ labels.MustNewMatcher(labels.MatchRegexp, "a", "b|c"), }, @@ -1641,7 +1643,7 @@ func TestStoreMatches(t *testing.T) { expectedMatch: true, }, { - s: &teststore.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, + s: &storetestutil.TestClient{ExtLset: []labels.Labels{labels.FromStrings("a", "b")}}, ms: []*labels.Matcher{ labels.MustNewMatcher(labels.MatchNotRegexp, "a", ""), }, @@ -1649,7 +1651,7 @@ func TestStoreMatches(t *testing.T) { expectedMatch: true, }, { - s: &teststore.TestClient{ExtLset: []labels.Labels{ + s: &storetestutil.TestClient{ExtLset: []labels.Labels{ labels.FromStrings("a", "b"), labels.FromStrings("a", "c"), labels.FromStrings("a", "d"), @@ -1662,7 +1664,7 @@ func TestStoreMatches(t *testing.T) { expectedReason: "external labels [{a=\"b\"} {a=\"c\"} {a=\"d\"}] does not match request label matchers: [a=\"e\"]", }, { - s: &teststore.TestClient{ExtLset: []labels.Labels{ + s: &storetestutil.TestClient{ExtLset: []labels.Labels{ labels.FromStrings("a", "b"), labels.FromStrings("a", "c"), labels.FromStrings("a", "d"), @@ -1674,7 +1676,7 @@ func TestStoreMatches(t *testing.T) { expectedMatch: true, }, { - s: &teststore.TestClient{ExtLset: []labels.Labels{ + s: &storetestutil.TestClient{ExtLset: []labels.Labels{ labels.FromStrings("a", "b"), labels.FromStrings("a", "c"), labels.FromStrings("a", "d"), @@ -1891,12 +1893,13 @@ func benchProxySeries(t testutil.TB, totalSamples, totalSeries int) { resps = append(resps, storepb.NewSeriesResponse(created[i])) } - clients[j] = &teststore.TestClient{ + clients[j] = &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: resps, }, - MinTime: math.MinInt64, - MaxTime: math.MaxInt64, + MinTime: math.MinInt64, + MaxTime: math.MaxInt64, + WithoutReplicaLabelsEnabled: true, } } @@ -1913,7 +1916,7 @@ func benchProxySeries(t testutil.TB, totalSamples, totalSeries int) { var expected []*storepb.Series lastLabels := storepb.Series{} for _, c := range clients { - m := c.(*teststore.TestClient).StoreClient.(*mockedStoreAPI) + m := c.(*storetestutil.TestClient).StoreClient.(*mockedStoreAPI) // NOTE: Proxy will merge all series with same labels without any frame limit (https://github.com/thanos-io/thanos/issues/2332). for _, r := range m.RespSeries { @@ -1951,16 +1954,17 @@ func benchProxySeries(t testutil.TB, totalSamples, totalSeries int) { }, ) - // Change client to just one. + // Change client to one, containing all series. store.stores = func() []Client { - return []Client{&teststore.TestClient{ + return []Client{&storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ // All responses. RespSeries: allResps, }, - ExtLset: []labels.Labels{labels.FromStrings("ext1", "1")}, - MinTime: math.MinInt64, - MaxTime: math.MaxInt64, + ExtLset: []labels.Labels{labels.FromStrings("ext1", "1")}, + MinTime: math.MinInt64, + MaxTime: math.MaxInt64, + WithoutReplicaLabelsEnabled: true, }} } @@ -1988,7 +1992,7 @@ func TestProxyStore_NotLeakingOnPrematureFinish(t *testing.T) { defer custom.TolerantVerifyLeak(t) clients := []Client{ - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ // Ensure more than 10 (internal respCh channel). @@ -2007,7 +2011,7 @@ func TestProxyStore_NotLeakingOnPrematureFinish(t *testing.T) { MinTime: math.MinInt64, MaxTime: math.MaxInt64, }, - &teststore.TestClient{ + &storetestutil.TestClient{ StoreClient: &mockedStoreAPI{ RespSeries: []*storepb.SeriesResponse{ storeSeriesResponse(t, labels.FromStrings("b", "a"), []sample{{0, 0}, {2, 1}, {3, 2}}), @@ -2051,7 +2055,7 @@ func TestProxyStore_NotLeakingOnPrematureFinish(t *testing.T) { } func TestProxyStore_storeMatchMetadata(t *testing.T) { - c := teststore.TestClient{} + c := storetestutil.TestClient{Name: "testaddr"} c.IsLocalStore = true ok, reason := storeMatchDebugMetadata(c, [][]*labels.Matcher{{}}) diff --git a/pkg/testutil/teststore/cient.go b/pkg/store/storepb/testutil/client.go similarity index 97% rename from pkg/testutil/teststore/cient.go rename to pkg/store/storepb/testutil/client.go index 6c72225205f..de1b9709a17 100644 --- a/pkg/testutil/teststore/cient.go +++ b/pkg/store/storepb/testutil/client.go @@ -1,4 +1,4 @@ -package teststore +package storetestutil import ( "github.com/prometheus/prometheus/model/labels"