github.com/grafana/pyroscope@v1.18.0/pkg/frontend/readpath/query_service_handler.go (about)

     1  package readpath
     2  
     3  import (
     4  	"context"
     5  	"slices"
     6  
     7  	"connectrpc.com/connect"
     8  	"golang.org/x/sync/errgroup"
     9  
    10  	profilev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
    11  	querierv1 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1"
    12  	"github.com/grafana/pyroscope/api/gen/proto/go/querier/v1/querierv1connect"
    13  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
    14  	phlaremodel "github.com/grafana/pyroscope/pkg/model"
    15  	"github.com/grafana/pyroscope/pkg/pprof"
    16  )
    17  
    18  var _ querierv1connect.QuerierServiceHandler = (*Router)(nil)
    19  
    20  func (r *Router) LabelValues(
    21  	ctx context.Context,
    22  	c *connect.Request[typesv1.LabelValuesRequest],
    23  ) (*connect.Response[typesv1.LabelValuesResponse], error) {
    24  	return Query[typesv1.LabelValuesRequest, typesv1.LabelValuesResponse](ctx, r, c,
    25  		func(_, _ *typesv1.LabelValuesRequest) {},
    26  		func(a, b *typesv1.LabelValuesResponse) (*typesv1.LabelValuesResponse, error) {
    27  			m := phlaremodel.NewLabelMerger()
    28  			m.MergeLabelValues(a.Names)
    29  			m.MergeLabelValues(b.Names)
    30  			return &typesv1.LabelValuesResponse{Names: m.LabelValues()}, nil
    31  		})
    32  }
    33  
    34  func (r *Router) LabelNames(
    35  	ctx context.Context,
    36  	c *connect.Request[typesv1.LabelNamesRequest],
    37  ) (*connect.Response[typesv1.LabelNamesResponse], error) {
    38  	return Query[typesv1.LabelNamesRequest, typesv1.LabelNamesResponse](ctx, r, c,
    39  		func(_, _ *typesv1.LabelNamesRequest) {},
    40  		func(a, b *typesv1.LabelNamesResponse) (*typesv1.LabelNamesResponse, error) {
    41  			m := phlaremodel.NewLabelMerger()
    42  			m.MergeLabelNames(a.Names)
    43  			m.MergeLabelNames(b.Names)
    44  			return &typesv1.LabelNamesResponse{Names: m.LabelNames()}, nil
    45  		})
    46  }
    47  
    48  func (r *Router) Series(
    49  	ctx context.Context,
    50  	c *connect.Request[querierv1.SeriesRequest],
    51  ) (*connect.Response[querierv1.SeriesResponse], error) {
    52  	return Query[querierv1.SeriesRequest, querierv1.SeriesResponse](ctx, r, c,
    53  		func(_, _ *querierv1.SeriesRequest) {},
    54  		func(a, b *querierv1.SeriesResponse) (*querierv1.SeriesResponse, error) {
    55  			m := phlaremodel.NewLabelMerger()
    56  			m.MergeSeries(a.LabelsSet)
    57  			m.MergeSeries(b.LabelsSet)
    58  			return &querierv1.SeriesResponse{LabelsSet: m.Labels()}, nil
    59  		})
    60  }
    61  
    62  func (r *Router) SelectMergeStacktraces(
    63  	ctx context.Context,
    64  	c *connect.Request[querierv1.SelectMergeStacktracesRequest],
    65  ) (*connect.Response[querierv1.SelectMergeStacktracesResponse], error) {
    66  	// We always query data in the tree format and
    67  	// return it in the format requested by the client.
    68  	f := c.Msg.Format
    69  	c.Msg.Format = querierv1.ProfileFormat_PROFILE_FORMAT_TREE
    70  	resp, err := Query[querierv1.SelectMergeStacktracesRequest, querierv1.SelectMergeStacktracesResponse](ctx, r, c,
    71  		func(_, _ *querierv1.SelectMergeStacktracesRequest) {},
    72  		func(a, b *querierv1.SelectMergeStacktracesResponse) (*querierv1.SelectMergeStacktracesResponse, error) {
    73  			m := phlaremodel.NewTreeMerger()
    74  			if err := m.MergeTreeBytes(a.Tree); err != nil {
    75  				return nil, err
    76  			}
    77  			if err := m.MergeTreeBytes(b.Tree); err != nil {
    78  				return nil, err
    79  			}
    80  			tree := m.Tree().Bytes(c.Msg.GetMaxNodes())
    81  			return &querierv1.SelectMergeStacktracesResponse{Tree: tree}, nil
    82  		},
    83  	)
    84  	if err == nil && f != c.Msg.Format {
    85  		resp.Msg.Flamegraph = phlaremodel.NewFlameGraph(
    86  			phlaremodel.MustUnmarshalTree(resp.Msg.Tree),
    87  			c.Msg.GetMaxNodes())
    88  	}
    89  	return resp, err
    90  }
    91  
    92  func (r *Router) SelectMergeSpanProfile(
    93  	ctx context.Context,
    94  	c *connect.Request[querierv1.SelectMergeSpanProfileRequest],
    95  ) (*connect.Response[querierv1.SelectMergeSpanProfileResponse], error) {
    96  	// We always query data in the tree format and
    97  	// return it in the format requested by the client.
    98  	f := c.Msg.Format
    99  	c.Msg.Format = querierv1.ProfileFormat_PROFILE_FORMAT_TREE
   100  	resp, err := Query[querierv1.SelectMergeSpanProfileRequest, querierv1.SelectMergeSpanProfileResponse](ctx, r, c,
   101  		func(_, _ *querierv1.SelectMergeSpanProfileRequest) {},
   102  		func(a, b *querierv1.SelectMergeSpanProfileResponse) (*querierv1.SelectMergeSpanProfileResponse, error) {
   103  			m := phlaremodel.NewTreeMerger()
   104  			if err := m.MergeTreeBytes(a.Tree); err != nil {
   105  				return nil, err
   106  			}
   107  			if err := m.MergeTreeBytes(b.Tree); err != nil {
   108  				return nil, err
   109  			}
   110  			tree := m.Tree().Bytes(c.Msg.GetMaxNodes())
   111  			return &querierv1.SelectMergeSpanProfileResponse{Tree: tree}, nil
   112  		},
   113  	)
   114  	if err == nil && f != c.Msg.Format {
   115  		resp.Msg.Flamegraph = phlaremodel.NewFlameGraph(
   116  			phlaremodel.MustUnmarshalTree(resp.Msg.Tree),
   117  			c.Msg.GetMaxNodes())
   118  	}
   119  	return resp, err
   120  }
   121  
   122  func (r *Router) SelectMergeProfile(
   123  	ctx context.Context,
   124  	c *connect.Request[querierv1.SelectMergeProfileRequest],
   125  ) (*connect.Response[profilev1.Profile], error) {
   126  	return Query[querierv1.SelectMergeProfileRequest, profilev1.Profile](ctx, r, c,
   127  		func(_, _ *querierv1.SelectMergeProfileRequest) {},
   128  		func(a, b *profilev1.Profile) (*profilev1.Profile, error) {
   129  			var m pprof.ProfileMerge
   130  			if err := m.Merge(a, false); err != nil {
   131  				return nil, err
   132  			}
   133  			if err := m.Merge(b, false); err != nil {
   134  				return nil, err
   135  			}
   136  			return m.Profile(), nil
   137  		})
   138  }
   139  
   140  func (r *Router) SelectSeries(
   141  	ctx context.Context,
   142  	c *connect.Request[querierv1.SelectSeriesRequest],
   143  ) (*connect.Response[querierv1.SelectSeriesResponse], error) {
   144  	limit := int(c.Msg.GetLimit())
   145  	return Query[querierv1.SelectSeriesRequest, querierv1.SelectSeriesResponse](ctx, r, c,
   146  		func(a, b *querierv1.SelectSeriesRequest) {
   147  			// If both frontends are queries, the limit must
   148  			// be applied after merging the time series, as
   149  			// the function is not associative. Otherwise, each
   150  			// frontend applies the limit independently.
   151  			if a != nil && b != nil && limit > 0 {
   152  				a.Limit = nil
   153  				b.Limit = nil
   154  			}
   155  		},
   156  		func(a, b *querierv1.SelectSeriesResponse) (*querierv1.SelectSeriesResponse, error) {
   157  			m := phlaremodel.NewTimeSeriesMerger(true)
   158  			m.MergeTimeSeries(a.Series)
   159  			m.MergeTimeSeries(b.Series)
   160  			return &querierv1.SelectSeriesResponse{Series: m.Top(limit)}, nil
   161  		})
   162  }
   163  
   164  func (r *Router) Diff(
   165  	ctx context.Context,
   166  	c *connect.Request[querierv1.DiffRequest],
   167  ) (*connect.Response[querierv1.DiffResponse], error) {
   168  	g, ctx := errgroup.WithContext(ctx)
   169  	getTree := func(dst *phlaremodel.Tree, req *querierv1.SelectMergeStacktracesRequest) func() error {
   170  		return func() error {
   171  			resp, err := r.SelectMergeStacktraces(ctx, connect.NewRequest(req))
   172  			if err != nil {
   173  				return err
   174  			}
   175  			tree, err := phlaremodel.UnmarshalTree(resp.Msg.Tree)
   176  			if err != nil {
   177  				return err
   178  			}
   179  			*dst = *tree
   180  			return nil
   181  		}
   182  	}
   183  
   184  	var left, right phlaremodel.Tree
   185  	g.Go(getTree(&left, c.Msg.Left))
   186  	g.Go(getTree(&right, c.Msg.Right))
   187  	if err := g.Wait(); err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	diff, err := phlaremodel.NewFlamegraphDiff(&left, &right, 0)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	return connect.NewResponse(&querierv1.DiffResponse{Flamegraph: diff}), nil
   197  }
   198  
   199  // Stubs: these methods are not supposed to be implemented
   200  // and only needed to satisfy interfaces.
   201  
   202  func (r *Router) AnalyzeQuery(
   203  	ctx context.Context,
   204  	req *connect.Request[querierv1.AnalyzeQueryRequest],
   205  ) (*connect.Response[querierv1.AnalyzeQueryResponse], error) {
   206  	if r.oldFrontend != nil {
   207  		return r.oldFrontend.AnalyzeQuery(ctx, req)
   208  	}
   209  	return connect.NewResponse(&querierv1.AnalyzeQueryResponse{}), nil
   210  }
   211  
   212  func (r *Router) GetProfileStats(
   213  	ctx context.Context,
   214  	c *connect.Request[typesv1.GetProfileStatsRequest],
   215  ) (*connect.Response[typesv1.GetProfileStatsResponse], error) {
   216  	return Query[typesv1.GetProfileStatsRequest, typesv1.GetProfileStatsResponse](ctx, r, c,
   217  		func(_, _ *typesv1.GetProfileStatsRequest) {},
   218  		func(a, b *typesv1.GetProfileStatsResponse) (*typesv1.GetProfileStatsResponse, error) {
   219  			oldestProfileTime := a.OldestProfileTime
   220  			newestProfileTime := a.NewestProfileTime
   221  			if b.OldestProfileTime < oldestProfileTime {
   222  				oldestProfileTime = b.OldestProfileTime
   223  			}
   224  			if b.NewestProfileTime > newestProfileTime {
   225  				newestProfileTime = b.NewestProfileTime
   226  			}
   227  			return &typesv1.GetProfileStatsResponse{
   228  				DataIngested:      a.DataIngested || b.DataIngested,
   229  				OldestProfileTime: oldestProfileTime,
   230  				NewestProfileTime: newestProfileTime,
   231  			}, nil
   232  		})
   233  }
   234  
   235  func (r *Router) ProfileTypes(
   236  	ctx context.Context,
   237  	c *connect.Request[querierv1.ProfileTypesRequest],
   238  ) (*connect.Response[querierv1.ProfileTypesResponse], error) {
   239  	return Query[querierv1.ProfileTypesRequest, querierv1.ProfileTypesResponse](ctx, r, c,
   240  		func(_, _ *querierv1.ProfileTypesRequest) {},
   241  		func(a, b *querierv1.ProfileTypesResponse) (*querierv1.ProfileTypesResponse, error) {
   242  			pTypes := a.ProfileTypes
   243  			for _, pType := range b.ProfileTypes {
   244  				if !slices.Contains(pTypes, pType) {
   245  					pTypes = append(pTypes, pType)
   246  				}
   247  			}
   248  			return &querierv1.ProfileTypesResponse{ProfileTypes: pTypes}, nil
   249  		})
   250  }