Skip to content

Observability of @Async annotated methods does not work #29977

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

Closed
jmecsei opened this issue Feb 15, 2023 · 9 comments
Closed

Observability of @Async annotated methods does not work #29977

jmecsei opened this issue Feb 15, 2023 · 9 comments
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: superseded An issue that has been superseded by another theme: observability An issue related to observability and tracing

Comments

@jmecsei
Copy link

jmecsei commented Feb 15, 2023

Here is an example how we can configure Spring if we want to observ the async methods:

    @Bean(name = "taskExecutor", destroyMethod = "shutdown")
    ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler() {
            @Override
            protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
                ExecutorService executorService = super.initializeExecutor(threadFactory, rejectedExecutionHandler);
                return ContextExecutorService.wrap(executorService, ContextSnapshot::captureAll);
            }
        };
        threadPoolTaskScheduler.initialize();
        return threadPoolTaskScheduler;
    }

I think this approach can not be work because, the Spring store the executor in local variable: https://github.com/spring-projects/spring-framework/blob/main/spring-context/src/m[…]ringframework/scheduling/concurrent/ThreadPoolTaskExecutor.java

Thus when we wrap the result : ContextExecutorService.wrap(executorService), the Spring uses this wrapped Executor only in shutdown() process. https://github.com/spring-projects/spring-framework/blob/main/spring-context/src/m[…]amework/scheduling/concurrent/ExecutorConfigurationSupport.java

I cc: @marcingrzejszczak according to our slack conversation

@jmecsei
Copy link
Author

jmecsei commented Feb 15, 2023

A small example to to reproduce the problem: https://github.com/jmecsei/micrometer-threadpool

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Feb 15, 2023
@marcingrzejszczak
Copy link
Contributor

@bclozel this will be related to instrumenting @Scheduled too I guess

@simonbasle
Copy link
Contributor

This probably doesn't solve everything but this should get you a long way:

ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler() {

	@Nullable
	ScheduledExecutorService contextScheduler;

	@Override
	public ScheduledExecutorService getScheduledExecutor() throws IllegalStateException {
		Assert.state(this.contextScheduler != null, "ThreadPoolTaskScheduler not initialized");
		return this.contextScheduler;
	}

	@Override
	protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
		ExecutorService executorService = super.initializeExecutor(threadFactory, rejectedExecutionHandler);
		if (executorService instanceof ScheduledExecutorService scheduledExecutorService) {
			//the wrapped executor to be used in getScheduledExecutor(), which is used for actual task submission
			this.contextScheduler = ContextScheduledExecutorService.wrap(scheduledExecutorService, ContextSnapshot::captureAll);
			//returning the original, which will be affected to private scheduledExecutor field.
			//maintains the concrete type (notably, is it a configurable pool).
			return executorService;
		}
		throw new IllegalStateException("initializeExecutor didn't return a ScheduledExecutorService");
	}
};

@rstoyanchev rstoyanchev added the in: core Issues in core modules (aop, beans, core, context, expression) label Feb 23, 2023
@bclozel
Copy link
Member

bclozel commented Mar 7, 2023

I don't think we should create dedicated observations for @Async methods - this is just a signal that tells Framework that this regular method should be executed asynchronously; this does not qualify as a signal that the developer wants this method to be instrumented. I think that @Scheduled methods are different there since this annotation carries a different meaning. This will be instrumented in #29883.

After chatting with @marcingrzejszczak, we agreed that context propagation should be taken care of in #29883. We'll reopen this issue if it turns out this is not enough.

I'm declining this issue as a result.

@bclozel bclozel closed this as not planned Won't fix, can't repro, duplicate, stale Mar 7, 2023
@bclozel bclozel added status: declined A suggestion or change that we don't feel we should currently apply theme: observability An issue related to observability and tracing and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Mar 7, 2023
@vpavic
Copy link
Contributor

vpavic commented Jul 10, 2023

Could this be reconsidered? I recently opened #30832 that was marked as a duplicate of this one.

this is just a signal that tells Framework that this regular method should be executed asynchronously

The way I see it, annotating some method with @Async is an implementation detail and I don't think that by itself should have an impact on whether some code is instrumented or not.

If some codepath is being instrumented, and somewhere along the lines it call a method that's @Async then my expectation would be that whatever happens in that method is instrumented the same way as if it wasn't @Async. Quite frequently @Async is used to invoke some non-essential but potentially expensive operations - for example, you don't want those to impact the thread that handles HTTP request but is still in some ways part of the request handling. I think it's a reasonable expectation that whatever is behind @Async to be a part of the parent trace.

@bclozel
Copy link
Member

bclozel commented Jul 10, 2023

If some codepath is being instrumented, and somewhere along the lines it call a method that's @async then my expectation would be that whatever happens in that method is instrumented the same way as if it wasn't @async

So I guess this is not about creating observations but rather context propagation?

We could consider this, although @Async is so ubiquitous that I'm wondering if there are cases where propagating the current observation as parent doesn't make sense. For example, in cases of deferred work, using the caller context as the trace parent can break the traces because the execution is too long or happens way after the initial trace. For these cases, trace links are better choices.

After chatting with @marcingrzejszczak, we agreed that context propagation should be taken care of in #29883. We'll reopen this issue if it turns out this is not enough.

I don't think this happened in the end with the implementation for the reasons listed above. Let me think about this more.

@vpavic
Copy link
Contributor

vpavic commented Jul 11, 2023

So I guess this is not about creating observations but rather context propagation?

This sounds correct as far as my expectations are concerned (ones that prompted me to open #30089). ATM I can't think of a scenario where I wouldn't want @Async code to be a part of the trace that invoked that code.

Also I should familiarize myself with the correct terminology in the whole observability area. 🙂

@marcingrzejszczak
Copy link
Contributor

Wouldn't there be currently an option to just ask the user to wrap an executor service with Context Propagation API?

@bclozel
Copy link
Member

bclozel commented Aug 29, 2023

Superseded by #31130

@bclozel bclozel added status: superseded An issue that has been superseded by another and removed status: declined A suggestion or change that we don't feel we should currently apply labels Aug 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: superseded An issue that has been superseded by another theme: observability An issue related to observability and tracing
Projects
None yet
Development

No branches or pull requests

7 participants