-
-
Notifications
You must be signed in to change notification settings - Fork 461
Hubs / Scopes Merge 18 - Implement pushScope ,popScope and withScope for Scopes
#3321
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
…pes-merge-2-add-scopes
pushScope ,popScope and withScope for ScopespushScope ,popScope and withScope for Scopes
|
| final @Nullable Scopes parent = getParent(); | ||
| if (parent != null) { | ||
| // TODO this is never closed | ||
| parent.makeCurrent(); |
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.
We're handed back a lifecycle token here which we never call close on. Popping scopes this way is kinda hacky. I haven't come up with a better approach yet.
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.
If I read it right makeCurrent() returns the token for the previous scope. Since this is legacy push/pop API, wouldn't it make sense to automatically close the previous scope?
| parent.makeCurrent(); | |
| final @NotNull ISentryLifecycleToken previousScopes = parent.makeCurrent(); | |
| previousScopes.close(); |
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.
The previous Scopes should be restored, when the current one is closed, so we can't close it when creating a new (child) scope.
Take a piece of nested code:
// outermost scopes (scopes1)
Sentry.withScope(scope -> {
// inner scopes (scopes2)
try { ... = scope.forkedCurrentScopes("...").makeCurrent()) {
// innermost scopes (scopes3)
}
}
When leaving the try block, we're closing scopes3 and restoring scopes2. When leaving the withScope lambda, we're closing scopes2 and restoring scopes1.
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 copied this pattern from OpenTelemetry, as we'll have to call their io.opentelemetry.context.Scope.close() inside our OTEL flavor lifecycle token. See https://github.com/getsentry/sentry-java/pull/3285/files#diff-ce5e1c852e10adfae80110c82615eaa9571a7eeb5ed8bb79512c08dc388c4bc0R43
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.
Think of this chain of lifecycle tokens as a replacement for the stack. We're keeping scopes from being garbage collected by holding a reference to each parent scopes instance.
| serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestHub); | ||
| Sentry.setCurrentScopes(requestHub); | ||
| requestHub.pushScope(); | ||
| requestHub.pushScope(); // TODO don't |
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.
pushScope by itself here wouldn't be problematic, however combining this with e.g. setting request on the requestHub reference, leads to unexpected results where e.g. request isn't set on captured messages.
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.
Instead of pushScope, maybe we could simply call requestHub.forkedCurrentScopes() and then call .makeCurrent() on that. Then from there on only use the reference, returned by the fork call.
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'm thinking tho we don't even need to call pushScope here since we're forking Scopes anyways.
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.
We could look into Mono.using() to do the cleanup for us in a follow up PR
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.
Let's test first and then follow up with a separate PR if it actually works / helps
Performance metrics 🚀
|
Is this really an issue? IMHO push/pop should only be used in combination with other old APIs like |
We'll see as bug reports come in, I guess. We can fix all our integrations but in theory it's possible users will experience some weird behaviour because of this. |
| Scopes forkedScopes = forkedScopes("withScope"); | ||
| Scopes forkedScopes = forkedCurrentScope("withScope"); | ||
| // TODO should forkedScopes be made current inside callback? | ||
| // TODO forkedScopes.makeCurrent()? |
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.
Depending on the behaviour we want to achieve, it might make sense to make them the currentScopes. IIRC Hub does this by pushing/popping scope around the callback.
We could just add the makeCurrent call to the the try/catch block here.
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.
Changed in #3375
#skip-changelog
📜 Description
Without having a stack as we used to in
Hubthe only option I see is to make pushScope etc. manipulate scopes storage.There is one major problem here, where we use a
Scopes(used to beHub) reference that isn'tScopesAdapter(used to beHubAdapter). I think the most relevant here are reactive frameworks (reactor / webflux). Here's problematic code:Because
pushScopenow creates a fork ofScopesand sets it as current, doing anything with thescopesreference, will lead to unexpected results. In this caserequestisn't set on the message being captured. The problem is, this used to work just fine withHubbecause of its internal stack.The main reason, we have so much code, using scopes (previously hub) references is testability. Maybe we should consider a different approach for testing, e.g. using a special scopes storage. However I don't want to fiddle with tests and production code too much at the same time, so I suggest leaving tests alone as much as possible and once production changes are (mostly) done, we can revisit our testing approach.
This PR also introduces
makeCurrentonScopeswhich I copied from OpenTelemetry. It stores aScopesin the scopes storage, making it the current one, that is used by static API.I'm not yet entirely certain how to handle
pushScopeandpopScope. Maybe we should deprecate them in favor of a single new method, that hands back a lifecycle token for closing (popping) and restoring previous state.💡 Motivation and Context
💚 How did you test it?
📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps