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

instrument spring scheduling @Async #1873

Merged
merged 6 commits into from
Sep 18, 2020
Merged

Conversation

richardstartin
Copy link
Member

@richardstartin richardstartin commented Sep 16, 2020

This works by instrumenting the @ Async interceptor and wrapping the MethodInvocation to capture the active context, if it exists.

@richardstartin richardstartin requested a review from a team as a code owner September 16, 2020 10:11
@richardstartin richardstartin marked this pull request as draft September 16, 2020 10:11
@richardstartin richardstartin force-pushed the rgs/spring-scheduling-async branch from eae92f3 to 02cc033 Compare September 16, 2020 12:29
@richardstartin richardstartin marked this pull request as ready for review September 16, 2020 12:29
@richardstartin richardstartin force-pushed the rgs/spring-scheduling-async branch 7 times, most recently from 4893d7f to a9f2d81 Compare September 17, 2020 09:45
Copy link
Contributor

@mcculls mcculls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@richardstartin
Copy link
Member Author

LGTM

Well, instrumenting the interceptor was your idea...

Copy link
Contributor

@bantonsson bantonsson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice and targeted!

@richardstartin richardstartin force-pushed the rgs/spring-scheduling-async branch from a9f2d81 to a7d7f25 Compare September 17, 2020 17:39
@richardstartin richardstartin force-pushed the rgs/spring-scheduling-async branch from a7d7f25 to bc2fb5e Compare September 17, 2020 18:37

@Override
public ElementMatcher<? super TypeDescription> typeMatcher() {
return named("org.springframework.aop.interceptor.AsyncExecutionInterceptor");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little concerned about adding instrumentation for a class that seems relatively unrelated to scheduling in the module for spring-scheduling. Is @Async used for anything besides spring scheduling?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This intercepts the annotation (notice the package) org.springframework.scheduling.annotation.Async.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this seems pretty clearly related to Async to me. Although, I'll admit their package naming makes me a little concern that maybe AsyncExecutionInterceptor does more.

But presuming AsyncExecutionInterceptor is specific to @Async annotations, I think this is probably the right place.
It solves my general aim which to let the frameworks find the target methods for us -- rather than us seeking them out.

scope.setAsyncPropagation(true);
return delegate.proceed();
} finally {
// question: Why can't this just be AutoCloseable? Dogma?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a fair bit of history here, but I can't find any original sources. I think these summarize the problem well:
opentracing/opentracing-java#361 (comment)
https://github.com/Nike-Inc/wingtips#warning-about-error-handling-when-using-try-with-resources-to-autoclose-spans

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That reads like an issue with programming discipline, and sacrifices convenience in the common case for the inability to be incorrect in the rare case. Just my opinion.

Comment on lines +39 to +42
// question: is this necessary? What does it do?
// if the delegate does async work is everything OK because of this?
// if the delegate does async work, should I need to worry about it here?
scope.setAsyncPropagation(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will hopefully be unnecessary with the planned changes to PendingTrace.

@Advice.OnMethodEnter(suppress = Throwable.class)
public static void scheduleAsync(
@Advice.Argument(value = 0, readOnly = false) MethodInvocation invocation) {
invocation = new SpannedMethodInvocation(activeSpan(), invocation);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One other thing to mention is that currently this will result in the span likely being reported as a separate trace since you're not using continuations to force the trace to stick around.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is fine modeling wise because it's timing does not matter for the critical path

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by saying it is a separate trace, isn't it just spans sent separately? That's what I think it should be.

I think today. These spans might go to PendingTrace that is thought to be done, but I think that's a problem with PendingTrace.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dougqh to clarify, I mean it would likely be reported independently from the rest of the trace.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like the wrong API to me. I should need to call AgentSpan.finish() to finish the span. DDSpanContext has a PendingTrace - so why shouldn't AgentSpan.finish() be enough to hold the PendingTrace back?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, right now, an unfinished span holds back sending the trace leg.
But that's leading to problems where an unfinished span, holds back the whole trace.

I think we want to get away from that.

My thought is that a span is published in the first payload when it finishes before the root span.
(Except for long root spans for batch jobs where we need to send sooner).

Otherwise, the span is deemed to have finished late. If it makes it with a grace period, it can be included. Otherwise, it goes separately. But basically, it is only guarantee if it finishes before the root.

@richardstartin richardstartin force-pushed the rgs/spring-scheduling-async branch from cb4317d to cd7bd80 Compare September 17, 2020 19:13
@richardstartin richardstartin merged commit f2ae36b into master Sep 18, 2020
@richardstartin richardstartin deleted the rgs/spring-scheduling-async branch September 18, 2020 08:23
@github-actions github-actions bot added this to the 0.63.0 milestone Sep 18, 2020
@richardstartin
Copy link
Member Author

Fixes #1510

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants