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

Endless loop with DataSourceUtils in spring-jdbc #34484

Closed
drachenpalme opened this issue Feb 25, 2025 · 7 comments
Closed

Endless loop with DataSourceUtils in spring-jdbc #34484

drachenpalme opened this issue Feb 25, 2025 · 7 comments
Assignees
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) status: backported An issue that has been backported to maintenance branches status: feedback-provided Feedback has been provided type: bug A general bug
Milestone

Comments

@drachenpalme
Copy link

DataSourceUtils resolution getTargetConnection gets 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 the TransactionAwareDataSourceProxy and then creating an EntityManagerFactory. Then the datasource and the emf both get posted to a JpaTransactionManager.
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.
TransactionAwareDataSourceProxy gets 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 the TransactionAwareInvocationHandler.

Next step is, that this proxy gets bound to the current transaction-context in the TransactionSynchronizationManager. This happens during JpaTransactionManager.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 instanceof ConnectionProxy and therefore gets resolved within TransactionAwareDataSourceProxy, which delegates to DataSourceUtils.doGetConnection, which delegates to TransactionSynchronizationManager.getResource which 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?

@sbrannen sbrannen added status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged or decided on in: data Issues in data modules (jdbc, orm, oxm, tx) labels Feb 25, 2025
@sbrannen
Copy link
Member

sbrannen commented Feb 25, 2025

DataSourceUtils resolution getTargetConnection gets 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

@drachenpalme
Copy link
Author

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.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Feb 25, 2025
@sbrannen sbrannen added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Feb 25, 2025
@drachenpalme
Copy link
Author

Hi Sam,

I managed to distill the issue into a mini-project here:
https://github.com/drachenpalme/spring_ticket_34484

Upon executing Main.main you will first enter the TransactionalBean#doWithDb(), which actually does trigger unproxying the javax.sql.Connection prior to the doCommit() and therefore behaves as expected.
It will then enter TransactionalBean#doWithoutDb(), which will immediately return, not unproxying the Connection and 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#doReleaseConnection should be made aware, if a connection is completely unused.
Maybe ConnectionHolder.hasConnection should be more like ConnectionHolder.hasActiveConnection and that should check, whether the connectionHandle is a non-initialized proxy?

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Feb 25, 2025
@jhoeller jhoeller added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Feb 26, 2025
@jhoeller jhoeller self-assigned this Feb 26, 2025
@jhoeller jhoeller added this to the 6.2.4 milestone Feb 26, 2025
@jhoeller
Copy link
Contributor

jhoeller commented 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()) to emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter()) (the recommended variant which applies HibernateJpaDialect plus 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.

@jhoeller jhoeller added the for: backport-to-6.1.x Marks an issue as a candidate for backport to 6.1.x label Feb 26, 2025
@github-actions github-actions bot added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-6.1.x Marks an issue as a candidate for backport to 6.1.x labels Feb 26, 2025
@jhoeller
Copy link
Contributor

It's caused by the Hibernate connection release mode which our HibernateJpaVendorAdapter switches to DELAYED_ACQUISITION_AND_HOLD while Hibernate has a more aggressive release-and-reacquire policy by default. And only with the latter, we end up going in a loop where getTargetConnection seems to end up with the same uninitialized proxy that we called the method on.

@jhoeller
Copy link
Contributor

Aside from the Hibernate connection release mode, it is also unusual to provide a TransactionAwareDataSourceProxy to the JPA setup. The persistence provider should rather see the actual target DataSource, and only the JDBC-accessing applications beans need to see the TransactionAwareDataSourceProxy if 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 getTargetConnection handling within TransactionAwareDataSourceProxy. This revision is available in the latest 6.2.4 snapshot already, please give it an early try if you have the chance...

jhoeller added a commit that referenced this issue Feb 28, 2025
@drachenpalme
Copy link
Author

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 the TransactionAwareDataSourceProxy - 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) status: backported An issue that has been backported to maintenance branches status: feedback-provided Feedback has been provided type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants