Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkg/query: Sort samples before generating flame charts #5489

Merged
merged 3 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ require (
github.com/olekukonko/tablewriter v0.0.5
github.com/parquet-go/parquet-go v0.24.0
github.com/planetscale/vtprotobuf v0.6.1-0.20241121165744-79df5c4772f2
github.com/polarsignals/frostdb v0.0.0-20250107161604-e9ab6f315eae
github.com/polarsignals/frostdb v0.0.0-20250218183442-9c9972458112
github.com/polarsignals/iceberg-go v0.0.0-20240502213135-2ee70b71e76b
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/common v0.62.0
Expand All @@ -55,7 +55,7 @@ require (
go.opentelemetry.io/proto/otlp v1.3.1
go.uber.org/atomic v1.11.0
go.uber.org/automaxprocs v1.6.0
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c
golang.org/x/net v0.35.0
golang.org/x/oauth2 v0.26.0
golang.org/x/sync v0.11.0
Expand Down Expand Up @@ -238,7 +238,7 @@ require (
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.28.0 // indirect
golang.org/x/tools v0.29.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20241121165744-79df5c4772f2/go.mod h1
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polarsignals/frostdb v0.0.0-20250107161604-e9ab6f315eae h1:Pg4yVBVw4O5O4TR+XCEU9pE0oKra4MOkq7pDaJUq0NM=
github.com/polarsignals/frostdb v0.0.0-20250107161604-e9ab6f315eae/go.mod h1:Pdv/t9Qrx2c2dTbQTwsOmrgCvsz9fpcktVoq/6Lnjm0=
github.com/polarsignals/frostdb v0.0.0-20250218183442-9c9972458112 h1:nL8vkrs13TXKhLgOrYol3ziC4eH0UGFh1IjgrdwrsY0=
github.com/polarsignals/frostdb v0.0.0-20250218183442-9c9972458112/go.mod h1:/uYMJOhlj30NEsZUSxZMi3XvoYEtmMIPqM81bPVPoDk=
github.com/polarsignals/iceberg-go v0.0.0-20240502213135-2ee70b71e76b h1:Dbm5itapR0uYIMujR8OntWpDJ/nm5OM6JiaKauLcZ4Y=
github.com/polarsignals/iceberg-go v0.0.0-20240502213135-2ee70b71e76b/go.mod h1:5T9ChEZjRNhAGGLwH1cqzDA7wXB84SmU+WkXQr/ZAjo=
github.com/polarsignals/wal v0.0.0-20240619104840-9da940027f9c h1:ReFgEXqZ9/y+/9ZdNHOa1L62wqt8mWqoqrWutWj2x+A=
Expand Down Expand Up @@ -939,8 +939,8 @@ golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4=
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down Expand Up @@ -1106,8 +1106,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
51 changes: 51 additions & 0 deletions pkg/query/columnquery.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/polarsignals/frostdb/pqarrow/arrowutils"

metastorev1alpha1 "github.com/parca-dev/parca/gen/proto/go/parca/metastore/v1alpha1"
pb "github.com/parca-dev/parca/gen/proto/go/parca/query/v1alpha1"
sharepb "github.com/parca-dev/parca/gen/proto/go/parca/share/v1alpha1"
Expand Down Expand Up @@ -600,6 +602,55 @@ func RenderReport(
},
}, nil
case pb.QueryRequest_REPORT_TYPE_FLAMEGRAPH_ARROW, pb.QueryRequest_REPORT_TYPE_FLAMECHART:
if typ == pb.QueryRequest_REPORT_TYPE_FLAMECHART {
// Generating the flame chart assumes a single record that is sorted by timestamp.
for i, sample := range p.Samples {
indices := sample.Schema().FieldIndices(FlamegraphFieldTimestamp)
if len(indices) != 1 {
return nil, status.Errorf(codes.Internal, "invalid flame chart timestamp indices: %v", indices)
}
sortedIndices, err := arrowutils.SortRecord(sample, []arrowutils.SortingColumn{
{Index: indices[0], Direction: arrowutils.Ascending},
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to sort flame chart record: %v", err.Error())
}

isSorted := true
for j := 0; j < sortedIndices.Len(); j++ {
if sortedIndices.Value(j) != int32(j) {
isSorted = false
break
}
}
if isSorted {
// Don't sort if the indices are already sorted.
continue
}

sorted, err := arrowutils.Take(ctx, sample, sortedIndices)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to sort flame chart record: %v", err.Error())
}

p.Samples[i] = sorted
}

if len(p.Samples) > 1 {
indices := p.Samples[0].Schema().FieldIndices(FlamegraphFieldTimestamp)
if len(indices) != 1 {
return nil, status.Errorf(codes.Internal, "invalid flame chart timestamp indices: %v", indices)
}
sorted, err := arrowutils.MergeRecords(mem, p.Samples, []arrowutils.SortingColumn{
{Index: indices[0], Direction: arrowutils.Ascending},
}, 0)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to merge flame chart records: %v", err.Error())
}
p.Samples = []arrow.Record{sorted}
}
}

fa, total, err := GenerateFlamegraphArrow(ctx, mem, tracer, p, groupBy, nodeTrimFraction)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to generate arrow flamegraph: %v", err.Error())
Expand Down
8 changes: 6 additions & 2 deletions pkg/query/flamegraph_arrow.go
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,9 @@ func (fb *flamegraphBuilder) mergeUnsymbolizedRows(
}

func matchRowsByTimestamp(compareTimestamp, compareDuration, timestamp, duration int64) (bool, error) {
if compareTimestamp > timestamp {
return false, fmt.Errorf("compareTimestamp > timestamp: %d > %d", compareTimestamp, timestamp)
}
if compareTimestamp == timestamp {
return false, fmt.Errorf("multiple samples for the same timestamp is not allowed: %d", timestamp)
}
Expand All @@ -764,8 +767,9 @@ func matchRowsByTimestamp(compareTimestamp, compareDuration, timestamp, duration
// We truncate 10% jitter. We use duration which usually is the period.
// For example, for 19hz sampling rate, we'll get a duration of 1000ms/19hz = 52.63ms
// and 10% are 5.2ms jitter that gets truncated.
truncated := difference.Truncate(time.Duration(duration / 10))
return truncated == 0, nil
jitter := time.Duration(duration / 10)
truncated := difference - jitter
return truncated <= 0, nil
}

func (fb *flamegraphBuilder) intersectLabels(
Expand Down
21 changes: 21 additions & 0 deletions pkg/query/flamegraph_arrow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1459,3 +1459,24 @@ func drawFlamegraphToConsole(testing *testing.T, record arrow.Record) {
populateChild(t, 0)
fmt.Println(t)
}

func Test_matchRowsByTimestamp(t *testing.T) {
second := time.Second.Nanoseconds()

tests := []struct {
name string
ct, cd, t, d int64
match bool
}{
{"0", 0, second, second, second, true},
{"1/100", 0, second, second + second/100, second, true},
{"1/10", 0, second, second + second/10, second, true},
{"1/5", 0, second, second + second/5, second, false},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
m, _ := matchRowsByTimestamp(tc.ct, tc.cd, tc.t, tc.d)
require.Equal(t, tc.match, m)
})
}
}
Loading