springrain opened SPR-15121 and commented

IsolationLevelDataSourceRouter.determineCurrentLookupKey returns the result was null, since AbstractPlatformTransactionManager.getTransaction (TransactionDefinition definition) before calling doBegin(Object transaction, TransactionDefinition definition), setCurrentTransactionIsolationLevel(Integer isolationLevel) is not set, but get a connection, it's bug.


Affects: 4.3.4

Referenced from: pull request https://github.com/spring-projects/spring-framework/pull/1291

Comment From: spring-projects-issues

Juergen Hoeller commented

IsolationLevelDataSourceRouter is primarily intended for JTA scenarios where Connection lookups only happen after transaction begin. With DataSourceTransactionManager, the isolation level is applied to the retrieved Connection directly, so it is not really designed to interact with such DataSource-specific isolation setup.

Are you trying to optimize the switching overhead there? Why are you using IsolationLevelDataSourceRouter to begin with?

Comment From: spring-projects-issues

springrain commented

I used to read and write the database separation,I don't have to use JTA

\ \ \ \ \ \ \ \

Comment From: andrei-ivanov

I've hit a similar issue, where I'm trying to use the readOnly status of the transaction to determine which dataSource to use, just to see that AbstractPlatformTransactionManager#prepareSynchronization is invoked just after doBegin, which obtains the connection (using a JpaTransactionManager) 😥

Comment From: andrei-ivanov

is there any chance that moving the prepareSynchronization call before doBegin might work?

Comment From: andrei-ivanov

@jhoeller ?

Comment From: lukago

I have similar problem to @andrei-ivanov, any updates on this? @jhoeller

Comment From: jhoeller

Returning to DataSource setup issues for a bit of a JDBC theme in 6.1.2, including new sharding support and several refinements to Spring's DataSource adapters : see https://github.com/spring-projects/spring-framework/issues?q=is%3Aopen+milestone%3A6.1.2+label%3A%22in%3A+data%22+

I am inclined to address the isolation level scenario here through an extension of SmartDataSource with a dedicated getConnection(int isolationLevel, boolean readOnly) method that DataSourceTransactionManager can call if the target DataSource implements it. IsolationLevelDataSourceRouter and a similar routing DataSource based on @Transactional(readOnly=...) can then take this into account directly when specified. This would allow us to preserve the semantics of our existing transaction synchronization arrangement which we cannot easily bend towards earlier exposure.

As an alternative, LazyConnectionDataSourceProxy could be used to approach JTA-like late retrieval semantics which should make the existing transaction synchronization arrangement work with IsolationLevelDataSourceRouter as the target behind a LazyConnectionDataSourceProxy, since the actual target Connection will be retrieved after transaction begin then.

Comment From: jhoeller

Experimenting with a few scenarios here, such an extension to SmartDataSource is rather involved in more complex setups, e.g. behind a JPA provider. In comparison to that, LazyConnectionDataSourceProxy is actually the simplest solution out: configuring both your transaction manager and your data access setup (JdbcTemplate or JPA setup) with a LazyConnectionDataSourceProxy conveniently allows for IsolationLevelDataSourceRouter to pick up a late-bound current isolation level within the transaction. And this comes with the extra benefit that connection contention is minimized and even avoided completely (with no connection ever fetched) for "empty" transactions which is quite common with JPA queries that can be answered from a cache.

As a consequence, I am going to turn this ticket into a documentation ticket for LazyConnectionDataSourceProxy.