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 }