diff --git a/pkg/storegateway/bucket.go b/pkg/storegateway/bucket.go index 5b539e1d642..82a540da602 100644 --- a/pkg/storegateway/bucket.go +++ b/pkg/storegateway/bucket.go @@ -562,17 +562,6 @@ func (s *BucketStore) Series(req *storepb.SeriesRequest, srv storepb.Store_Serie err = status.Error(code, err.Error()) }() - if s.queryGate != nil { - tracing.DoWithSpan(srv.Context(), "store_query_gate_ismyturn", func(ctx context.Context, _ tracing.Span) { - err = s.queryGate.Start(srv.Context()) - }) - if err != nil { - return errors.Wrapf(err, "failed to wait for turn") - } - - defer s.queryGate.Done() - } - matchers, err := storepb.MatchersToPromMatchers(req.Matchers...) if err != nil { return status.Error(codes.InvalidArgument, err.Error()) @@ -620,6 +609,16 @@ func (s *BucketStore) Series(req *storepb.SeriesRequest, srv storepb.Store_Serie readers = newChunkReaders(chunkReaders) } + // Wait for the query gate only after opening blocks. Opening blocks is usually fast (~1ms), + // but sometimes it can take minutes if the block isn't loaded and there is a surge in queries for unloaded blocks. + tracing.DoWithSpan(ctx, "store_query_gate_ismyturn", func(ctx context.Context, _ tracing.Span) { + err = s.queryGate.Start(ctx) + }) + if err != nil { + return errors.Wrapf(err, "failed to wait for turn") + } + defer s.queryGate.Done() + var ( // If we are streaming the series labels and chunks separately, we don't need to fetch the postings // twice. So we use these slices to re-use them. Each reuse[i] corresponds to a single block.