Skip to content

Endless loop with DataSourceUtils in spring-jdbc #34484

Description

@drachenpalme

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?

Activity

  1. sbrannen commented on Feb 25, 2025

    @sbrannen
    Member

    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

  2. drachenpalme commented on Feb 25, 2025

    @drachenpalme
    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.

  3. drachenpalme commented on Feb 25, 2025

    @drachenpalme
    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?

  4. added and removed on Feb 26, 2025
  5. self-assigned this
    on Feb 26, 2025
  6. added this to the 6.2.4 milestone on Feb 26, 2025
  7. jhoeller commented on Feb 26, 2025

    @jhoeller
    Contributor

    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.

  8. jhoeller commented on Feb 26, 2025

    @jhoeller
    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.

  9. added a commit that references this issue on Feb 28, 2025
    c64dae3
  10. jhoeller commented on Feb 28, 2025

    @jhoeller
    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...

  11. added a commit that references this issue on Feb 28, 2025
    1e31cd6
  12. drachenpalme commented on Feb 28, 2025

    @drachenpalme
    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

Metadata

Metadata

Assignees

Labels

in: dataIssues in data modules (jdbc, orm, oxm, tx)status: backportedAn issue that has been backported to maintenance branchesstatus: feedback-providedFeedback has been providedtype: bugA general bug

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions