-
Notifications
You must be signed in to change notification settings - Fork 661
feat(v2): recording rules of function names #4232
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
Conversation
pkg/experiment/block/compaction.go
Outdated
// TODO: Find an alternative to this | ||
Flush(ProfileEntry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to find a way to avoid this. We want to observe row first, then symbols, then observe row again when symbols are rewritten. I tried some strategies but failed to make this interface simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the most confusing I find the naming here:
Flush
, is used already in the exporter and also there isflush()
.Observe
is not really observing yet untilObserveSymbols
orFlush
.
Maybe naming it Observe -> Prepare
and Flush -> ObserveValues
.
In terms of interface, I am not too sure if I would prefer a callback here, but it could be a possibility. It feels weird though with ObserveSymbols
:
if m.observer != nil {
observeValues := m.observer.Prepare(r)
defer observeValues()
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of deferred observation. Something like:
if observe := evaluate(entry); observe != nil {
defer observe()
}
I'm curious how it performs on widely spread functions, e.g., Go runtime.mallocgc
. What I had in mind is that we could resolve symbols once per aggregate (we'd need to aggregate all sample values first) or at dataset boundaries. That would also allow for slightly clearer contracts. I'm not proposing we implement this unless we encounter performance issues – just something that might be helpful in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for working on this @alsoba13.
You have done a good job making your implementation easy to follow and it is well structured. I also think you taken a lot of optimisations already into account, so I think we should merge this at this stage and get some real world usage to inform us what to do next.
I think before you merge, please make sure you cover the new code paths with some tests.
|
||
// StacktraceFilterFunctionNameRegExp functionNameRegExp = 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would remove those comments from here, to avoid confusing users.
|
||
// SELF = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would remove those comments from here, to avoid confusing users.
pkg/experiment/block/compaction.go
Outdated
// TODO: Find an alternative to this | ||
Flush(ProfileEntry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the most confusing I find the naming here:
Flush
, is used already in the exporter and also there isflush()
.Observe
is not really observing yet untilObserveSymbols
orFlush
.
Maybe naming it Observe -> Prepare
and Flush -> ObserveValues
.
In terms of interface, I am not too sure if I would prefer a callback here, but it could be a possibility. It feels weird though with ObserveSymbols
:
if m.observer != nil {
observeValues := m.observer.Prepare(r)
defer observeValues()
}
pkg/phlare/phlare.go
Outdated
@@ -593,6 +593,7 @@ func (f *Phlare) Run() error { | |||
"service_git_ref": serviceGitRef(), | |||
"service_repository": "https://github.com/grafana/pyroscope", | |||
}, | |||
//TenantID: "pyroscope-tenant", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clean me up
pkg/phlaredb/symdb/rewriter.go
Outdated
} | ||
|
||
func NewRewriter(w *SymDB, r SymbolsReader) *Rewriter { | ||
type SymbolsObserver interface { | ||
// ObserveSymbols is called once new symbols has been rewritten. This method must not modify the symbols. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth noting that Prepare()
should be called before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thanks Alberto for adding this! It looks great. I think we can iterate on the API and performance improvements in the future
537ee8f
to
89a24b0
Compare
Tests to cover observeSymbols will be added in a following PR |
Since #4232 we added supported for it.
Since #4232 we added supported for it.
This PR approaches recording rules of functions.
There's one part I'd like to change (marked as TODO), but as it mostly involves code style, and is not compromising performance yet, I prefer to introduce this PR with a clearer presented core logic.
You may want to read this before jumping to reviewing the code, as this may help:
Observer state
Data is organized and accessed sequentially this way:
...
...
...
...
Observer only see single rows (last column), and need to deduce if a context switch has occurred.
There are 3 types of context/states/scopes:
Observing symbols
We will try to avoid processing symbols that don't matter to any rule. With that purpose, row is Observed before symbols. Observe will compute the state of the series so we know if the series matches some rule with a FunctionName.
Observer will completely ignore symbols when the current Series state has no FunctionName rules. In a later series, if there's one rule that matches and has a FunctionName, symbols will be observed and pointers symbol-to-rule will be computed. As we only want to process symbols once for every new symbol rewritten, we will compute lookup tables of all rules that contain FunctionNames, not only the ones matching the current Series. The reason for this is that in a later series, a new rule with FunctionName may match and maybe all the symbols were already observed, and hence missed.
As a summary, requirements/properties we find: