Skip to content
This repository was archived by the owner on Jul 19, 2023. It is now read-only.

Commit c7d96b7

Browse files
authored
Use pyroscope tree representation in MergeProfilesStacktraces API (#702)
* Add pyroscope tree to the MergeProfilesStacktracesResult type * Add support for multiple merge result formats * Refactor tree and flamegraph types to the model package * Update reference-configuration-parameters * Add stack truncation implementation notes * Implement tree merge * Fix tree merge * Fix stacktrace rewrite * Refine tree merge * Fix tests * Refactor unused functions * Remove dead code * Fix tree min value counting
1 parent e965112 commit c7d96b7

File tree

31 files changed

+1607
-691
lines changed

31 files changed

+1607
-691
lines changed

api/gen/proto/go/ingester/v1/ingester.pb.go

Lines changed: 312 additions & 217 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/gen/proto/go/ingester/v1/ingester_vtproto.pb.go

Lines changed: 100 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/ingester/v1/ingester.proto

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,29 @@ message SelectProfilesRequest {
4242
int64 start = 3;
4343
int64 end = 4;
4444
}
45+
4546
message MergeProfilesStacktracesRequest {
4647
// The client starts the stream with a request containing the profile type and the labels.
4748
SelectProfilesRequest request = 1;
48-
49+
// Max nodes in the resulting tree.
50+
optional int64 max_nodes = 3;
4951
// On a batch of profiles, the client sends the profiles to keep for merging.
5052
repeated bool profiles = 2;
5153
}
5254

5355
message MergeProfilesStacktracesResult {
56+
StacktracesMergeFormat format = 3;
5457
// The list of stracktraces with their respective value
5558
repeated StacktraceSample stacktraces = 1;
5659
repeated string function_names = 2;
60+
// Merge result marshaled to pyroscope tree bytes.
61+
bytes tree_bytes = 4;
62+
}
63+
64+
enum StacktracesMergeFormat {
65+
MERGE_FORMAT_UNSPECIFIED = 0;
66+
MERGE_FORMAT_STACKTRACES = 1;
67+
MERGE_FORMAT_TREE = 2;
5768
}
5869

5970
message MergeProfilesStacktracesResponse {

api/openapiv2/gen/phlare.swagger.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,9 @@
722722
"v1MergeProfilesStacktracesResult": {
723723
"type": "object",
724724
"properties": {
725+
"format": {
726+
"$ref": "#/definitions/v1StacktracesMergeFormat"
727+
},
725728
"stacktraces": {
726729
"type": "array",
727730
"items": {
@@ -734,6 +737,11 @@
734737
"items": {
735738
"type": "string"
736739
}
740+
},
741+
"treeBytes": {
742+
"type": "string",
743+
"format": "byte",
744+
"description": "Merge result marshaled to pyroscope tree bytes."
737745
}
738746
}
739747
},
@@ -967,6 +975,15 @@
967975
}
968976
}
969977
},
978+
"v1StacktracesMergeFormat": {
979+
"type": "string",
980+
"enum": [
981+
"MERGE_FORMAT_UNSPECIFIED",
982+
"MERGE_FORMAT_STACKTRACES",
983+
"MERGE_FORMAT_TREE"
984+
],
985+
"default": "MERGE_FORMAT_UNSPECIFIED"
986+
},
970987
"v1State": {
971988
"type": "string",
972989
"enum": [

docs/sources/configure-server/reference-configuration-parameters/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ client:
153153

154154
api:
155155
# base URL for when the server is behind a reverse proxy with a different path
156-
# CLI flag: -base-url
156+
# CLI flag: -api.base-url
157157
[base-url: <string> | default = ""]
158158

159159
# The server block configures the HTTP and gRPC server of the launched

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/bufbuild/connect-go v1.4.1
77
github.com/bufbuild/connect-grpchealth-go v1.0.0
88
github.com/cespare/xxhash/v2 v2.2.0
9+
github.com/dennwc/varint v1.0.0
910
github.com/drone/envsubst v1.0.3
1011
github.com/dustin/go-humanize v1.0.0
1112
github.com/felixge/fgprof v0.9.4-0.20221116204635-ececf7638e93
@@ -115,7 +116,6 @@ require (
115116
github.com/coreos/go-semver v0.3.0 // indirect
116117
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
117118
github.com/davecgh/go-spew v1.1.1 // indirect
118-
github.com/dennwc/varint v1.0.0 // indirect
119119
github.com/dgraph-io/badger/v2 v2.2007.2 // indirect
120120
github.com/dgraph-io/ristretto v0.1.1 // indirect
121121
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect

pkg/querier/flamegraph.go renamed to pkg/model/flamegraph.go

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
package querier
1+
package model
22

33
import (
4-
"container/heap"
5-
64
"github.com/pyroscope-io/pyroscope/pkg/storage/metadata"
75
"github.com/pyroscope-io/pyroscope/pkg/structs/flamebearer"
86
"github.com/samber/lo"
@@ -11,13 +9,7 @@ import (
119
typesv1 "github.com/grafana/phlare/api/gen/proto/go/types/v1"
1210
)
1311

14-
type stackNode struct {
15-
xOffset int
16-
level int
17-
node *node
18-
}
19-
20-
func NewFlameGraph(t *tree, maxNodes int64) *querierv1.FlameGraph {
12+
func NewFlameGraph(t *Tree, maxNodes int64) *querierv1.FlameGraph {
2113
var total, max int64
2214
for _, node := range t.root {
2315
total += node.total
@@ -175,37 +167,3 @@ func ExportDiffToFlamebearer(fg *querierv1.FlameGraphDiff, profileType *typesv1.
175167

176168
return fb
177169
}
178-
179-
// minValue returns the minimum "total" value a node in a tree has to have to show up in
180-
// the resulting flamegraph
181-
func (t *tree) minValue(maxNodes int64) int64 {
182-
if maxNodes == -1 { // -1 means show all nodes
183-
return 0
184-
}
185-
nodes := t.root
186-
187-
mh := &minHeap{}
188-
heap.Init(mh)
189-
190-
for len(nodes) > 0 {
191-
node := nodes[0]
192-
nodes = nodes[1:]
193-
number := node.total
194-
195-
if mh.Len() < int(maxNodes) {
196-
heap.Push(mh, number)
197-
} else {
198-
if number > (*mh)[0] {
199-
heap.Pop(mh)
200-
heap.Push(mh, number)
201-
nodes = append(node.children, nodes...)
202-
}
203-
}
204-
}
205-
206-
if mh.Len() < int(maxNodes) {
207-
return 0
208-
}
209-
210-
return (*mh)[0]
211-
}

pkg/querier/flamegraph_diff.go renamed to pkg/model/flamegraph_diff.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package querier
1+
package model
22

33
import (
44
"bytes"
@@ -25,7 +25,7 @@ const MaxNodes = 8192
2525
// i+4 = total , right tree
2626
// i+5 = self , right tree
2727
// i+6 = index in the names array
28-
func NewFlamegraphDiff(left, right *tree, maxNodes int) (*querierv1.FlameGraphDiff, error) {
28+
func NewFlamegraphDiff(left, right *Tree, maxNodes int) (*querierv1.FlameGraphDiff, error) {
2929
// The algorithm doesn't work properly with negative nodes
3030
// Although it's possible to silently drop these nodes
3131
// Let's fail early and analyze properly with real data when the issue happens
@@ -148,7 +148,7 @@ func NewFlamegraphDiff(left, right *tree, maxNodes int) (*querierv1.FlameGraphDi
148148
// combineTree aligns 2 trees by making them having the same structure with the
149149
// same number of nodes
150150
// It also makes the tree have a single root
151-
func combineTree(leftTree, rightTree *tree) (*tree, *tree) {
151+
func combineTree(leftTree, rightTree *Tree) (*Tree, *Tree) {
152152
leftTotal := int64(0)
153153
for _, l := range leftTree.root {
154154
leftTotal = leftTotal + l.total
@@ -161,15 +161,15 @@ func combineTree(leftTree, rightTree *tree) (*tree, *tree) {
161161

162162
// differently from pyroscope, there could be multiple roots
163163
// so we add a fake root as expected
164-
leftTree = &tree{
164+
leftTree = &Tree{
165165
root: []*node{{
166166
children: leftTree.root,
167167
total: leftTotal,
168168
self: 0,
169169
}},
170170
}
171171

172-
rightTree = &tree{
172+
rightTree = &Tree{
173173
root: []*node{{
174174
children: rightTree.root,
175175
total: rightTotal,
@@ -263,7 +263,7 @@ func nextPow2(a int) int {
263263
return a
264264
}
265265

266-
func combineMinValues(leftTree, rightTree *tree, maxNodes int) uint64 {
266+
func combineMinValues(leftTree, rightTree *Tree, maxNodes int) uint64 {
267267
c := cappedarr.New(maxNodes)
268268
combineIterateWithTotal(leftTree, rightTree, func(left uint64, right uint64) bool {
269269
return c.Push(maxUint64(left, right))
@@ -272,7 +272,7 @@ func combineMinValues(leftTree, rightTree *tree, maxNodes int) uint64 {
272272
}
273273

274274
// iterate both trees, both trees must be returned from combineTree
275-
func combineIterateWithTotal(leftTree, rightTree *tree, cb func(uint64, uint64) bool) {
275+
func combineIterateWithTotal(leftTree, rightTree *Tree, cb func(uint64, uint64) bool) {
276276
leftNodes, rghtNodes := leftTree.root, rightTree.root
277277
i := 0
278278
for len(leftNodes) > 0 {
@@ -289,7 +289,7 @@ func combineIterateWithTotal(leftTree, rightTree *tree, cb func(uint64, uint64)
289289
}
290290

291291
// isPositiveTree returns whether a tree only contain positive values
292-
func isPositiveTree(t *tree) bool {
292+
func isPositiveTree(t *Tree) bool {
293293
stack := Stack[*node]{}
294294
for _, node := range t.root {
295295
stack.Push(node)
@@ -313,7 +313,7 @@ func isPositiveTree(t *tree) bool {
313313
return true
314314
}
315315

316-
func assertPositiveTrees(left *tree, right *tree) error {
316+
func assertPositiveTrees(left *Tree, right *Tree) error {
317317
leftRes := isPositiveTree(left)
318318
rightRes := isPositiveTree(right)
319319

pkg/querier/flamegraph_diff_test.go renamed to pkg/model/flamegraph_diff_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package querier
1+
package model
22

33
import (
44
"testing"

pkg/querier/flamegraph_test.go renamed to pkg/model/flamegraph_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package querier
1+
package model
22

33
import (
44
"fmt"

0 commit comments

Comments
 (0)