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

Duplicated transaction manager #831

Closed
kewne opened this issue Feb 6, 2022 · 13 comments
Closed

Duplicated transaction manager #831

kewne opened this issue Feb 6, 2022 · 13 comments

Comments

@kewne
Copy link

kewne commented Feb 6, 2022

In 00b8aca#diff-1ebccb318e2d6a373043c744ca37eb7e8048d8130765f8904184795a593d5a89L92, @ConditionalOnMissingBean was removed from the springCloudTaskTransactionManager bean, which means that it will conflict with any other AutoConfiguration that creates a PlatformTransactionManager bean (such as JPA/Hibernate).

This repository contains a project that reproduces the error: https://github.com/kewne/spring-cloud-issue-repro;
trying to launch com.example.demo.DemoApplication fails with

java.lang.IllegalStateException: Failed to execute CommandLineRunner
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:772) ~[spring-boot-2.6.3.jar:2.6.3]
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:753) ~[spring-boot-2.6.3.jar:2.6.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:309) ~[spring-boot-2.6.3.jar:2.6.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.6.3.jar:2.6.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.6.3.jar:2.6.3]
	at com.example.demo.DemoApplication.main(DemoApplication.java:14) ~[classes/:na]
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,springCloudTaskTransactionManager
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1271) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:494) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:503) ~[spring-tx-5.3.15.jar:5.3.15]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:342) ~[spring-tx-5.3.15.jar:5.3.15]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.15.jar:5.3.15]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.15.jar:5.3.15]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.15.jar:5.3.15]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.15.jar:5.3.15]
	at com.example.demo.DemoService$$EnhancerBySpringCGLIB$$318b617c.foo(<generated>) ~[classes/:na]
	at com.example.demo.DemoApplication.lambda$runner$0(DemoApplication.java:19) ~[classes/:na]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:769) ~[spring-boot-2.6.3.jar:2.6.3]
	... 5 common frames omitted
@cppwfs
Copy link
Collaborator

cppwfs commented Feb 7, 2022

In your stack trace there are more than one PlatformTransactionManger that was found. In this case you need to specify which transaction manager you want to use. For example:

@Service
public class DemoService {

    @Transactional("transactionManager")
    public void foo() {

    }
}

@kewne
Copy link
Author

kewne commented Feb 8, 2022

I understood that's the issue, what I find weird is why Spring Cloud Task creates a new PlatformTransactionManager bean itself when, from what I could understand, it reuses an existing datasource;
IMO it could also reuse an existing transaction manager (and it seems to do so, in certain situations).
In effect, the current behavior forces code to be explicit about the transaction manager to use everywhere simply because one pulled Spring Cloud Task.

There's also the case where, if one creates a TaskConfigurer that returns an already existing PlatformTransactionManager, you still get the same error because apparently the same object is registered as two different beans.

@cppwfs
Copy link
Collaborator

cppwfs commented Feb 8, 2022

In this case it was to allow task to utilize its own transaction manager seperate from those that may be provided by the user.
More can be read about this issue from the original case: #652 and the subsequent PR that resolved it.

@kewne
Copy link
Author

kewne commented Feb 8, 2022

Shouldn't the transaction manager be provided by the TaskConfigurer, though, in which case also exposing it as a bean is unnecessary?

My points here are that:

  1. if you use Spring's transactional features as "normal", pulling in Spring Cloud Task breaks your setup; in particular, if you're using Boot and not even customizing anything, it seems like a reasonable expectation that things should "just work";
  2. there's nothing in the docs that even suggests one might run into this issue (I had to resort to looking through source code and github issues and PRs); the generic error message is also not very helpful.

Another alternative would be to add @ConditionalOnMissingBean(names = "springCloudTaskTransactionManager"), which is easy to document as "if you want to reuse a TransactionManager, add 'springCloudTaskTransactionManager' as an alias".

@cppwfs
Copy link
Collaborator

cppwfs commented Feb 8, 2022

Thank you @kewne for your issue and the thought that went into it.

  1. In this case Spring Cloud Task needs to respect the transaction managers that are provided by other starters and their settings that maybe different than those needed by task.
  2. Agreed this issue is not represented in the documentation and needs to be added. I'll update the label for this issue and add the docs.

@ankushthakur2593
Copy link

ankushthakur2593 commented Sep 27, 2022

I'm also facing the same issue and as a workaround to unblock, I've mentioned which transaction manager I want to use but there are too many files in my project which are using this annotation.

I've noticed that the solution for this issue is merged as part of this PR.
@cppwfs - Could you please share a tentative release date for the same?

@cppwfs
Copy link
Collaborator

cppwfs commented Feb 16, 2023

This issue was resolved with the following PR #858

@cppwfs cppwfs closed this as completed Feb 16, 2023
@Manu10744
Copy link

I'm using spring-cloud-starter-task version 3.0.2 and still get this error no matter if i specify spring.cloud.task.transaction-manager or not.

I have to inject a PlatformTransactionManager in my step methods and it always says it finds two..

@Manu10744
Copy link

Manu10744 commented Oct 20, 2023

Any solution to this?
I'm still having two transaction managers only because I have Spring Cloud Dependencies in my app. This breaks all of my @Transactional methods.

@kewne
Copy link
Author

kewne commented Oct 20, 2023

@Manu10744 AFAICT #858 was apparently never merged and, looking at the current code, it doesn't look like it was cherry-picked at all, so the issue still exists.

@Manu10744
Copy link

Manu10744 commented Oct 20, 2023

@kewne I think it is because take a look at https://github.com/fmbenhassine/spring-cloud-task/blob/e4581420b04e872f8aac4cb3ad8bab62bbe9c941/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java

The TransactionManager Bean is annotated with Conditional(NoTransactionManagerProperty.class)

So, i am 100% sure I have this property in my application.yml. So why is this Bean still getting created??

@cppwfs Could you please explain?

@AhHa45
Copy link

AhHa45 commented Nov 13, 2023

@kewne I think it is because take a look at fmbenhassine/spring-cloud-task@e458142/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java

The TransactionManager Bean is annotated with Conditional(NoTransactionManagerProperty.class)

So, i am 100% sure I have this property in my application.yml. So why is this Bean still getting created??

@cppwfs Could you please explain?

I have the same issue using spring boot 2.7.16 and spring-cloud-task 2.4.6
any news on how to fix this without upgrading?

@cppwfs
Copy link
Collaborator

cppwfs commented Nov 17, 2023

Closed via #934

@cppwfs cppwfs closed this as completed Nov 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants