Repository navigation
Endless loop with DataSourceUtils in spring-jdbc #34484
Description
Activity
- addedstatus: waiting-for-feedbackWe need additional information before we can continueWe need additional information before we can continuestatus: waiting-for-triageAn issue we've not yet triaged or decided onAn issue we've not yet triaged or decided onin: dataIssues in data modules (jdbc, orm, oxm, tx)Issues in data modules (jdbc, orm, oxm, tx)
on Feb 25, 2025 DataSourceUtils resolution
getTargetConnectiongets stuck in an endless loop under certain conditions in spring-jdbc 6.2.2/6.2.3.Are you claiming that this is a regression that did not occur before Spring Framework 6.2.2?
In any case, can you please provide a small sample application that reproduces the problem (preferably something that we can download and run, such as a public Git repository or a ZIP file attached to this issue)?
Thanks
Sorry, I didn't make that clear. It occurs both in 6.2.2 and 6.2.3, I have not tested other versions.
I will try to create a sample app.Reacted by Sam BrannenReacted by Bruce GuoReacted by Bruce Guo- addedstatus: feedback-providedFeedback has been providedFeedback has been providedand removedstatus: waiting-for-feedbackWe need additional information before we can continueWe need additional information before we can continue
on Feb 25, 2025 - addedstatus: waiting-for-feedbackWe need additional information before we can continueWe need additional information before we can continueand removedstatus: feedback-providedFeedback has been providedFeedback has been provided
on Feb 25, 2025 Hi Sam,
I managed to distill the issue into a mini-project here:
https://github.com/drachenpalme/spring_ticket_34484Upon executing Main.main you will first enter the
TransactionalBean#doWithDb(), which actually does trigger unproxying thejavax.sql.Connectionprior to thedoCommit()and therefore behaves as expected.
It will then enterTransactionalBean#doWithoutDb(), which will immediately return, not unproxying theConnectionand therefor triggering the issue.Please let me know, if you need further help or info. As I said, as I do not understand the concepts within the spring code, I am not sure, what the best way to solve this is. My assumption would be,
DataSouerceUtils#doReleaseConnectionshould be made aware, if a connection is completely unused.
MaybeConnectionHolder.hasConnectionshould be more likeConnectionHolder.hasActiveConnectionand that should check, whether the connectionHandle is a non-initialized proxy?- addedstatus: feedback-providedFeedback has been providedFeedback has been providedand removedstatus: waiting-for-feedbackWe need additional information before we can continueWe need additional information before we can continue
on Feb 25, 2025 - addedtype: bugA general bugA general bugand removedstatus: waiting-for-triageAn issue we've not yet triaged or decided onAn issue we've not yet triaged or decided on
on Feb 26, 2025 I was able to reproduce this locally but only with the very specific configuration in your repro project. Changing
emf.setJpaDialect(new HibernateJpaDialect())toemf.setJpaVendorAdapter(new HibernateJpaVendorAdapter())(the recommended variant which appliesHibernateJpaDialectplus a few extra settings) makes the test pass for me. Digging deeper to find out why this is so nuanced. In any case, we need to be able to defensively handle uninitialized proxies in all scenarios.Reacted by Sam Brannen- addedstatus: backportedAn issue that has been backported to maintenance branchesAn issue that has been backported to maintenance branchesand removed
on Feb 26, 2025 It's caused by the Hibernate connection release mode which our
HibernateJpaVendorAdapterswitches toDELAYED_ACQUISITION_AND_HOLDwhile Hibernate has a more aggressive release-and-reacquire policy by default. And only with the latter, we end up going in a loop wheregetTargetConnectionseems to end up with the same uninitialized proxy that we called the method on.- added a commit that references this issue
on Feb 28, 2025 Aside from the Hibernate connection release mode, it is also unusual to provide a
TransactionAwareDataSourceProxyto the JPA setup. The persistence provider should rather see the actual target DataSource, and only the JDBC-accessing applications beans need to see theTransactionAwareDataSourceProxyif they perform direct DataSource interactions. This is part of the problem here, without that part the test passes fine as well.In any case, I've revised this for more defensiveness in
getTargetConnectionhandling withinTransactionAwareDataSourceProxy. This revision is available in the latest 6.2.4 snapshot already, please give it an early try if you have the chance...- added a commit that references this issue
on Feb 28, 2025 Thanks a lot for the quick fix.
I actually successfully tested your suggestion to set the vendoradapter instead of the jpadialect, which works well enough.
As for your remark in relation to theTransactionAwareDataSourceProxy- thanks a lot. I will keep this in mind. All of this is part of the solution of some really ugly bug with some heavy trial and error.
DataSourceUtils resolution
getTargetConnectiongets stuck in an endless loop under certain conditions in spring-jdbc 6.2.2/6.2.3.I am creating a
DataSource, wrapping it into theTransactionAwareDataSourceProxyand then creating anEntityManagerFactory. Then the datasource and the emf both get posted to aJpaTransactionManager.Upon trying to commit a transaction, the application goes into an endless loop in
DataSourceUtils.getTargetConnection().As far as I could understand it by debugging, the following happens.
TransactionAwareDataSourceProxygets asked for a connection and creates a lazy initialization proxy. As this proxy is lazy, it does not initialize an inner. The proxy handler is theTransactionAwareInvocationHandler.Next step is, that this proxy gets bound to the current transaction-context in the
TransactionSynchronizationManager. This happens duringJpaTransactionManager.doBegin(Object, TransactionDefinition).Now, during release of the connection while committing, the connection proxy gets into
DataSourceUtils.getTargetConnection()and there it is determined, it is an instanceofConnectionProxyand therefore gets resolved withinTransactionAwareDataSourceProxy, which delegates toDataSourceUtils.doGetConnection, which delegates toTransactionSynchronizationManager.getResourcewhich returns the uninitialized proxy.I am not quite sure, why most invocations do not trigger this issue and some do. My current assumption is, that there is no issue, as long as the databaseconnection is actually used within the transaction. We have, however, cases, where a spring bean caches data, requests a transaction but does not actually trigger database queries upon cache-hits.
I am not quite sure, how to solve this correctly. Probably a non-initialized proxy for a connection should not be registered at all within the
TransactionAwareDataSourceProxy?