github.com/grafana/pyroscope@v1.18.0/pkg/frontend/frontend_select_time_series.go (about) 1 package frontend 2 3 import ( 4 "context" 5 "time" 6 7 "connectrpc.com/connect" 8 "github.com/grafana/dskit/tenant" 9 "github.com/prometheus/common/model" 10 "golang.org/x/sync/errgroup" 11 12 querierv1 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1" 13 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1/querierv1connect" 14 phlaremodel "github.com/grafana/pyroscope/pkg/model" 15 "github.com/grafana/pyroscope/pkg/util/connectgrpc" 16 validationutil "github.com/grafana/pyroscope/pkg/util/validation" 17 "github.com/grafana/pyroscope/pkg/validation" 18 ) 19 20 func (f *Frontend) SelectSeries( 21 ctx context.Context, 22 c *connect.Request[querierv1.SelectSeriesRequest], 23 ) (*connect.Response[querierv1.SelectSeriesResponse], error) { 24 ctx = connectgrpc.WithProcedure(ctx, querierv1connect.QuerierServiceSelectSeriesProcedure) 25 tenantIDs, err := tenant.TenantIDs(ctx) 26 if err != nil { 27 return nil, connect.NewError(connect.CodeInvalidArgument, err) 28 } 29 30 validated, err := validation.ValidateRangeRequest(f.limits, tenantIDs, model.Interval{Start: model.Time(c.Msg.Start), End: model.Time(c.Msg.End)}, model.Now()) 31 if err != nil { 32 return nil, connect.NewError(connect.CodeInvalidArgument, err) 33 } 34 if validated.IsEmpty { 35 return connect.NewResponse(&querierv1.SelectSeriesResponse{}), nil 36 } 37 c.Msg.Start = int64(validated.Start) 38 c.Msg.End = int64(validated.End) 39 40 g, ctx := errgroup.WithContext(ctx) 41 if maxConcurrent := validationutil.SmallestPositiveNonZeroIntPerTenant(tenantIDs, f.limits.MaxQueryParallelism); maxConcurrent > 0 { 42 g.SetLimit(maxConcurrent) 43 } 44 45 m := phlaremodel.NewTimeSeriesMerger(true) 46 interval := validationutil.MaxDurationOrZeroPerTenant(tenantIDs, f.limits.QuerySplitDuration) 47 intervals := NewTimeIntervalIterator(time.UnixMilli(c.Msg.Start), time.UnixMilli(c.Msg.End), interval, 48 WithAlignment(time.Second*time.Duration(c.Msg.Step))) 49 50 for intervals.Next() { 51 r := intervals.At() 52 g.Go(func() error { 53 req := connectgrpc.CloneRequest(c, &querierv1.SelectSeriesRequest{ 54 ProfileTypeID: c.Msg.ProfileTypeID, 55 LabelSelector: c.Msg.LabelSelector, 56 Start: r.Start.UnixMilli(), 57 End: r.End.UnixMilli(), 58 GroupBy: c.Msg.GroupBy, 59 Step: c.Msg.Step, 60 Aggregation: c.Msg.Aggregation, 61 StackTraceSelector: c.Msg.StackTraceSelector, 62 }) 63 resp, err := connectgrpc.RoundTripUnary[ 64 querierv1.SelectSeriesRequest, 65 querierv1.SelectSeriesResponse](ctx, f, req) 66 if err != nil { 67 return err 68 } 69 m.MergeTimeSeries(resp.Msg.Series) 70 return nil 71 }) 72 } 73 74 if err = g.Wait(); err != nil { 75 return nil, err 76 } 77 78 series := m.Top(int(c.Msg.GetLimit())) 79 return connect.NewResponse(&querierv1.SelectSeriesResponse{Series: series}), nil 80 }