spring-cloud-sleuth icon indicating copy to clipboard operation
spring-cloud-sleuth copied to clipboard

TraceAsyncCustomAutoConfiguration configures a BeanPostProcessor that changes the type of AsyncConfigurer beans and breaks dependency resolution

Open wilkinsona opened this issue 3 years ago • 3 comments

Describe the bug

Please see https://github.com/spring-projects/spring-boot/issues/29151 for details.

Sample

This zipped Maven project will reproduce the problem using Sleuth 3.0.5 without really involving Boot. Running the main method will fail:

12:12:49.395 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2401f4c3
12:12:49.414 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
12:12:49.536 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
12:12:49.538 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
12:12:49.539 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
12:12:49.540 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
12:12:49.543 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'asyncBeanPostProcessor'
12:12:49.559 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'gh29151Application'
12:12:49.560 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'exampleConfigurer'
12:12:49.606 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2401f4c3, started on Wed Jan 19 12:12:49 GMT 2022
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'exampleConfigurer' is expected to be of type 'com.example.demo.Gh29151Application$Example' but was actually of type 'org.springframework.cloud.sleuth.instrument.async.LazyTraceAsyncCustomizer'
	at org.springframework.beans.factory.support.AbstractBeanFactory.adaptBeanInstance(AbstractBeanFactory.java:417)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:398)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1160)
	at com.example.demo.Gh29151Application.main(Gh29151Application.java:14)

The failure does not occur without the TraceAsyncCustomAutoConfiguration bean post-processor.

wilkinsona avatar Jan 19 '22 12:01 wilkinsona

Here is a workaround if you do not want to disable Sleuth for async (spring.sleuth.async.enabled=false). You have to exclude autoconfiguration TraceAsyncCustomAutoConfiguration and wrap your executor with LazyTraceExecutor:

spring:
  autoconfigure:
    exclude:
      - org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncCustomAutoConfiguration
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

  private final BeanFactory beanFactory;

  public AsyncConfig(BeanFactory beanFactory) {
    this.beanFactory = beanFactory;
  }

  @Override
  public Executor getAsyncExecutor() {
    Executor executor = new ThreadPoolTaskExecutor(); // create or configure your executor
    return new LazyTraceExecutor(beanFactory, executor);
  }
}

Saljack avatar Feb 11 '22 14:02 Saljack

This is blocked by https://github.com/spring-projects/spring-framework/pull/27953

marcingrzejszczak avatar May 18 '22 11:05 marcingrzejszczak

Another workaround you can do without modifying any other settings is this.

before

@Configuration
@EnableAsync
class AsyncConfiguration : AsyncConfigurer {
    override fun getAsyncExecutor(): Executor {
        val executor = ThreadPoolTaskExecutor()
        executor.initialize()
        return executor
    }

    override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
        return YourCustomAsyncUncaughtExceptionHandler()
    }
}

after

@Configuration
@EnableAsync
class AsyncConfiguration {

    @Bean
    fun asyncConfigurer(): AsyncConfigurer {
        return object : AsyncConfigurer {
            override fun getAsyncExecutor(): Executor {
                val executor = ThreadPoolTaskExecutor()
                executor.initialize()
                return executor
            }

            override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
                return YourCustomAsyncUncaughtExceptionHandler()
            }
        }
    }
}

lette1394 avatar Aug 18 '22 16:08 lette1394

Do we have an fix for this issue. We are still seeing the error with Spring boot 2.6.8 and 3.1.0 version of sleuth.

Arunanandam123 avatar Dec 13 '22 14:12 Arunanandam123

May I ask for any progresses or any better solutions?

jxfruit avatar Jun 29 '23 04:06 jxfruit

Adding the following configuration into the application.properties and keeping the @Bean annotation above the override method getAsyncExecutor() can be a solution. spring.sleuth.async.enabled=false

My spring version is spring-boot:2.6.7 and spirng-cloud:3.1.x

jxfruit avatar Jun 30 '23 03:06 jxfruit

keeping the @bean annotation above the override method getAsyncExecutor() can be a solution. spring.sleuth.async.enabled=false

this is not working for me

Abhishek-Khelge avatar Jul 11 '23 06:07 Abhishek-Khelge

@Saljack @lette1394 The following solutions mentioned are not working for me. Here is the sample reproducible code https://github.com/maradanasai/otel-async-exper.git . Using spring boot 2.7.14 and spring cloud 2021.0.8 version with jdk 17. It is blocking migrating to latest versions.

maradanasai avatar Aug 21 '23 09:08 maradanasai

There's a PR open in Framework that should be fixing this (https://github.com/spring-projects/spring-framework/pull/27953) you can write a comment there that you're being blocked because of this

marcingrzejszczak avatar Aug 21 '23 09:08 marcingrzejszczak

Since Spring Framework won't merge this PR. The only thing we can do is to document that in cases of exceptions you must

  • disable sleuth async
  • manually instrument the executors using their trace representations

example:

spring.sleuth.async.enabled=false

and configuration example

@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {

	private final BeanFactory beanFactory;

	public AsyncConfiguration(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	@Override
    @Bean("AsyncTaskExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.initialize();
        return new LazyTraceThreadPoolTaskExecutor(beanFactory, executor);
    }
}

I will update the documentation

marcingrzejszczak avatar Aug 22 '23 10:08 marcingrzejszczak