github.com/grafana/pyroscope@v1.18.0/pkg/phlaredb/head_queriers.go (about)

     1  package phlaredb
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  
     7  	"connectrpc.com/connect"
     8  	"github.com/go-kit/log/level"
     9  	"github.com/opentracing/opentracing-go"
    10  	"github.com/parquet-go/parquet-go"
    11  	"github.com/pkg/errors"
    12  	"github.com/prometheus/common/model"
    13  
    14  	profilev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
    15  	ingestv1 "github.com/grafana/pyroscope/api/gen/proto/go/ingester/v1"
    16  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
    17  	"github.com/grafana/pyroscope/pkg/iter"
    18  	phlaremodel "github.com/grafana/pyroscope/pkg/model"
    19  	"github.com/grafana/pyroscope/pkg/phlaredb/query"
    20  	schemav1 "github.com/grafana/pyroscope/pkg/phlaredb/schemas/v1"
    21  	"github.com/grafana/pyroscope/pkg/phlaredb/symdb"
    22  )
    23  
    24  type headOnDiskQuerier struct {
    25  	head        *Head
    26  	rowGroupIdx int
    27  }
    28  
    29  func (q *headOnDiskQuerier) rowGroup() *rowGroupOnDisk {
    30  	q.head.profiles.rowsLock.RLock()
    31  	defer q.head.profiles.rowsLock.RUnlock()
    32  	return q.head.profiles.rowGroups[q.rowGroupIdx]
    33  }
    34  
    35  func (q *headOnDiskQuerier) Open(_ context.Context) error {
    36  	return nil
    37  }
    38  
    39  func (q *headOnDiskQuerier) BlockID() string {
    40  	return q.head.meta.ULID.String()
    41  }
    42  
    43  func (q *headOnDiskQuerier) SelectMatchingProfiles(ctx context.Context, params *ingestv1.SelectProfilesRequest) (iter.Iterator[Profile], error) {
    44  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMatchingProfiles - HeadOnDisk")
    45  	defer sp.Finish()
    46  
    47  	// query the index for rows
    48  	rowIter, labelsPerFP, err := q.head.profiles.index.selectMatchingRowRanges(ctx, params, q.rowGroupIdx)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	// get time nano information for profiles
    54  	var (
    55  		start = model.Time(params.Start)
    56  		end   = model.Time(params.End)
    57  	)
    58  	pIt := query.NewBinaryJoinIterator(0,
    59  		query.NewBinaryJoinIterator(
    60  			0,
    61  			rowIter,
    62  			q.rowGroup().columnIter(ctx, "TimeNanos", query.NewIntBetweenPredicate(start.UnixNano(), end.UnixNano()), "TimeNanos"),
    63  		),
    64  		q.rowGroup().columnIter(ctx, "StacktracePartition", nil, "StacktracePartition"),
    65  	)
    66  	defer pIt.Close()
    67  
    68  	var (
    69  		profiles []Profile
    70  		buf      = make([][]parquet.Value, 2)
    71  	)
    72  	for pIt.Next() {
    73  		res := pIt.At()
    74  
    75  		v, ok := res.Entries[0].RowValue.(fingerprintWithRowNum)
    76  		if !ok {
    77  			panic("no fingerprint information found")
    78  		}
    79  
    80  		lbls, ok := labelsPerFP[v.fp]
    81  		if !ok {
    82  			panic("no profile series labels with matching fingerprint found")
    83  		}
    84  
    85  		buf = res.Columns(buf, "TimeNanos", "StacktracePartition")
    86  		if len(buf) < 1 || len(buf[0]) != 1 {
    87  			level.Error(q.head.logger).Log("msg", "unable to read timeNanos from profiles", "row", res.RowNumber[0], "rowGroup", q.rowGroupIdx)
    88  			continue
    89  		}
    90  		profiles = append(profiles, BlockProfile{
    91  			labels:      lbls,
    92  			fingerprint: v.fp,
    93  			timestamp:   model.TimeFromUnixNano(buf[0][0].Int64()),
    94  			partition:   retrieveStacktracePartition(buf, 1),
    95  			rowNum:      res.RowNumber[0],
    96  		})
    97  	}
    98  	if err := pIt.Err(); err != nil {
    99  		return nil, errors.Wrap(pIt.Err(), "iterator error")
   100  	}
   101  
   102  	// Sort profiles by time, the slice is already sorted by series order
   103  	sort.Slice(profiles, func(i, j int) bool {
   104  		a, b := profiles[i], profiles[j]
   105  		if a.Timestamp() != b.Timestamp() {
   106  			return a.Timestamp() < b.Timestamp()
   107  		}
   108  		return phlaremodel.CompareLabelPairs(a.Labels(), b.Labels()) < 0
   109  	})
   110  
   111  	return iter.NewSliceIterator(profiles), nil
   112  }
   113  
   114  func (q *headOnDiskQuerier) SelectMergeByStacktraces(ctx context.Context, params *ingestv1.SelectProfilesRequest, maxNodes int64) (*phlaremodel.Tree, error) {
   115  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMergeByStacktraces - HeadOnDisk")
   116  	defer sp.Finish()
   117  
   118  	// query the index for rows
   119  	rowIter, _, err := q.head.profiles.index.selectMatchingRowRanges(ctx, params, q.rowGroupIdx)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	// get time nano information for profiles
   125  	var (
   126  		start = model.Time(params.Start)
   127  		end   = model.Time(params.End)
   128  	)
   129  	it := query.NewBinaryJoinIterator(0,
   130  		query.NewBinaryJoinIterator(
   131  			0,
   132  			rowIter,
   133  			q.rowGroup().columnIter(ctx, "TimeNanos", query.NewIntBetweenPredicate(start.UnixNano(), end.UnixNano()), ""),
   134  		),
   135  		q.rowGroup().columnIter(ctx, "StacktracePartition", nil, "StacktracePartition"))
   136  
   137  	rows := profileRowBatchIterator(it)
   138  	defer rows.Close()
   139  
   140  	r := symdb.NewResolver(ctx, q.head.symdb, symdb.WithResolverMaxNodes(maxNodes))
   141  	defer r.Release()
   142  
   143  	if err := mergeByStacktraces[rowProfile](ctx, q.rowGroup(), rows, r); err != nil {
   144  		return nil, err
   145  	}
   146  	return r.Tree()
   147  }
   148  
   149  func (q *headOnDiskQuerier) SelectMergeBySpans(ctx context.Context, params *ingestv1.SelectSpanProfileRequest) (*phlaremodel.Tree, error) {
   150  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMergeBySpans - HeadOnDisk")
   151  	defer sp.Finish()
   152  
   153  	// query the index for rows
   154  	rowIter, _, err := q.head.profiles.index.selectMatchingRowRanges(ctx, &ingestv1.SelectProfilesRequest{
   155  		LabelSelector: params.LabelSelector,
   156  		Type:          params.Type,
   157  		Start:         params.Start,
   158  		End:           params.End,
   159  		Hints:         params.Hints,
   160  	}, q.rowGroupIdx)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	spans, err := phlaremodel.NewSpanSelector(params.SpanSelector)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	// get time nano information for profiles
   170  	var (
   171  		start = model.Time(params.Start)
   172  		end   = model.Time(params.End)
   173  	)
   174  	it := query.NewBinaryJoinIterator(0,
   175  		query.NewBinaryJoinIterator(
   176  			0,
   177  			rowIter,
   178  			q.rowGroup().columnIter(ctx, "TimeNanos", query.NewIntBetweenPredicate(start.UnixNano(), end.UnixNano()), ""),
   179  		),
   180  		q.rowGroup().columnIter(ctx, "StacktracePartition", nil, "StacktracePartition"))
   181  
   182  	rows := profileRowBatchIterator(it)
   183  	defer rows.Close()
   184  
   185  	r := symdb.NewResolver(ctx, q.head.symdb)
   186  	defer r.Release()
   187  
   188  	if err = mergeBySpans[rowProfile](ctx, q.rowGroup(), rows, r, spans); err != nil {
   189  		return nil, err
   190  	}
   191  	return r.Tree()
   192  }
   193  
   194  func (q *headOnDiskQuerier) SelectMergePprof(ctx context.Context, params *ingestv1.SelectProfilesRequest, maxNodes int64, sts *typesv1.StackTraceSelector) (*profilev1.Profile, error) {
   195  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMergePprof - HeadOnDisk")
   196  	defer sp.Finish()
   197  
   198  	// query the index for rows
   199  	rowIter, _, err := q.head.profiles.index.selectMatchingRowRanges(ctx, params, q.rowGroupIdx)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	// get time nano information for profiles
   205  	var (
   206  		start = model.Time(params.Start)
   207  		end   = model.Time(params.End)
   208  	)
   209  	it := query.NewBinaryJoinIterator(0,
   210  		query.NewBinaryJoinIterator(
   211  			0,
   212  			rowIter,
   213  			q.rowGroup().columnIter(ctx, "TimeNanos", query.NewIntBetweenPredicate(start.UnixNano(), end.UnixNano()), ""),
   214  		),
   215  		q.rowGroup().columnIter(ctx, "StacktracePartition", nil, "StacktracePartition"))
   216  
   217  	rows := profileRowBatchIterator(it)
   218  	defer rows.Close()
   219  
   220  	r := symdb.NewResolver(ctx, q.head.symdb,
   221  		symdb.WithResolverMaxNodes(maxNodes),
   222  		symdb.WithResolverStackTraceSelector(sts))
   223  	defer r.Release()
   224  
   225  	if err = mergeByStacktraces[rowProfile](ctx, q.rowGroup(), rows, r); err != nil {
   226  		return nil, err
   227  	}
   228  	return r.Pprof()
   229  }
   230  
   231  func (q *headOnDiskQuerier) Bounds() (model.Time, model.Time) {
   232  	// TODO: Use per rowgroup information
   233  	return q.head.Bounds()
   234  }
   235  
   236  func (q *headOnDiskQuerier) ProfileTypes(context.Context, *connect.Request[ingestv1.ProfileTypesRequest]) (*connect.Response[ingestv1.ProfileTypesResponse], error) {
   237  	return connect.NewResponse(&ingestv1.ProfileTypesResponse{}), nil
   238  }
   239  
   240  func (q *headOnDiskQuerier) LabelValues(ctx context.Context, req *connect.Request[typesv1.LabelValuesRequest]) (*connect.Response[typesv1.LabelValuesResponse], error) {
   241  	return connect.NewResponse(&typesv1.LabelValuesResponse{}), nil
   242  }
   243  
   244  func (q *headOnDiskQuerier) LabelNames(ctx context.Context, req *connect.Request[typesv1.LabelNamesRequest]) (*connect.Response[typesv1.LabelNamesResponse], error) {
   245  	return connect.NewResponse(&typesv1.LabelNamesResponse{}), nil
   246  }
   247  
   248  func (q *headOnDiskQuerier) MergeByStacktraces(ctx context.Context, rows iter.Iterator[Profile], maxNodes int64) (*phlaremodel.Tree, error) {
   249  	sp, ctx := opentracing.StartSpanFromContext(ctx, "MergeByStacktraces")
   250  	defer sp.Finish()
   251  	r := symdb.NewResolver(ctx, q.head.symdb, symdb.WithResolverMaxNodes(maxNodes))
   252  	defer r.Release()
   253  	if err := mergeByStacktraces(ctx, q.rowGroup(), rows, r); err != nil {
   254  		return nil, err
   255  	}
   256  	return r.Tree()
   257  }
   258  
   259  func (q *headOnDiskQuerier) MergePprof(ctx context.Context, rows iter.Iterator[Profile], maxNodes int64, sts *typesv1.StackTraceSelector) (*profilev1.Profile, error) {
   260  	sp, ctx := opentracing.StartSpanFromContext(ctx, "MergePprof")
   261  	defer sp.Finish()
   262  	r := symdb.NewResolver(ctx, q.head.symdb,
   263  		symdb.WithResolverMaxNodes(maxNodes),
   264  		symdb.WithResolverStackTraceSelector(sts))
   265  	defer r.Release()
   266  	if err := mergeByStacktraces(ctx, q.rowGroup(), rows, r); err != nil {
   267  		return nil, err
   268  	}
   269  	return r.Pprof()
   270  }
   271  
   272  func (q *headOnDiskQuerier) MergeByLabels(ctx context.Context, rows iter.Iterator[Profile], sts *typesv1.StackTraceSelector, by ...string) ([]*typesv1.Series, error) {
   273  	sp, ctx := opentracing.StartSpanFromContext(ctx, "MergeByLabels - HeadOnDisk")
   274  	defer sp.Finish()
   275  	if len(sts.GetCallSite()) == 0 {
   276  		return mergeByLabels(ctx, q.rowGroup(), "TotalValue", rows, by...)
   277  	}
   278  	r := symdb.NewResolver(ctx, q.head.symdb,
   279  		symdb.WithResolverStackTraceSelector(sts))
   280  	defer r.Release()
   281  	return mergeByLabelsWithStackTraceSelector(ctx, q.rowGroup(), rows, r, by...)
   282  }
   283  
   284  func (q *headOnDiskQuerier) SelectMergeByLabels(ctx context.Context, params *ingestv1.SelectProfilesRequest, sts *typesv1.StackTraceSelector, by ...string) ([]*typesv1.Series, error) {
   285  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMergeByLabels - HeadOnDisk")
   286  	defer sp.Finish()
   287  
   288  	// query the index for rows
   289  	rowIter, labelsPerFP, err := q.head.profiles.index.selectMatchingRowRanges(ctx, params, q.rowGroupIdx)
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  
   294  	// get time nano information for profiles
   295  	var (
   296  		start = model.Time(params.Start).UnixNano()
   297  		end   = model.Time(params.End).UnixNano()
   298  	)
   299  	it := query.NewBinaryJoinIterator(
   300  		0,
   301  		rowIter,
   302  		q.rowGroup().columnIter(ctx, "TimeNanos", query.NewIntBetweenPredicate(start, end), "TimeNanos"),
   303  	)
   304  
   305  	if len(sts.GetCallSite()) == 0 {
   306  		rows := profileBatchIteratorByFingerprints(it, labelsPerFP)
   307  		defer rows.Close()
   308  		return mergeByLabels[Profile](ctx, q.rowGroup(), "TotalValue", rows, by...)
   309  	}
   310  
   311  	r := symdb.NewResolver(ctx, q.head.symdb,
   312  		symdb.WithResolverStackTraceSelector(sts))
   313  	defer r.Release()
   314  
   315  	it = query.NewBinaryJoinIterator(0, it, q.rowGroup().columnIter(ctx, "StacktracePartition", nil, "StacktracePartition"))
   316  	rows := profileBatchIteratorByFingerprints(it, labelsPerFP)
   317  	defer rows.Close()
   318  
   319  	return mergeByLabelsWithStackTraceSelector[Profile](ctx, q.rowGroup(), rows, r, by...)
   320  }
   321  
   322  func (q *headOnDiskQuerier) Series(ctx context.Context, params *ingestv1.SeriesRequest) ([]*typesv1.Labels, error) {
   323  	// The TSDB is kept in memory until the head block is flushed to disk.
   324  	return []*typesv1.Labels{}, nil
   325  }
   326  
   327  func (q *headOnDiskQuerier) MergeBySpans(ctx context.Context, rows iter.Iterator[Profile], spanSelector phlaremodel.SpanSelector) (*phlaremodel.Tree, error) {
   328  	sp, ctx := opentracing.StartSpanFromContext(ctx, "MergeBySpans")
   329  	defer sp.Finish()
   330  	r := symdb.NewResolver(ctx, q.head.symdb)
   331  	defer r.Release()
   332  	if err := mergeBySpans(ctx, q.rowGroup(), rows, r, spanSelector); err != nil {
   333  		return nil, err
   334  	}
   335  	return r.Tree()
   336  }
   337  
   338  func (q *headOnDiskQuerier) Sort(in []Profile) []Profile {
   339  	var rowI, rowJ int64
   340  	sort.Slice(in, func(i, j int) bool {
   341  		if pI, ok := in[i].(BlockProfile); ok {
   342  			rowI = pI.rowNum
   343  		}
   344  		if pJ, ok := in[j].(BlockProfile); ok {
   345  			rowJ = pJ.rowNum
   346  		}
   347  		return rowI < rowJ
   348  	})
   349  	return in
   350  }
   351  
   352  type headInMemoryQuerier struct {
   353  	head *Head
   354  }
   355  
   356  func (q *headInMemoryQuerier) Open(_ context.Context) error {
   357  	return nil
   358  }
   359  
   360  func (q *headInMemoryQuerier) BlockID() string {
   361  	return q.head.meta.ULID.String()
   362  }
   363  
   364  func (q *headInMemoryQuerier) SelectMatchingProfiles(ctx context.Context, params *ingestv1.SelectProfilesRequest) (iter.Iterator[Profile], error) {
   365  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMatchingProfiles - HeadInMemory")
   366  	defer sp.Finish()
   367  
   368  	index := q.head.profiles.index
   369  
   370  	ids, err := index.selectMatchingFPs(ctx, params)
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  
   375  	// get time nano information for profiles
   376  	var (
   377  		start = model.Time(params.Start)
   378  		end   = model.Time(params.End)
   379  	)
   380  
   381  	iters := make([]iter.Iterator[Profile], 0, len(ids))
   382  	index.mutex.RLock()
   383  	defer index.mutex.RUnlock()
   384  
   385  	for _, fp := range ids {
   386  		profileSeries, ok := index.profilesPerFP[fp]
   387  		if !ok {
   388  			continue
   389  		}
   390  
   391  		profiles := make([]*schemav1.InMemoryProfile, len(profileSeries.profiles))
   392  		copy(profiles, profileSeries.profiles)
   393  
   394  		iters = append(iters,
   395  			NewSeriesIterator(
   396  				profileSeries.lbs,
   397  				profileSeries.fp,
   398  				phlaremodel.NewTimeRangedIterator(iter.NewSliceIterator(profiles), start, end),
   399  			),
   400  		)
   401  	}
   402  
   403  	return phlaremodel.NewMergeIterator(maxBlockProfile, false, iters...), nil
   404  }
   405  
   406  func (q *headInMemoryQuerier) SelectMergeByStacktraces(ctx context.Context, params *ingestv1.SelectProfilesRequest, maxNodes int64) (*phlaremodel.Tree, error) {
   407  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMergeByStacktraces - HeadInMemory")
   408  	defer sp.Finish()
   409  	r := symdb.NewResolver(ctx, q.head.symdb, symdb.WithResolverMaxNodes(maxNodes))
   410  	defer r.Release()
   411  	index := q.head.profiles.index
   412  
   413  	ids, err := index.selectMatchingFPs(ctx, params)
   414  	if err != nil {
   415  		return nil, err
   416  	}
   417  
   418  	// get time nano information for profiles
   419  	var (
   420  		start = model.Time(params.Start)
   421  		end   = model.Time(params.End)
   422  	)
   423  
   424  	index.mutex.RLock()
   425  	defer index.mutex.RUnlock()
   426  
   427  	for _, fp := range ids {
   428  		profileSeries, ok := index.profilesPerFP[fp]
   429  		if !ok {
   430  			continue
   431  		}
   432  		for _, p := range profileSeries.profiles {
   433  			if p.Timestamp() < start {
   434  				continue
   435  			}
   436  			if p.Timestamp() > end {
   437  				break
   438  			}
   439  			r.AddSamples(p.StacktracePartition, p.Samples)
   440  		}
   441  	}
   442  	return r.Tree()
   443  }
   444  
   445  func (q *headInMemoryQuerier) SelectMergeBySpans(ctx context.Context, params *ingestv1.SelectSpanProfileRequest) (*phlaremodel.Tree, error) {
   446  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMergeBySpans - HeadInMemory")
   447  	defer sp.Finish()
   448  	r := symdb.NewResolver(ctx, q.head.symdb)
   449  	defer r.Release()
   450  	index := q.head.profiles.index
   451  
   452  	ids, err := index.selectMatchingFPs(ctx, &ingestv1.SelectProfilesRequest{
   453  		LabelSelector: params.LabelSelector,
   454  		Type:          params.Type,
   455  		Start:         params.Start,
   456  		End:           params.End,
   457  		Hints:         params.Hints,
   458  	})
   459  	if err != nil {
   460  		return nil, err
   461  	}
   462  	spans, err := phlaremodel.NewSpanSelector(params.SpanSelector)
   463  	if err != nil {
   464  		return nil, err
   465  	}
   466  
   467  	// get time nano information for profiles
   468  	var (
   469  		start = model.Time(params.Start)
   470  		end   = model.Time(params.End)
   471  	)
   472  
   473  	index.mutex.RLock()
   474  	defer index.mutex.RUnlock()
   475  
   476  	for _, fp := range ids {
   477  		profileSeries, ok := index.profilesPerFP[fp]
   478  		if !ok {
   479  			continue
   480  		}
   481  		for _, p := range profileSeries.profiles {
   482  			if p.Timestamp() < start {
   483  				continue
   484  			}
   485  			if p.Timestamp() > end {
   486  				break
   487  			}
   488  			if len(p.Samples.Spans) > 0 {
   489  				r.AddSamplesWithSpanSelector(p.StacktracePartition, p.Samples, spans)
   490  			}
   491  		}
   492  	}
   493  	return r.Tree()
   494  }
   495  
   496  func (q *headInMemoryQuerier) SelectMergePprof(ctx context.Context, params *ingestv1.SelectProfilesRequest, maxNodes int64, sts *typesv1.StackTraceSelector) (*profilev1.Profile, error) {
   497  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMergePprof - HeadInMemory")
   498  	defer sp.Finish()
   499  	r := symdb.NewResolver(ctx, q.head.symdb,
   500  		symdb.WithResolverMaxNodes(maxNodes),
   501  		symdb.WithResolverStackTraceSelector(sts))
   502  	defer r.Release()
   503  	index := q.head.profiles.index
   504  
   505  	ids, err := index.selectMatchingFPs(ctx, params)
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  
   510  	// get time nano information for profiles
   511  	var (
   512  		start = model.Time(params.Start)
   513  		end   = model.Time(params.End)
   514  	)
   515  
   516  	index.mutex.RLock()
   517  	defer index.mutex.RUnlock()
   518  
   519  	for _, fp := range ids {
   520  		profileSeries, ok := index.profilesPerFP[fp]
   521  		if !ok {
   522  			continue
   523  		}
   524  		for _, p := range profileSeries.profiles {
   525  			if p.Timestamp() < start {
   526  				continue
   527  			}
   528  			if p.Timestamp() > end {
   529  				break
   530  			}
   531  			r.AddSamples(p.StacktracePartition, p.Samples)
   532  		}
   533  	}
   534  	return r.Pprof()
   535  }
   536  
   537  func (q *headInMemoryQuerier) Bounds() (model.Time, model.Time) {
   538  	// TODO: Use per rowgroup information
   539  	return q.head.Bounds()
   540  }
   541  
   542  func (q *headInMemoryQuerier) ProfileTypes(ctx context.Context, req *connect.Request[ingestv1.ProfileTypesRequest]) (*connect.Response[ingestv1.ProfileTypesResponse], error) {
   543  	return q.head.ProfileTypes(ctx, req)
   544  }
   545  
   546  func (q *headInMemoryQuerier) LabelValues(ctx context.Context, req *connect.Request[typesv1.LabelValuesRequest]) (*connect.Response[typesv1.LabelValuesResponse], error) {
   547  	return q.head.LabelValues(ctx, req)
   548  }
   549  
   550  func (q *headInMemoryQuerier) LabelNames(ctx context.Context, req *connect.Request[typesv1.LabelNamesRequest]) (*connect.Response[typesv1.LabelNamesResponse], error) {
   551  	return q.head.LabelNames(ctx, req)
   552  }
   553  
   554  func (q *headInMemoryQuerier) MergeByStacktraces(ctx context.Context, rows iter.Iterator[Profile], maxNodes int64) (*phlaremodel.Tree, error) {
   555  	sp, _ := opentracing.StartSpanFromContext(ctx, "MergeByStacktraces - HeadInMemory")
   556  	defer sp.Finish()
   557  	r := symdb.NewResolver(ctx, q.head.symdb, symdb.WithResolverMaxNodes(maxNodes))
   558  	defer r.Release()
   559  	for rows.Next() {
   560  		p, ok := rows.At().(ProfileWithLabels)
   561  		if !ok {
   562  			return nil, errors.New("expected ProfileWithLabels")
   563  		}
   564  		r.AddSamples(p.StacktracePartition(), p.Samples())
   565  	}
   566  	if err := rows.Err(); err != nil {
   567  		return nil, err
   568  	}
   569  	return r.Tree()
   570  }
   571  
   572  func (q *headInMemoryQuerier) MergePprof(ctx context.Context, rows iter.Iterator[Profile], maxNodes int64, sts *typesv1.StackTraceSelector) (*profilev1.Profile, error) {
   573  	sp, _ := opentracing.StartSpanFromContext(ctx, "MergePprof - HeadInMemory")
   574  	defer sp.Finish()
   575  	r := symdb.NewResolver(ctx, q.head.symdb,
   576  		symdb.WithResolverMaxNodes(maxNodes),
   577  		symdb.WithResolverStackTraceSelector(sts))
   578  	defer r.Release()
   579  	for rows.Next() {
   580  		p, ok := rows.At().(ProfileWithLabels)
   581  		if !ok {
   582  			return nil, errors.New("expected ProfileWithLabels")
   583  		}
   584  		r.AddSamples(p.StacktracePartition(), p.Samples())
   585  	}
   586  	if err := rows.Err(); err != nil {
   587  		return nil, err
   588  	}
   589  	return r.Pprof()
   590  }
   591  
   592  func (q *headInMemoryQuerier) MergeByLabels(
   593  	ctx context.Context,
   594  	rows iter.Iterator[Profile],
   595  	sts *typesv1.StackTraceSelector,
   596  	by ...string,
   597  ) ([]*typesv1.Series, error) {
   598  	sp, _ := opentracing.StartSpanFromContext(ctx, "MergeByLabels - HeadInMemory")
   599  	defer sp.Finish()
   600  
   601  	seriesBuilder := phlaremodel.NewTimeSeriesBuilder(by...)
   602  	if len(sts.GetCallSite()) == 0 {
   603  		for rows.Next() {
   604  			p, ok := rows.At().(ProfileWithLabels)
   605  			if !ok {
   606  				return nil, errors.New("expected ProfileWithLabels")
   607  			}
   608  			seriesBuilder.Add(p.Fingerprint(), p.Labels(), int64(p.Timestamp()), float64(p.Total()), p.Annotations(), "")
   609  		}
   610  	} else {
   611  		r := symdb.NewResolver(ctx, q.head.symdb,
   612  			symdb.WithResolverStackTraceSelector(sts))
   613  		defer r.Release()
   614  		var v symdb.CallSiteValues
   615  		for rows.Next() {
   616  			p, ok := rows.At().(ProfileWithLabels)
   617  			if !ok {
   618  				return nil, errors.New("expected ProfileWithLabels")
   619  			}
   620  			if err := r.CallSiteValues(&v, p.StacktracePartition(), p.Samples()); err != nil {
   621  				return nil, err
   622  			}
   623  			seriesBuilder.Add(p.Fingerprint(), p.Labels(), int64(p.Timestamp()), float64(v.Total), p.Annotations(), "")
   624  		}
   625  	}
   626  
   627  	if err := rows.Err(); err != nil {
   628  		return nil, err
   629  	}
   630  
   631  	return seriesBuilder.Build(), nil
   632  }
   633  
   634  func (q *headInMemoryQuerier) SelectMergeByLabels(
   635  	ctx context.Context,
   636  	params *ingestv1.SelectProfilesRequest,
   637  	sts *typesv1.StackTraceSelector,
   638  	by ...string,
   639  ) ([]*typesv1.Series, error) {
   640  	sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMergeByLabels - HeadInMemory")
   641  	defer sp.Finish()
   642  
   643  	index := q.head.profiles.index
   644  
   645  	ids, err := index.selectMatchingFPs(ctx, params)
   646  	if err != nil {
   647  		return nil, err
   648  	}
   649  
   650  	// get time nano information for profiles
   651  	var (
   652  		start = model.Time(params.Start)
   653  		end   = model.Time(params.End)
   654  	)
   655  	seriesBuilder := phlaremodel.NewTimeSeriesBuilder(by...)
   656  
   657  	index.mutex.RLock()
   658  	defer index.mutex.RUnlock()
   659  
   660  	if len(sts.GetCallSite()) == 0 {
   661  		for _, fp := range ids {
   662  			profileSeries, ok := index.profilesPerFP[fp]
   663  			if !ok {
   664  				continue
   665  			}
   666  			for _, p := range profileSeries.profiles {
   667  				if p.Timestamp() < start {
   668  					continue
   669  				}
   670  				if p.Timestamp() > end {
   671  					break
   672  				}
   673  				seriesBuilder.Add(fp, profileSeries.lbs, int64(p.Timestamp()), float64(p.Total()), p.Annotations, "")
   674  			}
   675  		}
   676  	} else {
   677  		r := symdb.NewResolver(ctx, q.head.symdb,
   678  			symdb.WithResolverStackTraceSelector(sts))
   679  		defer r.Release()
   680  		var v symdb.CallSiteValues
   681  		for _, fp := range ids {
   682  			profileSeries, ok := index.profilesPerFP[fp]
   683  			if !ok {
   684  				continue
   685  			}
   686  			for _, p := range profileSeries.profiles {
   687  				if p.Timestamp() < start {
   688  					continue
   689  				}
   690  				if p.Timestamp() > end {
   691  					break
   692  				}
   693  				if err = r.CallSiteValues(&v, p.StacktracePartition, p.Samples); err != nil {
   694  					return nil, err
   695  				}
   696  				seriesBuilder.Add(fp, profileSeries.lbs, int64(p.Timestamp()), float64(v.Total), p.Annotations, "")
   697  			}
   698  		}
   699  	}
   700  	return seriesBuilder.Build(), nil
   701  }
   702  
   703  func (q *headInMemoryQuerier) Series(ctx context.Context, params *ingestv1.SeriesRequest) ([]*typesv1.Labels, error) {
   704  	res, err := q.head.Series(ctx, connect.NewRequest(params))
   705  	if err != nil {
   706  		return nil, err
   707  	}
   708  	return res.Msg.LabelsSet, nil
   709  }
   710  
   711  func (q *headInMemoryQuerier) MergeBySpans(ctx context.Context, rows iter.Iterator[Profile], spanSelector phlaremodel.SpanSelector) (*phlaremodel.Tree, error) {
   712  	sp, _ := opentracing.StartSpanFromContext(ctx, "MergeBySpans - HeadInMemory")
   713  	defer sp.Finish()
   714  	r := symdb.NewResolver(ctx, q.head.symdb)
   715  	defer r.Release()
   716  	for rows.Next() {
   717  		p, ok := rows.At().(ProfileWithLabels)
   718  		if !ok {
   719  			return nil, errors.New("expected ProfileWithLabels")
   720  		}
   721  		samples := p.Samples()
   722  		if len(samples.Spans) > 0 {
   723  			r.AddSamplesWithSpanSelector(p.StacktracePartition(), samples, spanSelector)
   724  		}
   725  	}
   726  	if err := rows.Err(); err != nil {
   727  		return nil, err
   728  	}
   729  	return r.Tree()
   730  }
   731  
   732  func (q *headInMemoryQuerier) Sort(in []Profile) []Profile {
   733  	return in
   734  }