DataSource Routing with Spring @Transactional

I was inspired by Carl Papa’s use of aspects with the Spring Framework to determine the DataSource to use (either read-write or read-only). So, I’m writing this post.

I must admit that I have long been familiar with Spring’s AbstractRoutingDataSource. But I did not have a good idea where it can be used. Thanks to Carl and team, and one of their projects. Now, I know a good use case.

@Transactional

With Spring, read-only transactions are typically marked with annotations.

public class SomeTransactionalComponent {
    @Transactional(readOnly=true)
    public void ...() {...}
 
    @Transactional // read-write
    public void ...() {...}
}

To take advantage of this, we use Spring’s TransactionSynchronizationManager to determine if the current transaction is read-only or not.

AbstractRoutingDataSource

Master and replica databases

Here, we use Spring’s AbstractRoutingDataSource to route to the read-only replica if the current transaction is read-only. Otherwise, it routes to the default which is the master.

public class ... extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        if (TransactionSynchronizationManager
                .isCurrentTransactionReadOnly() ...) {
            // return key to a replica
        }
        return null; // use default
    }
    ...
}

Upon using the above approach, we found out that the TransactionSynchronizationManager is one step behind because Spring will have already called DataSource.getConnection() before a synchronization is established. Thus, a LazyConnectionDataSourceProxy needs to be configured as well.

As we were discussing this, we figured if there was another way to determine if the current transaction is read-only or not (without resorting to LazyConnectionDataSourceProxy). So, we came up with an experimental approach where an aspect captures the TransactionDefinition (from the @Transactional annotation, if any) as a thread-local variable, and an AbstractRoutingDataSource that routes based on the captured information.

The relevant source code can be found on GitHub. Thanks again, Carl! BTW, Carl is also an award-winning movie director. Wow, talent definitely knows no boundaries.

Originally posted at: DataSource Routing with Spring @Transactional